Added LGPL standard comment, and copyright notices where necessary.
[wine/multimedia.git] / files / drive.c
blob302edcaac1247a0573ea5f3a68cf7dd535c8df0b
1 /*
2 * DOS drives handling functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
7 * Label & serial number read support.
8 * (c) 1999 Petr Tomasek <tomasek@etf.cuni.cz>
9 * (c) 2000 Andreas Mohr (changes)
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "config.h"
27 #include "wine/port.h"
29 #include <assert.h>
30 #include <ctype.h>
31 #include <string.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <errno.h>
38 #include <unistd.h>
40 #ifdef HAVE_SYS_PARAM_H
41 # include <sys/param.h>
42 #endif
43 #ifdef STATFS_DEFINED_BY_SYS_VFS
44 # include <sys/vfs.h>
45 #else
46 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
47 # include <sys/mount.h>
48 # else
49 # ifdef STATFS_DEFINED_BY_SYS_STATFS
50 # include <sys/statfs.h>
51 # endif
52 # endif
53 #endif
55 #include "winbase.h"
56 #include "ntddk.h"
57 #include "wine/winbase16.h" /* for GetCurrentTask */
58 #include "winerror.h"
59 #include "drive.h"
60 #include "file.h"
61 #include "heap.h"
62 #include "msdos.h"
63 #include "options.h"
64 #include "task.h"
65 #include "wine/debug.h"
66 #include "wine/server.h"
67 #include "winioctl.h"
68 #include "ntddstor.h"
69 #include "ntddcdrm.h"
71 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
72 WINE_DECLARE_DEBUG_CHANNEL(file);
74 typedef struct
76 char *root; /* root dir in Unix format without trailing / */
77 char *dos_cwd; /* cwd in DOS format without leading or trailing \ */
78 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
79 char *device; /* raw device path */
80 char label_conf[12]; /* drive label as cfg'd in wine config */
81 char label_read[12]; /* drive label as read from device */
82 DWORD serial_conf; /* drive serial number as cfg'd in wine config */
83 UINT type; /* drive type */
84 UINT flags; /* drive flags */
85 dev_t dev; /* unix device number */
86 ino_t ino; /* unix inode number */
87 } DOSDRIVE;
90 static const char * const DRIVE_Types[] =
92 "", /* DRIVE_UNKNOWN */
93 "", /* DRIVE_NO_ROOT_DIR */
94 "floppy", /* DRIVE_REMOVABLE */
95 "hd", /* DRIVE_FIXED */
96 "network", /* DRIVE_REMOTE */
97 "cdrom", /* DRIVE_CDROM */
98 "ramdisk" /* DRIVE_RAMDISK */
102 /* Known filesystem types */
104 typedef struct
106 const char *name;
107 UINT flags;
108 } FS_DESCR;
110 static const FS_DESCR DRIVE_Filesystems[] =
112 { "unix", DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
113 { "msdos", DRIVE_SHORT_NAMES },
114 { "dos", DRIVE_SHORT_NAMES },
115 { "fat", DRIVE_SHORT_NAMES },
116 { "vfat", DRIVE_CASE_PRESERVING },
117 { "win95", DRIVE_CASE_PRESERVING },
118 { NULL, 0 }
122 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
123 static int DRIVE_CurDrive = -1;
125 static HTASK16 DRIVE_LastTask = 0;
127 /* strdup on the process heap */
128 inline static char *heap_strdup( const char *str )
130 INT len = strlen(str) + 1;
131 LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
132 if (p) memcpy( p, str, len );
133 return p;
136 /***********************************************************************
137 * DRIVE_GetDriveType
139 static UINT DRIVE_GetDriveType( const char *name )
141 char buffer[20];
142 int i;
144 PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
145 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
147 if (!strcasecmp( buffer, DRIVE_Types[i] )) return i;
149 MESSAGE("%s: unknown drive type '%s', defaulting to 'hd'.\n",
150 name, buffer );
151 return DRIVE_FIXED;
155 /***********************************************************************
156 * DRIVE_GetFSFlags
158 static UINT DRIVE_GetFSFlags( const char *name, const char *value )
160 const FS_DESCR *descr;
162 for (descr = DRIVE_Filesystems; descr->name; descr++)
163 if (!strcasecmp( value, descr->name )) return descr->flags;
164 MESSAGE("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
165 name, value );
166 return DRIVE_CASE_PRESERVING;
170 /***********************************************************************
171 * DRIVE_Init
173 int DRIVE_Init(void)
175 int i, len, count = 0;
176 char name[] = "Drive A";
177 char drive_env[] = "=A:";
178 char path[MAX_PATHNAME_LEN];
179 char buffer[80];
180 struct stat drive_stat_buffer;
181 char *p;
182 DOSDRIVE *drive;
184 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
186 PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
187 if (path[0])
189 p = path + strlen(path) - 1;
190 while ((p > path) && (*p == '/')) *p-- = '\0';
192 if (path[0] == '/')
194 drive->root = heap_strdup( path );
196 else
198 /* relative paths are relative to config dir */
199 const char *config = get_config_dir();
200 drive->root = HeapAlloc( GetProcessHeap(), 0, strlen(config) + strlen(path) + 2 );
201 sprintf( drive->root, "%s/%s", config, path );
204 if (stat( drive->root, &drive_stat_buffer ))
206 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
207 drive->root, strerror(errno), 'A' + i);
208 HeapFree( GetProcessHeap(), 0, drive->root );
209 drive->root = NULL;
210 continue;
212 if (!S_ISDIR(drive_stat_buffer.st_mode))
214 MESSAGE("%s is not a directory, ignoring drive %c:\n",
215 drive->root, 'A' + i );
216 HeapFree( GetProcessHeap(), 0, drive->root );
217 drive->root = NULL;
218 continue;
221 drive->dos_cwd = heap_strdup( "" );
222 drive->unix_cwd = heap_strdup( "" );
223 drive->type = DRIVE_GetDriveType( name );
224 drive->device = NULL;
225 drive->flags = 0;
226 drive->dev = drive_stat_buffer.st_dev;
227 drive->ino = drive_stat_buffer.st_ino;
229 /* Get the drive label */
230 PROFILE_GetWineIniString( name, "Label", "", drive->label_conf, 12 );
231 if ((len = strlen(drive->label_conf)) < 11)
233 /* Pad label with spaces */
234 memset( drive->label_conf + len, ' ', 11 - len );
235 drive->label_conf[11] = '\0';
238 /* Get the serial number */
239 PROFILE_GetWineIniString( name, "Serial", "12345678",
240 buffer, sizeof(buffer) );
241 drive->serial_conf = strtoul( buffer, NULL, 16 );
243 /* Get the filesystem type */
244 PROFILE_GetWineIniString( name, "Filesystem", "win95",
245 buffer, sizeof(buffer) );
246 drive->flags = DRIVE_GetFSFlags( name, buffer );
248 /* Get the device */
249 PROFILE_GetWineIniString( name, "Device", "",
250 buffer, sizeof(buffer) );
251 if (buffer[0])
253 drive->device = heap_strdup( buffer );
254 if (PROFILE_GetWineIniBool( name, "ReadVolInfo", 1))
255 drive->flags |= DRIVE_READ_VOL_INFO;
258 /* Get the FailReadOnly flag */
259 if (PROFILE_GetWineIniBool( name, "FailReadOnly", 0 ))
260 drive->flags |= DRIVE_FAIL_READ_ONLY;
262 /* Make the first hard disk the current drive */
263 if ((DRIVE_CurDrive == -1) && (drive->type == DRIVE_FIXED))
264 DRIVE_CurDrive = i;
266 count++;
267 TRACE("%s: path=%s type=%s label='%s' serial=%08lx "
268 "flags=%08x dev=%x ino=%x\n",
269 name, drive->root, DRIVE_Types[drive->type],
270 drive->label_conf, drive->serial_conf, drive->flags,
271 (int)drive->dev, (int)drive->ino );
273 else WARN("%s: not defined\n", name );
276 if (!count)
278 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
279 /* Create a C drive pointing to Unix root dir */
280 DOSDrives[2].root = heap_strdup( "/" );
281 DOSDrives[2].dos_cwd = heap_strdup( "" );
282 DOSDrives[2].unix_cwd = heap_strdup( "" );
283 strcpy( DOSDrives[2].label_conf, "Drive C " );
284 DOSDrives[2].serial_conf = 12345678;
285 DOSDrives[2].type = DRIVE_FIXED;
286 DOSDrives[2].device = NULL;
287 DOSDrives[2].flags = 0;
288 DRIVE_CurDrive = 2;
291 /* Make sure the current drive is valid */
292 if (DRIVE_CurDrive == -1)
294 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
296 if (drive->root && !(drive->flags & DRIVE_DISABLED))
298 DRIVE_CurDrive = i;
299 break;
304 /* get current working directory info for all drives */
305 for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
307 if (!GetEnvironmentVariableA(drive_env, path, sizeof(path))) continue;
308 /* sanity check */
309 if (toupper(path[0]) != drive_env[1] || path[1] != ':') continue;
310 DRIVE_Chdir( i, path + 2 );
312 return 1;
316 /***********************************************************************
317 * DRIVE_IsValid
319 int DRIVE_IsValid( int drive )
321 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
322 return (DOSDrives[drive].root &&
323 !(DOSDrives[drive].flags & DRIVE_DISABLED));
327 /***********************************************************************
328 * DRIVE_GetCurrentDrive
330 int DRIVE_GetCurrentDrive(void)
332 TDB *pTask = TASK_GetCurrent();
333 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
334 return DRIVE_CurDrive;
338 /***********************************************************************
339 * DRIVE_SetCurrentDrive
341 int DRIVE_SetCurrentDrive( int drive )
343 TDB *pTask = TASK_GetCurrent();
344 if (!DRIVE_IsValid( drive ))
346 SetLastError( ERROR_INVALID_DRIVE );
347 return 0;
349 TRACE("%c:\n", 'A' + drive );
350 DRIVE_CurDrive = drive;
351 if (pTask) pTask->curdrive = drive | 0x80;
352 chdir(DRIVE_GetUnixCwd(drive));
353 return 1;
357 /***********************************************************************
358 * DRIVE_FindDriveRoot
360 * Find a drive for which the root matches the beginning of the given path.
361 * This can be used to translate a Unix path into a drive + DOS path.
362 * Return value is the drive, or -1 on error. On success, path is modified
363 * to point to the beginning of the DOS path.
365 int DRIVE_FindDriveRoot( const char **path )
367 /* idea: check at all '/' positions.
368 * If the device and inode of that path is identical with the
369 * device and inode of the current drive then we found a solution.
370 * If there is another drive pointing to a deeper position in
371 * the file tree, we want to find that one, not the earlier solution.
373 int drive, rootdrive = -1;
374 char buffer[MAX_PATHNAME_LEN];
375 char *next = buffer;
376 const char *p = *path;
377 struct stat st;
379 strcpy( buffer, "/" );
380 for (;;)
382 if (stat( buffer, &st ) || !S_ISDIR( st.st_mode )) break;
384 /* Find the drive */
386 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
388 if (!DOSDrives[drive].root ||
389 (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
391 if ((DOSDrives[drive].dev == st.st_dev) &&
392 (DOSDrives[drive].ino == st.st_ino))
394 rootdrive = drive;
395 *path = p;
396 break;
400 /* Get the next path component */
402 *next++ = '/';
403 while ((*p == '/') || (*p == '\\')) p++;
404 if (!*p) break;
405 while (!IS_END_OF_NAME(*p)) *next++ = *p++;
406 *next = 0;
408 *next = 0;
410 if (rootdrive != -1)
411 TRACE("%s -> drive %c:, root='%s', name='%s'\n",
412 buffer, 'A' + rootdrive, DOSDrives[rootdrive].root, *path );
413 return rootdrive;
417 /***********************************************************************
418 * DRIVE_GetRoot
420 const char * DRIVE_GetRoot( int drive )
422 if (!DRIVE_IsValid( drive )) return NULL;
423 return DOSDrives[drive].root;
427 /***********************************************************************
428 * DRIVE_GetDosCwd
430 const char * DRIVE_GetDosCwd( int drive )
432 TDB *pTask = TASK_GetCurrent();
433 if (!DRIVE_IsValid( drive )) return NULL;
435 /* Check if we need to change the directory to the new task. */
436 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
437 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
438 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
440 /* Perform the task-switch */
441 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
442 DRIVE_LastTask = GetCurrentTask();
444 return DOSDrives[drive].dos_cwd;
448 /***********************************************************************
449 * DRIVE_GetUnixCwd
451 const char * DRIVE_GetUnixCwd( int drive )
453 TDB *pTask = TASK_GetCurrent();
454 if (!DRIVE_IsValid( drive )) return NULL;
456 /* Check if we need to change the directory to the new task. */
457 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
458 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
459 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
461 /* Perform the task-switch */
462 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
463 DRIVE_LastTask = GetCurrentTask();
465 return DOSDrives[drive].unix_cwd;
469 /***********************************************************************
470 * DRIVE_GetDevice
472 const char * DRIVE_GetDevice( int drive )
474 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
477 /******************************************************************
478 * static WORD CDROM_Data_FindBestVoldesc
482 static WORD CDROM_Data_FindBestVoldesc(int fd)
484 BYTE cur_vd_type, max_vd_type = 0;
485 unsigned int offs, best_offs = 0, extra_offs = 0;
486 char sig[3];
488 for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
490 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
491 * the volume label is displaced forward by 8
493 lseek(fd, offs + 11, SEEK_SET); /* check for non-ISO9660 signature */
494 read(fd, &sig, 3);
495 if ((sig[0] == 'R') && (sig[1] == 'O') && (sig[2]=='M'))
497 extra_offs = 8;
499 lseek(fd, offs + extra_offs, SEEK_SET);
500 read(fd, &cur_vd_type, 1);
501 if (cur_vd_type == 0xff) /* voldesc set terminator */
502 break;
503 if (cur_vd_type > max_vd_type)
505 max_vd_type = cur_vd_type;
506 best_offs = offs + extra_offs;
509 return best_offs;
512 /***********************************************************************
513 * DRIVE_ReadSuperblock
515 * NOTE
516 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
517 * to check, that they are writing on a FAT filesystem !
519 int DRIVE_ReadSuperblock (int drive, char * buff)
521 #define DRIVE_SUPER 96
522 int fd;
523 off_t offs;
525 if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
526 if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
528 struct stat st;
529 if (!DOSDrives[drive].device)
530 ERR("No device configured for drive %c: !\n", 'A'+drive);
531 else
532 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
533 (stat(DOSDrives[drive].device, &st)) ?
534 "not available or symlink not valid ?" : "no permission");
535 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
536 PROFILE_UsageWineIni();
537 return -1;
540 switch(DOSDrives[drive].type)
542 case DRIVE_REMOVABLE:
543 case DRIVE_FIXED:
544 offs = 0;
545 break;
546 case DRIVE_CDROM:
547 offs = CDROM_Data_FindBestVoldesc(fd);
548 break;
549 default:
550 offs = 0;
551 break;
554 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs)) return -4;
555 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER) return -2;
557 switch(DOSDrives[drive].type)
559 case DRIVE_REMOVABLE:
560 case DRIVE_FIXED:
561 if ((buff[0x26]!=0x29) || /* Check for FAT present */
562 /* FIXME: do really all FAT have their name beginning with
563 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
564 memcmp( buff+0x36,"FAT",3))
566 ERR("The filesystem is not FAT !! (device=%s)\n",
567 DOSDrives[drive].device);
568 return -3;
570 break;
571 case DRIVE_CDROM:
572 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
573 return -3;
574 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
575 break;
576 default:
577 return -3;
578 break;
581 return close(fd);
585 /***********************************************************************
586 * DRIVE_WriteSuperblockEntry
588 * NOTE
589 * We are writing as little as possible (ie. not the whole SuperBlock)
590 * not to interfere with kernel. The drive can be mounted !
592 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
594 int fd;
596 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
598 ERR("Cannot open the device %s (for writing)\n",
599 DOSDrives[drive].device);
600 return -1;
602 if (lseek(fd,ofs,SEEK_SET)!=ofs)
604 ERR("lseek failed on device %s !\n",
605 DOSDrives[drive].device);
606 close(fd);
607 return -2;
609 if (write(fd,buff,len)!=len)
611 close(fd);
612 ERR("Cannot write on %s !\n",
613 DOSDrives[drive].device);
614 return -3;
616 return close (fd);
619 /******************************************************************
620 * static HANDLE CDROM_Open
624 static HANDLE CDROM_Open(int drive)
626 char root[6];
628 strcpy(root, "\\\\.\\A:");
629 root[4] += drive;
631 return CreateFileA(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
634 /**************************************************************************
635 * CDROM_Data_GetLabel [internal]
637 DWORD CDROM_Data_GetLabel(int drive, char *label)
639 #define LABEL_LEN 32+1
640 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
641 WORD offs = CDROM_Data_FindBestVoldesc(dev);
642 WCHAR label_read[LABEL_LEN]; /* Unicode possible, too */
643 DWORD unicode_id = 0;
645 if (offs)
647 if ((lseek(dev, offs+0x58, SEEK_SET) == offs+0x58)
648 && (read(dev, &unicode_id, 3) == 3))
650 int ver = (unicode_id & 0xff0000) >> 16;
652 if ((lseek(dev, offs+0x28, SEEK_SET) != offs+0x28)
653 || (read(dev, &label_read, LABEL_LEN) != LABEL_LEN))
654 goto failure;
656 close(dev);
657 if ((LOWORD(unicode_id) == 0x2f25) /* Unicode ID */
658 && ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
659 { /* yippee, unicode */
660 int i;
661 WORD ch;
662 for (i=0; i<LABEL_LEN;i++)
663 { /* Motorola -> Intel Unicode conversion :-\ */
664 ch = label_read[i];
665 label_read[i] = (ch << 8) | (ch >> 8);
667 WideCharToMultiByte( CP_ACP, 0, label_read, -1, label, 12, NULL, NULL );
668 label[11] = 0;
670 else
672 strncpy(label, (LPSTR)label_read, 11);
673 label[11] = '\0';
675 return 1;
678 failure:
679 close(dev);
680 ERR("error reading label !\n");
681 return 0;
684 /**************************************************************************
685 * CDROM_GetLabel [internal]
687 static DWORD CDROM_GetLabel(int drive, char *label)
689 HANDLE h = CDROM_Open(drive);
690 CDROM_DISK_DATA cdd;
691 DWORD br;
692 DWORD ret = 1;
694 if (!h || !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
695 return 0;
697 switch (cdd.DiskData & 0x03)
699 case CDROM_DISK_DATA_TRACK:
700 if (!CDROM_Data_GetLabel(drive, label))
701 ret = 0;
702 break;
703 case CDROM_DISK_AUDIO_TRACK:
704 strcpy(label, "Audio CD ");
705 break;
706 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
707 FIXME("Need to get the label of a mixed mode CD: not implemented yet !\n");
708 /* fall through */
709 case 0:
710 ret = 0;
711 break;
713 TRACE("CD: label is '%s'.\n", label);
715 return ret;
717 /***********************************************************************
718 * DRIVE_GetLabel
720 const char * DRIVE_GetLabel( int drive )
722 int read = 0;
723 char buff[DRIVE_SUPER];
724 int offs = -1;
726 if (!DRIVE_IsValid( drive )) return NULL;
727 if (DOSDrives[drive].type == DRIVE_CDROM)
729 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
731 else
732 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
734 if (DRIVE_ReadSuperblock(drive,(char *) buff))
735 ERR("Invalid or unreadable superblock on %s (%c:).\n",
736 DOSDrives[drive].device, (char)(drive+'A'));
737 else {
738 if (DOSDrives[drive].type == DRIVE_REMOVABLE ||
739 DOSDrives[drive].type == DRIVE_FIXED)
740 offs = 0x2b;
742 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
743 if (offs != -1) memcpy(DOSDrives[drive].label_read,buff+offs,11);
744 DOSDrives[drive].label_read[11]='\0';
745 read = 1;
749 return (read) ?
750 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
753 #define CDFRAMES_PERSEC 75
754 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
755 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
756 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
758 /**************************************************************************
759 * CDROM_Audio_GetSerial [internal]
761 static DWORD CDROM_Audio_GetSerial(HANDLE h)
763 unsigned long serial = 0;
764 int i;
765 WORD wMagic;
766 DWORD dwStart, dwEnd, br;
767 CDROM_TOC toc;
769 if (!DeviceIoControl(h, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0))
770 return 0;
773 * wMagic collects the wFrames from track 1
774 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
775 * frames.
776 * There it is collected for correcting the serial when there are less than
777 * 3 tracks.
779 wMagic = toc.TrackData[0].Address[2];
780 dwStart = FRAME_OF_TOC(toc, toc.FirstTrack);
782 for (i = 0; i <= toc.LastTrack - toc.FirstTrack; i++) {
783 serial += (toc.TrackData[i].Address[0] << 16) |
784 (toc.TrackData[i].Address[1] << 8) | toc.TrackData[i].Address[2];
786 dwEnd = FRAME_OF_TOC(toc, toc.LastTrack + 1);
788 if (toc.LastTrack - toc.FirstTrack + 1 < 3)
789 serial += wMagic + (dwEnd - dwStart);
791 return serial;
794 /**************************************************************************
795 * CDROM_Data_GetSerial [internal]
797 static DWORD CDROM_Data_GetSerial(int drive)
799 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
800 WORD offs;
801 union {
802 unsigned long val;
803 unsigned char p[4];
804 } serial;
805 BYTE b0 = 0, b1 = 1, b2 = 2, b3 = 3;
808 if (dev == -1) return 0;
809 offs = CDROM_Data_FindBestVoldesc(dev);
811 serial.val = 0;
812 if (offs)
814 BYTE buf[2048];
815 OSVERSIONINFOA ovi;
816 int i;
818 lseek(dev, offs, SEEK_SET);
819 read(dev, buf, 2048);
821 * OK, another braindead one... argh. Just believe it.
822 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
823 * It's true and nobody will ever be able to change it.
825 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
826 GetVersionExA(&ovi);
827 if ((ovi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (ovi.dwMajorVersion >= 4))
829 b0 = 3; b1 = 2; b2 = 1; b3 = 0;
831 for (i = 0; i < 2048; i += 4)
833 /* DON'T optimize this into DWORD !! (breaks overflow) */
834 serial.p[b0] += buf[i+b0];
835 serial.p[b1] += buf[i+b1];
836 serial.p[b2] += buf[i+b2];
837 serial.p[b3] += buf[i+b3];
840 close(dev);
841 return serial.val;
844 /**************************************************************************
845 * CDROM_GetSerial [internal]
847 static DWORD CDROM_GetSerial(int drive)
849 DWORD serial = 0;
850 HANDLE h = CDROM_Open(drive);
851 CDROM_DISK_DATA cdd;
852 DWORD br;
854 if (!h || ! !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
855 return 0;
857 switch (cdd.DiskData & 0x03)
859 case CDROM_DISK_DATA_TRACK:
860 /* hopefully a data CD */
861 serial = CDROM_Data_GetSerial(drive);
862 break;
863 case CDROM_DISK_AUDIO_TRACK:
864 /* fall thru */
865 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
866 serial = CDROM_Audio_GetSerial(h);
867 break;
868 case 0:
869 break;
872 if (serial)
873 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial), LOWORD(serial));
875 CloseHandle(h);
877 return serial;
880 /***********************************************************************
881 * DRIVE_GetSerialNumber
883 DWORD DRIVE_GetSerialNumber( int drive )
885 DWORD serial = 0;
886 char buff[DRIVE_SUPER];
888 if (!DRIVE_IsValid( drive )) return 0;
890 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
892 switch(DOSDrives[drive].type)
894 case DRIVE_REMOVABLE:
895 case DRIVE_FIXED:
896 if (DRIVE_ReadSuperblock(drive,(char *) buff))
897 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
898 " Maybe not FAT?\n" ,
899 DOSDrives[drive].device, 'A'+drive);
900 else
901 serial = *((DWORD*)(buff+0x27));
902 break;
903 case DRIVE_CDROM:
904 serial = CDROM_GetSerial(drive);
905 break;
906 default:
907 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
911 return (serial) ? serial : DOSDrives[drive].serial_conf;
915 /***********************************************************************
916 * DRIVE_SetSerialNumber
918 int DRIVE_SetSerialNumber( int drive, DWORD serial )
920 char buff[DRIVE_SUPER];
922 if (!DRIVE_IsValid( drive )) return 0;
924 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
926 if ((DOSDrives[drive].type != DRIVE_REMOVABLE) &&
927 (DOSDrives[drive].type != DRIVE_FIXED)) return 0;
928 /* check, if the drive has a FAT filesystem */
929 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
930 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
931 return 1;
934 if (DOSDrives[drive].type == DRIVE_CDROM) return 0;
935 DOSDrives[drive].serial_conf = serial;
936 return 1;
940 /***********************************************************************
941 * DRIVE_GetType
943 static UINT DRIVE_GetType( int drive )
945 if (!DRIVE_IsValid( drive )) return DRIVE_UNKNOWN;
946 return DOSDrives[drive].type;
950 /***********************************************************************
951 * DRIVE_GetFlags
953 UINT DRIVE_GetFlags( int drive )
955 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
956 return DOSDrives[drive].flags;
960 /***********************************************************************
961 * DRIVE_Chdir
963 int DRIVE_Chdir( int drive, const char *path )
965 DOS_FULL_NAME full_name;
966 char buffer[MAX_PATHNAME_LEN];
967 LPSTR unix_cwd;
968 BY_HANDLE_FILE_INFORMATION info;
969 TDB *pTask = TASK_GetCurrent();
971 strcpy( buffer, "A:" );
972 buffer[0] += drive;
973 TRACE("(%s,%s)\n", buffer, path );
974 lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
976 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
977 if (!FILE_Stat( full_name.long_name, &info )) return 0;
978 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
980 SetLastError( ERROR_FILE_NOT_FOUND );
981 return 0;
983 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
984 while (*unix_cwd == '/') unix_cwd++;
986 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
987 'A' + drive, unix_cwd, full_name.short_name + 3 );
989 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
990 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
991 DOSDrives[drive].dos_cwd = heap_strdup( full_name.short_name + 3 );
992 DOSDrives[drive].unix_cwd = heap_strdup( unix_cwd );
994 if (pTask && (pTask->curdrive & 0x80) &&
995 ((pTask->curdrive & ~0x80) == drive))
997 lstrcpynA( pTask->curdir, full_name.short_name + 2,
998 sizeof(pTask->curdir) );
999 DRIVE_LastTask = GetCurrentTask();
1000 chdir(unix_cwd); /* Only change if on current drive */
1002 return 1;
1006 /***********************************************************************
1007 * DRIVE_Disable
1009 int DRIVE_Disable( int drive )
1011 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1013 SetLastError( ERROR_INVALID_DRIVE );
1014 return 0;
1016 DOSDrives[drive].flags |= DRIVE_DISABLED;
1017 return 1;
1021 /***********************************************************************
1022 * DRIVE_Enable
1024 int DRIVE_Enable( int drive )
1026 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1028 SetLastError( ERROR_INVALID_DRIVE );
1029 return 0;
1031 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
1032 return 1;
1036 /***********************************************************************
1037 * DRIVE_SetLogicalMapping
1039 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
1041 /* If new_drive is already valid, do nothing and return 0
1042 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1044 DOSDRIVE *old, *new;
1046 old = DOSDrives + existing_drive;
1047 new = DOSDrives + new_drive;
1049 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
1050 !old->root ||
1051 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
1053 SetLastError( ERROR_INVALID_DRIVE );
1054 return 0;
1057 if ( new->root )
1059 TRACE("Can't map drive %c: to already existing drive %c:\n",
1060 'A' + existing_drive, 'A' + new_drive );
1061 /* it is already mapped there, so return success */
1062 if (!strcmp(old->root,new->root))
1063 return 1;
1064 return 0;
1067 new->root = heap_strdup( old->root );
1068 new->dos_cwd = heap_strdup( old->dos_cwd );
1069 new->unix_cwd = heap_strdup( old->unix_cwd );
1070 new->device = heap_strdup( old->device );
1071 memcpy ( new->label_conf, old->label_conf, 12 );
1072 memcpy ( new->label_read, old->label_read, 12 );
1073 new->serial_conf = old->serial_conf;
1074 new->type = old->type;
1075 new->flags = old->flags;
1076 new->dev = old->dev;
1077 new->ino = old->ino;
1079 TRACE("Drive %c: is now equal to drive %c:\n",
1080 'A' + new_drive, 'A' + existing_drive );
1082 return 1;
1086 /***********************************************************************
1087 * DRIVE_OpenDevice
1089 * Open the drive raw device and return a Unix fd (or -1 on error).
1091 int DRIVE_OpenDevice( int drive, int flags )
1093 if (!DRIVE_IsValid( drive )) return -1;
1094 return open( DOSDrives[drive].device, flags );
1098 /***********************************************************************
1099 * DRIVE_RawRead
1101 * Read raw sectors from a device
1103 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1105 int fd;
1107 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1109 lseek( fd, begin * 512, SEEK_SET );
1110 /* FIXME: check errors */
1111 read( fd, dataptr, nr_sect * 512 );
1112 close( fd );
1114 else
1116 memset(dataptr, 0, nr_sect * 512);
1117 if (fake_success)
1119 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
1120 if (begin == 1) *dataptr = 0xf8;
1122 else
1123 return 0;
1125 return 1;
1129 /***********************************************************************
1130 * DRIVE_RawWrite
1132 * Write raw sectors to a device
1134 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1136 int fd;
1138 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1140 lseek( fd, begin * 512, SEEK_SET );
1141 /* FIXME: check errors */
1142 write( fd, dataptr, nr_sect * 512 );
1143 close( fd );
1145 else
1146 if (!(fake_success))
1147 return 0;
1149 return 1;
1153 /***********************************************************************
1154 * DRIVE_GetFreeSpace
1156 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
1157 PULARGE_INTEGER available )
1159 struct statfs info;
1161 if (!DRIVE_IsValid(drive))
1163 SetLastError( ERROR_INVALID_DRIVE );
1164 return 0;
1167 /* FIXME: add autoconf check for this */
1168 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1169 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
1170 #else
1171 if (statfs( DOSDrives[drive].root, &info) < 0)
1172 #endif
1174 FILE_SetDosError();
1175 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
1176 return 0;
1179 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bsize, info.f_blocks );
1180 #ifdef STATFS_HAS_BAVAIL
1181 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bavail, info.f_bsize );
1182 #else
1183 # ifdef STATFS_HAS_BFREE
1184 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bfree, info.f_bsize );
1185 # else
1186 # error "statfs has no bfree/bavail member!"
1187 # endif
1188 #endif
1189 if (DOSDrives[drive].type == DRIVE_CDROM)
1190 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1191 available->QuadPart = 0;
1193 return 1;
1196 /***********************************************************************
1197 * DRIVE_GetCurrentDirectory
1198 * Returns "X:\\path\\etc\\".
1200 * Despite the API description, return required length including the
1201 * terminating null when buffer too small. This is the real behaviour.
1204 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPSTR buf )
1206 UINT ret;
1207 const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1209 assert(s);
1210 ret = strlen(s) + 3; /* length of WHOLE current directory */
1211 if (ret >= buflen) return ret + 1;
1212 lstrcpynA( buf, "A:\\", min( 4u, buflen ) );
1213 if (buflen) buf[0] += DRIVE_GetCurrentDrive();
1214 if (buflen > 3) lstrcpynA( buf + 3, s, buflen - 3 );
1215 return ret;
1219 /***********************************************************************
1220 * DRIVE_BuildEnv
1222 * Build the environment array containing the drives' current directories.
1223 * Resulting pointer must be freed with HeapFree.
1225 char *DRIVE_BuildEnv(void)
1227 int i, length = 0;
1228 const char *cwd[MAX_DOS_DRIVES];
1229 char *env, *p;
1231 for (i = 0; i < MAX_DOS_DRIVES; i++)
1233 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0]) length += strlen(cwd[i]) + 8;
1235 if (!(env = HeapAlloc( GetProcessHeap(), 0, length+1 ))) return NULL;
1236 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
1238 if (cwd[i] && cwd[i][0])
1239 p += sprintf( p, "=%c:=%c:\\%s", 'A'+i, 'A'+i, cwd[i] ) + 1;
1241 *p = 0;
1242 return env;
1246 /***********************************************************************
1247 * GetDiskFreeSpace (KERNEL.422)
1249 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
1250 LPDWORD sector_bytes, LPDWORD free_clusters,
1251 LPDWORD total_clusters )
1253 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
1254 free_clusters, total_clusters );
1258 /***********************************************************************
1259 * GetDiskFreeSpaceA (KERNEL32.@)
1261 * Fails if expression resulting from current drive's dir and "root"
1262 * is not a root dir of the target drive.
1264 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1265 * if the corresponding info is unneeded.
1267 * FIXME: needs to support UNC names from Win95 OSR2 on.
1269 * Behaviour under Win95a:
1270 * CurrDir root result
1271 * "E:\\TEST" "E:" FALSE
1272 * "E:\\" "E:" TRUE
1273 * "E:\\" "E" FALSE
1274 * "E:\\" "\\" TRUE
1275 * "E:\\TEST" "\\" TRUE
1276 * "E:\\TEST" ":\\" FALSE
1277 * "E:\\TEST" "E:\\" TRUE
1278 * "E:\\TEST" "" FALSE
1279 * "E:\\" "" FALSE (!)
1280 * "E:\\" 0x0 TRUE
1281 * "E:\\TEST" 0x0 TRUE (!)
1282 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1283 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1285 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1286 LPDWORD sector_bytes, LPDWORD free_clusters,
1287 LPDWORD total_clusters )
1289 int drive, sec_size;
1290 ULARGE_INTEGER size,available;
1291 LPCSTR path;
1292 DWORD cluster_sec;
1294 if ((!root) || (strcmp(root,"\\") == 0))
1295 drive = DRIVE_GetCurrentDrive();
1296 else
1297 if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
1299 drive = toupper(root[0]) - 'A';
1300 path = &root[2];
1301 if (path[0] == '\0')
1302 path = DRIVE_GetDosCwd(drive);
1303 else
1304 if (path[0] == '\\')
1305 path++;
1306 if (path[0]) /* oops, we are in a subdir */
1308 SetLastError(ERROR_INVALID_NAME);
1309 return FALSE;
1312 else
1314 SetLastError(ERROR_INVALID_NAME);
1315 return FALSE;
1318 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1320 /* Cap the size and available at 2GB as per specs. */
1321 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1323 size.s.HighPart = 0;
1324 size.s.LowPart = 0x7fffffff;
1326 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1328 available.s.HighPart =0;
1329 available.s.LowPart = 0x7fffffff;
1331 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
1332 size.s.LowPart /= sec_size;
1333 available.s.LowPart /= sec_size;
1334 /* FIXME: probably have to adjust those variables too for CDFS */
1335 cluster_sec = 1;
1336 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1338 if (cluster_sectors)
1339 *cluster_sectors = cluster_sec;
1340 if (sector_bytes)
1341 *sector_bytes = sec_size;
1342 if (free_clusters)
1343 *free_clusters = available.s.LowPart / cluster_sec;
1344 if (total_clusters)
1345 *total_clusters = size.s.LowPart / cluster_sec;
1346 return TRUE;
1350 /***********************************************************************
1351 * GetDiskFreeSpaceW (KERNEL32.@)
1353 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1354 LPDWORD sector_bytes, LPDWORD free_clusters,
1355 LPDWORD total_clusters )
1357 LPSTR xroot;
1358 BOOL ret;
1360 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1361 ret = GetDiskFreeSpaceA( xroot,cluster_sectors, sector_bytes,
1362 free_clusters, total_clusters );
1363 HeapFree( GetProcessHeap(), 0, xroot );
1364 return ret;
1368 /***********************************************************************
1369 * GetDiskFreeSpaceExA (KERNEL32.@)
1371 * This function is used to acquire the size of the available and
1372 * total space on a logical volume.
1374 * RETURNS
1376 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1377 * detailed error information.
1380 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
1381 PULARGE_INTEGER avail,
1382 PULARGE_INTEGER total,
1383 PULARGE_INTEGER totalfree)
1385 int drive;
1386 ULARGE_INTEGER size,available;
1388 if (!root) drive = DRIVE_GetCurrentDrive();
1389 else
1390 { /* C: always works for GetDiskFreeSpaceEx */
1391 if ((root[1]) && ((root[1] != ':') || (root[2] && root[2] != '\\')))
1393 FIXME("there are valid root names which are not supported yet\n");
1394 /* ..like UNC names, for instance. */
1396 WARN("invalid root '%s'\n", root );
1397 return FALSE;
1399 drive = toupper(root[0]) - 'A';
1402 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1404 if (total)
1406 total->s.HighPart = size.s.HighPart;
1407 total->s.LowPart = size.s.LowPart;
1410 if (totalfree)
1412 totalfree->s.HighPart = available.s.HighPart;
1413 totalfree->s.LowPart = available.s.LowPart;
1416 if (avail)
1418 if (FIXME_ON(dosfs))
1420 /* On Windows2000, we need to check the disk quota
1421 allocated for the user owning the calling process. We
1422 don't want to be more obtrusive than necessary with the
1423 FIXME messages, so don't print the FIXME unless Wine is
1424 actually masquerading as Windows2000. */
1426 OSVERSIONINFOA ovi;
1427 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1428 if (GetVersionExA(&ovi))
1430 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1431 FIXME("no per-user quota support yet\n");
1435 /* Quick hack, should eventually be fixed to work 100% with
1436 Windows2000 (see comment above). */
1437 avail->s.HighPart = available.s.HighPart;
1438 avail->s.LowPart = available.s.LowPart;
1441 return TRUE;
1444 /***********************************************************************
1445 * GetDiskFreeSpaceExW (KERNEL32.@)
1447 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1448 PULARGE_INTEGER total,
1449 PULARGE_INTEGER totalfree)
1451 LPSTR xroot;
1452 BOOL ret;
1454 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1455 ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
1456 HeapFree( GetProcessHeap(), 0, xroot );
1457 return ret;
1460 /***********************************************************************
1461 * GetDriveType (KERNEL.136)
1462 * This function returns the type of a drive in Win16.
1463 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1464 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1465 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1466 * do any pseudo-clever changes.
1468 * RETURNS
1469 * drivetype DRIVE_xxx
1471 UINT16 WINAPI GetDriveType16( UINT16 drive ) /* [in] number (NOT letter) of drive */
1473 UINT type = DRIVE_GetType(drive);
1474 TRACE("(%c:)\n", 'A' + drive );
1475 if (type == DRIVE_CDROM) type = DRIVE_REMOTE;
1476 return type;
1480 /***********************************************************************
1481 * GetDriveTypeA (KERNEL32.@)
1483 * Returns the type of the disk drive specified. If root is NULL the
1484 * root of the current directory is used.
1486 * RETURNS
1488 * Type of drive (from Win32 SDK):
1490 * DRIVE_UNKNOWN unable to find out anything about the drive
1491 * DRIVE_NO_ROOT_DIR nonexistent root dir
1492 * DRIVE_REMOVABLE the disk can be removed from the machine
1493 * DRIVE_FIXED the disk can not be removed from the machine
1494 * DRIVE_REMOTE network disk
1495 * DRIVE_CDROM CDROM drive
1496 * DRIVE_RAMDISK virtual disk in RAM
1498 UINT WINAPI GetDriveTypeA(LPCSTR root) /* [in] String describing drive */
1500 int drive;
1501 TRACE("(%s)\n", debugstr_a(root));
1503 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1504 else
1506 if ((root[1]) && (root[1] != ':'))
1508 WARN("invalid root %s\n", debugstr_a(root));
1509 return DRIVE_NO_ROOT_DIR;
1511 drive = toupper(root[0]) - 'A';
1513 return DRIVE_GetType(drive);
1517 /***********************************************************************
1518 * GetDriveTypeW (KERNEL32.@)
1520 UINT WINAPI GetDriveTypeW( LPCWSTR root )
1522 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1523 UINT ret = GetDriveTypeA( xpath );
1524 HeapFree( GetProcessHeap(), 0, xpath );
1525 return ret;
1529 /***********************************************************************
1530 * GetCurrentDirectory (KERNEL.411)
1532 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1534 return (UINT16)DRIVE_GetCurrentDirectory(buflen, buf);
1538 /***********************************************************************
1539 * GetCurrentDirectoryA (KERNEL32.@)
1541 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1543 UINT ret;
1544 char longname[MAX_PATHNAME_LEN];
1545 char shortname[MAX_PATHNAME_LEN];
1546 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1547 if ( ret > MAX_PATHNAME_LEN ) {
1548 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1549 return ret;
1551 GetLongPathNameA(shortname, longname, MAX_PATHNAME_LEN);
1552 ret = strlen( longname ) + 1;
1553 if (ret > buflen) return ret;
1554 strcpy(buf, longname);
1555 return ret - 1;
1558 /***********************************************************************
1559 * GetCurrentDirectoryW (KERNEL32.@)
1561 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1563 LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
1564 UINT ret = GetCurrentDirectoryA( buflen, xpath );
1565 if (ret < buflen) ret = MultiByteToWideChar( CP_ACP, 0, xpath, -1, buf, buflen ) - 1;
1566 HeapFree( GetProcessHeap(), 0, xpath );
1567 return ret;
1571 /***********************************************************************
1572 * SetCurrentDirectory (KERNEL.412)
1574 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1576 return SetCurrentDirectoryA( dir );
1580 /***********************************************************************
1581 * SetCurrentDirectoryA (KERNEL32.@)
1583 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1585 int drive, olddrive = DRIVE_GetCurrentDrive();
1587 if (!dir) {
1588 ERR_(file)("(NULL)!\n");
1589 return FALSE;
1591 if (dir[0] && (dir[1]==':'))
1593 drive = toupper( *dir ) - 'A';
1594 dir += 2;
1596 else
1597 drive = olddrive;
1599 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1600 sets pTask->curdir only if pTask->curdrive is drive */
1601 if (!(DRIVE_SetCurrentDrive( drive )))
1602 return FALSE;
1603 /* FIXME: what about empty strings? Add a \\ ? */
1604 if (!DRIVE_Chdir( drive, dir )) {
1605 DRIVE_SetCurrentDrive(olddrive);
1606 return FALSE;
1608 return TRUE;
1612 /***********************************************************************
1613 * SetCurrentDirectoryW (KERNEL32.@)
1615 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1617 LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1618 BOOL res = SetCurrentDirectoryA( dir );
1619 HeapFree( GetProcessHeap(), 0, dir );
1620 return res;
1624 /***********************************************************************
1625 * GetLogicalDriveStringsA (KERNEL32.@)
1627 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1629 int drive, count;
1631 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1632 if (DRIVE_IsValid(drive)) count++;
1633 if ((count * 4) + 1 <= len)
1635 LPSTR p = buffer;
1636 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1637 if (DRIVE_IsValid(drive))
1639 *p++ = 'a' + drive;
1640 *p++ = ':';
1641 *p++ = '\\';
1642 *p++ = '\0';
1644 *p = '\0';
1645 return count * 4;
1647 else
1648 return (count * 4) + 1; /* account for terminating null */
1649 /* The API tells about these different return values */
1653 /***********************************************************************
1654 * GetLogicalDriveStringsW (KERNEL32.@)
1656 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1658 int drive, count;
1660 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1661 if (DRIVE_IsValid(drive)) count++;
1662 if (count * 4 * sizeof(WCHAR) <= len)
1664 LPWSTR p = buffer;
1665 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1666 if (DRIVE_IsValid(drive))
1668 *p++ = (WCHAR)('a' + drive);
1669 *p++ = (WCHAR)':';
1670 *p++ = (WCHAR)'\\';
1671 *p++ = (WCHAR)'\0';
1673 *p = (WCHAR)'\0';
1675 return count * 4 * sizeof(WCHAR);
1679 /***********************************************************************
1680 * GetLogicalDrives (KERNEL32.@)
1682 DWORD WINAPI GetLogicalDrives(void)
1684 DWORD ret = 0;
1685 int drive;
1687 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1689 if ( (DRIVE_IsValid(drive)) ||
1690 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1691 ret |= (1 << drive);
1693 return ret;
1697 /***********************************************************************
1698 * GetVolumeInformationA (KERNEL32.@)
1700 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1701 DWORD label_len, DWORD *serial,
1702 DWORD *filename_len, DWORD *flags,
1703 LPSTR fsname, DWORD fsname_len )
1705 int drive;
1706 char *cp;
1708 /* FIXME, SetLastError()s missing */
1710 if (!root) drive = DRIVE_GetCurrentDrive();
1711 else
1713 if ((root[1]) && (root[1] != ':'))
1715 WARN("invalid root '%s'\n",root);
1716 return FALSE;
1718 drive = toupper(root[0]) - 'A';
1720 if (!DRIVE_IsValid( drive )) return FALSE;
1721 if (label)
1723 lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1724 cp = label + strlen(label);
1725 while (cp != label && *(cp-1) == ' ') cp--;
1726 *cp = '\0';
1728 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1730 /* Set the filesystem information */
1731 /* Note: we only emulate a FAT fs at present */
1733 if (filename_len) {
1734 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1735 *filename_len = 12;
1736 else
1737 *filename_len = 255;
1739 if (flags)
1741 *flags=0;
1742 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1743 *flags|=FS_CASE_SENSITIVE;
1744 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1745 *flags|=FS_CASE_IS_PRESERVED;
1747 if (fsname) {
1748 /* Diablo checks that return code ... */
1749 if (DOSDrives[drive].type == DRIVE_CDROM)
1750 lstrcpynA( fsname, "CDFS", fsname_len );
1751 else
1752 lstrcpynA( fsname, "FAT", fsname_len );
1754 return TRUE;
1758 /***********************************************************************
1759 * GetVolumeInformationW (KERNEL32.@)
1761 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1762 DWORD label_len, DWORD *serial,
1763 DWORD *filename_len, DWORD *flags,
1764 LPWSTR fsname, DWORD fsname_len )
1766 LPSTR xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1767 LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1768 LPSTR xfsname = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1769 BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1770 filename_len, flags, xfsname,
1771 fsname_len );
1772 if (ret)
1774 if (label) MultiByteToWideChar( CP_ACP, 0, xvolname, -1, label, label_len );
1775 if (fsname) MultiByteToWideChar( CP_ACP, 0, xfsname, -1, fsname, fsname_len );
1777 HeapFree( GetProcessHeap(), 0, xroot );
1778 HeapFree( GetProcessHeap(), 0, xvolname );
1779 HeapFree( GetProcessHeap(), 0, xfsname );
1780 return ret;
1783 /***********************************************************************
1784 * SetVolumeLabelA (KERNEL32.@)
1786 BOOL WINAPI SetVolumeLabelA( LPCSTR root, LPCSTR volname )
1788 int drive;
1790 /* FIXME, SetLastErrors missing */
1792 if (!root) drive = DRIVE_GetCurrentDrive();
1793 else
1795 if ((root[1]) && (root[1] != ':'))
1797 WARN("invalid root '%s'\n",root);
1798 return FALSE;
1800 drive = toupper(root[0]) - 'A';
1802 if (!DRIVE_IsValid( drive )) return FALSE;
1804 /* some copy protection stuff check this */
1805 if (DOSDrives[drive].type == DRIVE_CDROM) return FALSE;
1807 FIXME("(%s,%s),stub!\n", root, volname);
1808 return TRUE;
1811 /***********************************************************************
1812 * SetVolumeLabelW (KERNEL32.@)
1814 BOOL WINAPI SetVolumeLabelW(LPCWSTR rootpath,LPCWSTR volname)
1816 LPSTR xroot, xvol;
1817 BOOL ret;
1819 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath);
1820 xvol = HEAP_strdupWtoA( GetProcessHeap(), 0, volname);
1821 ret = SetVolumeLabelA( xroot, xvol );
1822 HeapFree( GetProcessHeap(), 0, xroot );
1823 HeapFree( GetProcessHeap(), 0, xvol );
1824 return ret;