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)
14 #include "wine/port.h"
21 #include <sys/types.h>
27 #ifdef HAVE_SYS_PARAM_H
28 # include <sys/param.h>
30 #ifdef STATFS_DEFINED_BY_SYS_VFS
33 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
34 # include <sys/mount.h>
36 # ifdef STATFS_DEFINED_BY_SYS_STATFS
37 # include <sys/statfs.h>
44 #include "wine/winbase16.h" /* for GetCurrentTask */
52 #include "debugtools.h"
53 #include "wine/server.h"
58 DEFAULT_DEBUG_CHANNEL(dosfs);
59 DECLARE_DEBUG_CHANNEL(file);
63 char *root; /* root dir in Unix format without trailing / */
64 char *dos_cwd; /* cwd in DOS format without leading or trailing \ */
65 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
66 char *device; /* raw device path */
67 char label_conf[12]; /* drive label as cfg'd in wine config */
68 char label_read[12]; /* drive label as read from device */
69 DWORD serial_conf; /* drive serial number as cfg'd in wine config */
70 UINT type; /* drive type */
71 UINT flags; /* drive flags */
72 dev_t dev; /* unix device number */
73 ino_t ino; /* unix inode number */
77 static const char * const DRIVE_Types[] =
79 "", /* DRIVE_UNKNOWN */
80 "", /* DRIVE_NO_ROOT_DIR */
81 "floppy", /* DRIVE_REMOVABLE */
82 "hd", /* DRIVE_FIXED */
83 "network", /* DRIVE_REMOTE */
84 "cdrom", /* DRIVE_CDROM */
85 "ramdisk" /* DRIVE_RAMDISK */
89 /* Known filesystem types */
97 static const FS_DESCR DRIVE_Filesystems[] =
99 { "unix", DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
100 { "msdos", DRIVE_SHORT_NAMES },
101 { "dos", DRIVE_SHORT_NAMES },
102 { "fat", DRIVE_SHORT_NAMES },
103 { "vfat", DRIVE_CASE_PRESERVING },
104 { "win95", DRIVE_CASE_PRESERVING },
109 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
110 static int DRIVE_CurDrive = -1;
112 static HTASK16 DRIVE_LastTask = 0;
114 /* strdup on the process heap */
115 inline static char *heap_strdup( const char *str )
117 INT len = strlen(str) + 1;
118 LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
119 if (p) memcpy( p, str, len );
123 /***********************************************************************
126 static UINT DRIVE_GetDriveType( const char *name )
131 PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
132 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
134 if (!strcasecmp( buffer, DRIVE_Types[i] )) return i;
136 MESSAGE("%s: unknown drive type '%s', defaulting to 'hd'.\n",
142 /***********************************************************************
145 static UINT DRIVE_GetFSFlags( const char *name, const char *value )
147 const FS_DESCR *descr;
149 for (descr = DRIVE_Filesystems; descr->name; descr++)
150 if (!strcasecmp( value, descr->name )) return descr->flags;
151 MESSAGE("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
153 return DRIVE_CASE_PRESERVING;
157 /***********************************************************************
162 int i, len, count = 0;
163 char name[] = "Drive A";
164 char drive_env[] = "=A:";
165 char path[MAX_PATHNAME_LEN];
167 struct stat drive_stat_buffer;
171 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
173 PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
176 p = path + strlen(path) - 1;
177 while ((p > path) && (*p == '/')) *p-- = '\0';
181 drive->root = heap_strdup( path );
185 /* relative paths are relative to config dir */
186 const char *config = get_config_dir();
187 drive->root = HeapAlloc( GetProcessHeap(), 0, strlen(config) + strlen(path) + 2 );
188 sprintf( drive->root, "%s/%s", config, path );
191 if (stat( drive->root, &drive_stat_buffer ))
193 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
194 drive->root, strerror(errno), 'A' + i);
195 HeapFree( GetProcessHeap(), 0, drive->root );
199 if (!S_ISDIR(drive_stat_buffer.st_mode))
201 MESSAGE("%s is not a directory, ignoring drive %c:\n",
202 drive->root, 'A' + i );
203 HeapFree( GetProcessHeap(), 0, drive->root );
208 drive->dos_cwd = heap_strdup( "" );
209 drive->unix_cwd = heap_strdup( "" );
210 drive->type = DRIVE_GetDriveType( name );
211 drive->device = NULL;
213 drive->dev = drive_stat_buffer.st_dev;
214 drive->ino = drive_stat_buffer.st_ino;
216 /* Get the drive label */
217 PROFILE_GetWineIniString( name, "Label", "", drive->label_conf, 12 );
218 if ((len = strlen(drive->label_conf)) < 11)
220 /* Pad label with spaces */
221 memset( drive->label_conf + len, ' ', 11 - len );
222 drive->label_conf[11] = '\0';
225 /* Get the serial number */
226 PROFILE_GetWineIniString( name, "Serial", "12345678",
227 buffer, sizeof(buffer) );
228 drive->serial_conf = strtoul( buffer, NULL, 16 );
230 /* Get the filesystem type */
231 PROFILE_GetWineIniString( name, "Filesystem", "win95",
232 buffer, sizeof(buffer) );
233 drive->flags = DRIVE_GetFSFlags( name, buffer );
236 PROFILE_GetWineIniString( name, "Device", "",
237 buffer, sizeof(buffer) );
240 drive->device = heap_strdup( buffer );
241 if (PROFILE_GetWineIniBool( name, "ReadVolInfo", 1))
242 drive->flags |= DRIVE_READ_VOL_INFO;
245 /* Get the FailReadOnly flag */
246 if (PROFILE_GetWineIniBool( name, "FailReadOnly", 0 ))
247 drive->flags |= DRIVE_FAIL_READ_ONLY;
249 /* Make the first hard disk the current drive */
250 if ((DRIVE_CurDrive == -1) && (drive->type == DRIVE_FIXED))
254 TRACE("%s: path=%s type=%s label='%s' serial=%08lx "
255 "flags=%08x dev=%x ino=%x\n",
256 name, drive->root, DRIVE_Types[drive->type],
257 drive->label_conf, drive->serial_conf, drive->flags,
258 (int)drive->dev, (int)drive->ino );
260 else WARN("%s: not defined\n", name );
265 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
266 /* Create a C drive pointing to Unix root dir */
267 DOSDrives[2].root = heap_strdup( "/" );
268 DOSDrives[2].dos_cwd = heap_strdup( "" );
269 DOSDrives[2].unix_cwd = heap_strdup( "" );
270 strcpy( DOSDrives[2].label_conf, "Drive C " );
271 DOSDrives[2].serial_conf = 12345678;
272 DOSDrives[2].type = DRIVE_FIXED;
273 DOSDrives[2].device = NULL;
274 DOSDrives[2].flags = 0;
278 /* Make sure the current drive is valid */
279 if (DRIVE_CurDrive == -1)
281 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
283 if (drive->root && !(drive->flags & DRIVE_DISABLED))
291 /* get current working directory info for all drives */
292 for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
294 if (!GetEnvironmentVariableA(drive_env, path, sizeof(path))) continue;
296 if (toupper(path[0]) != drive_env[1] || path[1] != ':') continue;
297 DRIVE_Chdir( i, path + 2 );
303 /***********************************************************************
306 int DRIVE_IsValid( int drive )
308 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
309 return (DOSDrives[drive].root &&
310 !(DOSDrives[drive].flags & DRIVE_DISABLED));
314 /***********************************************************************
315 * DRIVE_GetCurrentDrive
317 int DRIVE_GetCurrentDrive(void)
319 TDB *pTask = TASK_GetCurrent();
320 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
321 return DRIVE_CurDrive;
325 /***********************************************************************
326 * DRIVE_SetCurrentDrive
328 int DRIVE_SetCurrentDrive( int drive )
330 TDB *pTask = TASK_GetCurrent();
331 if (!DRIVE_IsValid( drive ))
333 SetLastError( ERROR_INVALID_DRIVE );
336 TRACE("%c:\n", 'A' + drive );
337 DRIVE_CurDrive = drive;
338 if (pTask) pTask->curdrive = drive | 0x80;
339 chdir(DRIVE_GetUnixCwd(drive));
344 /***********************************************************************
345 * DRIVE_FindDriveRoot
347 * Find a drive for which the root matches the beginning of the given path.
348 * This can be used to translate a Unix path into a drive + DOS path.
349 * Return value is the drive, or -1 on error. On success, path is modified
350 * to point to the beginning of the DOS path.
352 int DRIVE_FindDriveRoot( const char **path )
354 /* idea: check at all '/' positions.
355 * If the device and inode of that path is identical with the
356 * device and inode of the current drive then we found a solution.
357 * If there is another drive pointing to a deeper position in
358 * the file tree, we want to find that one, not the earlier solution.
360 int drive, rootdrive = -1;
361 char buffer[MAX_PATHNAME_LEN];
363 const char *p = *path;
366 strcpy( buffer, "/" );
369 if (stat( buffer, &st ) || !S_ISDIR( st.st_mode )) break;
373 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
375 if (!DOSDrives[drive].root ||
376 (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
378 if ((DOSDrives[drive].dev == st.st_dev) &&
379 (DOSDrives[drive].ino == st.st_ino))
387 /* Get the next path component */
390 while ((*p == '/') || (*p == '\\')) p++;
392 while (!IS_END_OF_NAME(*p)) *next++ = *p++;
398 TRACE("%s -> drive %c:, root='%s', name='%s'\n",
399 buffer, 'A' + rootdrive, DOSDrives[rootdrive].root, *path );
404 /***********************************************************************
407 const char * DRIVE_GetRoot( int drive )
409 if (!DRIVE_IsValid( drive )) return NULL;
410 return DOSDrives[drive].root;
414 /***********************************************************************
417 const char * DRIVE_GetDosCwd( int drive )
419 TDB *pTask = TASK_GetCurrent();
420 if (!DRIVE_IsValid( drive )) return NULL;
422 /* Check if we need to change the directory to the new task. */
423 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
424 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
425 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
427 /* Perform the task-switch */
428 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
429 DRIVE_LastTask = GetCurrentTask();
431 return DOSDrives[drive].dos_cwd;
435 /***********************************************************************
438 const char * DRIVE_GetUnixCwd( int drive )
440 TDB *pTask = TASK_GetCurrent();
441 if (!DRIVE_IsValid( drive )) return NULL;
443 /* Check if we need to change the directory to the new task. */
444 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
445 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
446 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
448 /* Perform the task-switch */
449 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
450 DRIVE_LastTask = GetCurrentTask();
452 return DOSDrives[drive].unix_cwd;
456 /***********************************************************************
459 const char * DRIVE_GetDevice( int drive )
461 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
464 /******************************************************************
465 * static WORD CDROM_Data_FindBestVoldesc
469 static WORD CDROM_Data_FindBestVoldesc(int fd)
471 BYTE cur_vd_type, max_vd_type = 0;
472 unsigned int offs, best_offs = 0, extra_offs = 0;
475 for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
477 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
478 * the volume label is displaced forward by 8
480 lseek(fd, offs + 11, SEEK_SET); /* check for non-ISO9660 signature */
482 if ((sig[0] == 'R') && (sig[1] == 'O') && (sig[2]=='M'))
486 lseek(fd, offs + extra_offs, SEEK_SET);
487 read(fd, &cur_vd_type, 1);
488 if (cur_vd_type == 0xff) /* voldesc set terminator */
490 if (cur_vd_type > max_vd_type)
492 max_vd_type = cur_vd_type;
493 best_offs = offs + extra_offs;
499 /***********************************************************************
500 * DRIVE_ReadSuperblock
503 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
504 * to check, that they are writing on a FAT filesystem !
506 int DRIVE_ReadSuperblock (int drive, char * buff)
508 #define DRIVE_SUPER 96
512 if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
513 if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
516 if (!DOSDrives[drive].device)
517 ERR("No device configured for drive %c: !\n", 'A'+drive);
519 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
520 (stat(DOSDrives[drive].device, &st)) ?
521 "not available or symlink not valid ?" : "no permission");
522 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
523 PROFILE_UsageWineIni();
527 switch(DOSDrives[drive].type)
529 case DRIVE_REMOVABLE:
534 offs = CDROM_Data_FindBestVoldesc(fd);
541 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs)) return -4;
542 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER) return -2;
544 switch(DOSDrives[drive].type)
546 case DRIVE_REMOVABLE:
548 if ((buff[0x26]!=0x29) || /* Check for FAT present */
549 /* FIXME: do really all FAT have their name beginning with
550 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
551 memcmp( buff+0x36,"FAT",3))
553 ERR("The filesystem is not FAT !! (device=%s)\n",
554 DOSDrives[drive].device);
559 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
561 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
572 /***********************************************************************
573 * DRIVE_WriteSuperblockEntry
576 * We are writing as little as possible (ie. not the whole SuperBlock)
577 * not to interfere with kernel. The drive can be mounted !
579 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
583 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
585 ERR("Cannot open the device %s (for writing)\n",
586 DOSDrives[drive].device);
589 if (lseek(fd,ofs,SEEK_SET)!=ofs)
591 ERR("lseek failed on device %s !\n",
592 DOSDrives[drive].device);
596 if (write(fd,buff,len)!=len)
599 ERR("Cannot write on %s !\n",
600 DOSDrives[drive].device);
606 /******************************************************************
607 * static HANDLE CDROM_Open
611 static HANDLE CDROM_Open(int drive)
615 strcpy(root, "\\\\.\\A:");
618 return CreateFileA(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
621 /**************************************************************************
622 * CDROM_Data_GetLabel [internal]
624 DWORD CDROM_Data_GetLabel(int drive, char *label)
626 #define LABEL_LEN 32+1
627 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
628 WORD offs = CDROM_Data_FindBestVoldesc(dev);
629 WCHAR label_read[LABEL_LEN]; /* Unicode possible, too */
630 DWORD unicode_id = 0;
634 if ((lseek(dev, offs+0x58, SEEK_SET) == offs+0x58)
635 && (read(dev, &unicode_id, 3) == 3))
637 int ver = (unicode_id & 0xff0000) >> 16;
639 if ((lseek(dev, offs+0x28, SEEK_SET) != offs+0x28)
640 || (read(dev, &label_read, LABEL_LEN) != LABEL_LEN))
644 if ((LOWORD(unicode_id) == 0x2f25) /* Unicode ID */
645 && ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
646 { /* yippee, unicode */
649 for (i=0; i<LABEL_LEN;i++)
650 { /* Motorola -> Intel Unicode conversion :-\ */
652 label_read[i] = (ch << 8) | (ch >> 8);
654 WideCharToMultiByte( CP_ACP, 0, label_read, -1, label, 12, NULL, NULL );
659 strncpy(label, (LPSTR)label_read, 11);
667 ERR("error reading label !\n");
671 /**************************************************************************
672 * CDROM_GetLabel [internal]
674 static DWORD CDROM_GetLabel(int drive, char *label)
676 HANDLE h = CDROM_Open(drive);
681 if (!h || !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
684 switch (cdd.DiskData & 0x03)
686 case CDROM_DISK_DATA_TRACK:
687 if (!CDROM_Data_GetLabel(drive, label))
689 case CDROM_DISK_AUDIO_TRACK:
690 strcpy(label, "Audio CD ");
692 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
693 FIXME("Need to get the label of a mixed mode CD: not implemented yet !\n");
699 TRACE("CD: label is '%s'.\n", label);
703 /***********************************************************************
706 const char * DRIVE_GetLabel( int drive )
709 char buff[DRIVE_SUPER];
712 if (!DRIVE_IsValid( drive )) return NULL;
713 if (DOSDrives[drive].type == DRIVE_CDROM)
715 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
718 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
720 if (DRIVE_ReadSuperblock(drive,(char *) buff))
721 ERR("Invalid or unreadable superblock on %s (%c:).\n",
722 DOSDrives[drive].device, (char)(drive+'A'));
724 if (DOSDrives[drive].type == DRIVE_REMOVABLE ||
725 DOSDrives[drive].type == DRIVE_FIXED)
728 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
729 if (offs != -1) memcpy(DOSDrives[drive].label_read,buff+offs,11);
730 DOSDrives[drive].label_read[11]='\0';
736 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
739 #define CDFRAMES_PERSEC 75
740 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
741 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
742 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
744 /**************************************************************************
745 * CDROM_Audio_GetSerial [internal]
747 static DWORD CDROM_Audio_GetSerial(HANDLE h)
749 unsigned long serial = 0;
752 DWORD dwStart, dwEnd, br;
755 if (!DeviceIoControl(h, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0))
759 * wMagic collects the wFrames from track 1
760 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
762 * There it is collected for correcting the serial when there are less than
765 wMagic = toc.TrackData[0].Address[2];
766 dwStart = FRAME_OF_TOC(toc, toc.FirstTrack);
768 for (i = 0; i <= toc.LastTrack - toc.FirstTrack; i++) {
769 serial += (toc.TrackData[i].Address[0] << 16) |
770 (toc.TrackData[i].Address[1] << 8) | toc.TrackData[i].Address[2];
772 dwEnd = FRAME_OF_TOC(toc, toc.LastTrack + 1);
774 if (toc.LastTrack - toc.FirstTrack + 1 < 3)
775 serial += wMagic + (dwEnd - dwStart);
780 /**************************************************************************
781 * CDROM_Data_GetSerial [internal]
783 static DWORD CDROM_Data_GetSerial(int drive)
785 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
791 BYTE b0 = 0, b1 = 1, b2 = 2, b3 = 3;
794 if (dev == -1) return 0;
795 offs = CDROM_Data_FindBestVoldesc(dev);
804 lseek(dev, offs, SEEK_SET);
805 read(dev, buf, 2048);
807 * OK, another braindead one... argh. Just believe it.
808 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
809 * It's true and nobody will ever be able to change it.
811 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
813 if ((ovi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (ovi.dwMajorVersion >= 4))
815 b0 = 3; b1 = 2; b2 = 1; b3 = 0;
817 for (i = 0; i < 2048; i += 4)
819 /* DON'T optimize this into DWORD !! (breaks overflow) */
820 serial.p[b0] += buf[i+b0];
821 serial.p[b1] += buf[i+b1];
822 serial.p[b2] += buf[i+b2];
823 serial.p[b3] += buf[i+b3];
830 /**************************************************************************
831 * CDROM_GetSerial [internal]
833 static DWORD CDROM_GetSerial(int drive)
836 HANDLE h = CDROM_Open(drive);
840 if (!h || ! !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
843 switch (cdd.DiskData & 0x03)
845 case CDROM_DISK_DATA_TRACK:
846 /* hopefully a data CD */
847 serial = CDROM_Data_GetSerial(drive);
849 case CDROM_DISK_AUDIO_TRACK:
851 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
852 serial = CDROM_Audio_GetSerial(h);
859 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial), LOWORD(serial));
866 /***********************************************************************
867 * DRIVE_GetSerialNumber
869 DWORD DRIVE_GetSerialNumber( int drive )
872 char buff[DRIVE_SUPER];
874 if (!DRIVE_IsValid( drive )) return 0;
876 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
878 switch(DOSDrives[drive].type)
880 case DRIVE_REMOVABLE:
882 if (DRIVE_ReadSuperblock(drive,(char *) buff))
883 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
884 " Maybe not FAT?\n" ,
885 DOSDrives[drive].device, 'A'+drive);
887 serial = *((DWORD*)(buff+0x27));
890 serial = CDROM_GetSerial(drive);
893 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
897 return (serial) ? serial : DOSDrives[drive].serial_conf;
901 /***********************************************************************
902 * DRIVE_SetSerialNumber
904 int DRIVE_SetSerialNumber( int drive, DWORD serial )
906 char buff[DRIVE_SUPER];
908 if (!DRIVE_IsValid( drive )) return 0;
910 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
912 if ((DOSDrives[drive].type != DRIVE_REMOVABLE) &&
913 (DOSDrives[drive].type != DRIVE_FIXED)) return 0;
914 /* check, if the drive has a FAT filesystem */
915 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
916 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
920 if (DOSDrives[drive].type == DRIVE_CDROM) return 0;
921 DOSDrives[drive].serial_conf = serial;
926 /***********************************************************************
929 static UINT DRIVE_GetType( int drive )
931 if (!DRIVE_IsValid( drive )) return DRIVE_UNKNOWN;
932 return DOSDrives[drive].type;
936 /***********************************************************************
939 UINT DRIVE_GetFlags( int drive )
941 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
942 return DOSDrives[drive].flags;
946 /***********************************************************************
949 int DRIVE_Chdir( int drive, const char *path )
951 DOS_FULL_NAME full_name;
952 char buffer[MAX_PATHNAME_LEN];
954 BY_HANDLE_FILE_INFORMATION info;
955 TDB *pTask = TASK_GetCurrent();
957 strcpy( buffer, "A:" );
959 TRACE("(%s,%s)\n", buffer, path );
960 lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
962 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
963 if (!FILE_Stat( full_name.long_name, &info )) return 0;
964 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
966 SetLastError( ERROR_FILE_NOT_FOUND );
969 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
970 while (*unix_cwd == '/') unix_cwd++;
972 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
973 'A' + drive, unix_cwd, full_name.short_name + 3 );
975 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
976 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
977 DOSDrives[drive].dos_cwd = heap_strdup( full_name.short_name + 3 );
978 DOSDrives[drive].unix_cwd = heap_strdup( unix_cwd );
980 if (pTask && (pTask->curdrive & 0x80) &&
981 ((pTask->curdrive & ~0x80) == drive))
983 lstrcpynA( pTask->curdir, full_name.short_name + 2,
984 sizeof(pTask->curdir) );
985 DRIVE_LastTask = GetCurrentTask();
986 chdir(unix_cwd); /* Only change if on current drive */
992 /***********************************************************************
995 int DRIVE_Disable( int drive )
997 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
999 SetLastError( ERROR_INVALID_DRIVE );
1002 DOSDrives[drive].flags |= DRIVE_DISABLED;
1007 /***********************************************************************
1010 int DRIVE_Enable( int drive )
1012 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1014 SetLastError( ERROR_INVALID_DRIVE );
1017 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
1022 /***********************************************************************
1023 * DRIVE_SetLogicalMapping
1025 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
1027 /* If new_drive is already valid, do nothing and return 0
1028 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1030 DOSDRIVE *old, *new;
1032 old = DOSDrives + existing_drive;
1033 new = DOSDrives + new_drive;
1035 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
1037 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
1039 SetLastError( ERROR_INVALID_DRIVE );
1045 TRACE("Can't map drive %c: to already existing drive %c:\n",
1046 'A' + existing_drive, 'A' + new_drive );
1047 /* it is already mapped there, so return success */
1048 if (!strcmp(old->root,new->root))
1053 new->root = heap_strdup( old->root );
1054 new->dos_cwd = heap_strdup( old->dos_cwd );
1055 new->unix_cwd = heap_strdup( old->unix_cwd );
1056 new->device = heap_strdup( old->device );
1057 memcpy ( new->label_conf, old->label_conf, 12 );
1058 memcpy ( new->label_read, old->label_read, 12 );
1059 new->serial_conf = old->serial_conf;
1060 new->type = old->type;
1061 new->flags = old->flags;
1062 new->dev = old->dev;
1063 new->ino = old->ino;
1065 TRACE("Drive %c: is now equal to drive %c:\n",
1066 'A' + new_drive, 'A' + existing_drive );
1072 /***********************************************************************
1075 * Open the drive raw device and return a Unix fd (or -1 on error).
1077 int DRIVE_OpenDevice( int drive, int flags )
1079 if (!DRIVE_IsValid( drive )) return -1;
1080 return open( DOSDrives[drive].device, flags );
1084 /***********************************************************************
1087 * Read raw sectors from a device
1089 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1093 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1095 lseek( fd, begin * 512, SEEK_SET );
1096 /* FIXME: check errors */
1097 read( fd, dataptr, nr_sect * 512 );
1102 memset(dataptr, 0, nr_sect * 512);
1105 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
1106 if (begin == 1) *dataptr = 0xf8;
1115 /***********************************************************************
1118 * Write raw sectors to a device
1120 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1124 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1126 lseek( fd, begin * 512, SEEK_SET );
1127 /* FIXME: check errors */
1128 write( fd, dataptr, nr_sect * 512 );
1132 if (!(fake_success))
1139 /***********************************************************************
1140 * DRIVE_GetFreeSpace
1142 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
1143 PULARGE_INTEGER available )
1147 if (!DRIVE_IsValid(drive))
1149 SetLastError( ERROR_INVALID_DRIVE );
1153 /* FIXME: add autoconf check for this */
1154 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1155 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
1157 if (statfs( DOSDrives[drive].root, &info) < 0)
1161 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
1165 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bsize, info.f_blocks );
1166 #ifdef STATFS_HAS_BAVAIL
1167 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bavail, info.f_bsize );
1169 # ifdef STATFS_HAS_BFREE
1170 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bfree, info.f_bsize );
1172 # error "statfs has no bfree/bavail member!"
1175 if (DOSDrives[drive].type == DRIVE_CDROM)
1176 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1177 available->QuadPart = 0;
1182 /***********************************************************************
1183 * DRIVE_GetCurrentDirectory
1184 * Returns "X:\\path\\etc\\".
1186 * Despite the API description, return required length including the
1187 * terminating null when buffer too small. This is the real behaviour.
1190 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPSTR buf )
1193 const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1196 ret = strlen(s) + 3; /* length of WHOLE current directory */
1197 if (ret >= buflen) return ret + 1;
1198 lstrcpynA( buf, "A:\\", min( 4u, buflen ) );
1199 if (buflen) buf[0] += DRIVE_GetCurrentDrive();
1200 if (buflen > 3) lstrcpynA( buf + 3, s, buflen - 3 );
1205 /***********************************************************************
1208 * Build the environment array containing the drives' current directories.
1209 * Resulting pointer must be freed with HeapFree.
1211 char *DRIVE_BuildEnv(void)
1214 const char *cwd[MAX_DOS_DRIVES];
1217 for (i = 0; i < MAX_DOS_DRIVES; i++)
1219 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0]) length += strlen(cwd[i]) + 8;
1221 if (!(env = HeapAlloc( GetProcessHeap(), 0, length+1 ))) return NULL;
1222 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
1224 if (cwd[i] && cwd[i][0])
1225 p += sprintf( p, "=%c:=%c:\\%s", 'A'+i, 'A'+i, cwd[i] ) + 1;
1232 /***********************************************************************
1233 * GetDiskFreeSpace (KERNEL.422)
1235 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
1236 LPDWORD sector_bytes, LPDWORD free_clusters,
1237 LPDWORD total_clusters )
1239 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
1240 free_clusters, total_clusters );
1244 /***********************************************************************
1245 * GetDiskFreeSpaceA (KERNEL32.@)
1247 * Fails if expression resulting from current drive's dir and "root"
1248 * is not a root dir of the target drive.
1250 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1251 * if the corresponding info is unneeded.
1253 * FIXME: needs to support UNC names from Win95 OSR2 on.
1255 * Behaviour under Win95a:
1256 * CurrDir root result
1257 * "E:\\TEST" "E:" FALSE
1261 * "E:\\TEST" "\\" TRUE
1262 * "E:\\TEST" ":\\" FALSE
1263 * "E:\\TEST" "E:\\" TRUE
1264 * "E:\\TEST" "" FALSE
1265 * "E:\\" "" FALSE (!)
1267 * "E:\\TEST" 0x0 TRUE (!)
1268 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1269 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1271 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1272 LPDWORD sector_bytes, LPDWORD free_clusters,
1273 LPDWORD total_clusters )
1275 int drive, sec_size;
1276 ULARGE_INTEGER size,available;
1280 if ((!root) || (strcmp(root,"\\") == 0))
1281 drive = DRIVE_GetCurrentDrive();
1283 if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
1285 drive = toupper(root[0]) - 'A';
1287 if (path[0] == '\0')
1288 path = DRIVE_GetDosCwd(drive);
1290 if (path[0] == '\\')
1292 if (strlen(path)) /* oops, we are in a subdir */
1298 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1300 /* Cap the size and available at 2GB as per specs. */
1301 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1303 size.s.HighPart = 0;
1304 size.s.LowPart = 0x7fffffff;
1306 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1308 available.s.HighPart =0;
1309 available.s.LowPart = 0x7fffffff;
1311 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
1312 size.s.LowPart /= sec_size;
1313 available.s.LowPart /= sec_size;
1314 /* fixme: probably have to adjust those variables too for CDFS */
1316 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1318 if (cluster_sectors)
1319 *cluster_sectors = cluster_sec;
1321 *sector_bytes = sec_size;
1323 *free_clusters = available.s.LowPart / cluster_sec;
1325 *total_clusters = size.s.LowPart / cluster_sec;
1330 /***********************************************************************
1331 * GetDiskFreeSpaceW (KERNEL32.@)
1333 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1334 LPDWORD sector_bytes, LPDWORD free_clusters,
1335 LPDWORD total_clusters )
1340 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1341 ret = GetDiskFreeSpaceA( xroot,cluster_sectors, sector_bytes,
1342 free_clusters, total_clusters );
1343 HeapFree( GetProcessHeap(), 0, xroot );
1348 /***********************************************************************
1349 * GetDiskFreeSpaceExA (KERNEL32.@)
1351 * This function is used to acquire the size of the available and
1352 * total space on a logical volume.
1356 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1357 * detailed error information.
1360 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
1361 PULARGE_INTEGER avail,
1362 PULARGE_INTEGER total,
1363 PULARGE_INTEGER totalfree)
1366 ULARGE_INTEGER size,available;
1368 if (!root) drive = DRIVE_GetCurrentDrive();
1370 { /* C: always works for GetDiskFreeSpaceEx */
1371 if ((root[1]) && ((root[1] != ':') || (root[2] && root[2] != '\\')))
1373 FIXME("there are valid root names which are not supported yet\n");
1374 /* ..like UNC names, for instance. */
1376 WARN("invalid root '%s'\n", root );
1379 drive = toupper(root[0]) - 'A';
1382 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1386 total->s.HighPart = size.s.HighPart;
1387 total->s.LowPart = size.s.LowPart;
1392 totalfree->s.HighPart = available.s.HighPart;
1393 totalfree->s.LowPart = available.s.LowPart;
1398 if (FIXME_ON(dosfs))
1400 /* On Windows2000, we need to check the disk quota
1401 allocated for the user owning the calling process. We
1402 don't want to be more obtrusive than necessary with the
1403 FIXME messages, so don't print the FIXME unless Wine is
1404 actually masquerading as Windows2000. */
1407 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1408 if (GetVersionExA(&ovi))
1410 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1411 FIXME("no per-user quota support yet\n");
1415 /* Quick hack, should eventually be fixed to work 100% with
1416 Windows2000 (see comment above). */
1417 avail->s.HighPart = available.s.HighPart;
1418 avail->s.LowPart = available.s.LowPart;
1424 /***********************************************************************
1425 * GetDiskFreeSpaceExW (KERNEL32.@)
1427 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1428 PULARGE_INTEGER total,
1429 PULARGE_INTEGER totalfree)
1434 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1435 ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
1436 HeapFree( GetProcessHeap(), 0, xroot );
1440 /***********************************************************************
1441 * GetDriveType (KERNEL.136)
1442 * This function returns the type of a drive in Win16.
1443 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1444 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1445 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1446 * do any pseudo-clever changes.
1449 * drivetype DRIVE_xxx
1451 UINT16 WINAPI GetDriveType16( UINT16 drive ) /* [in] number (NOT letter) of drive */
1453 UINT type = DRIVE_GetType(drive);
1454 TRACE("(%c:)\n", 'A' + drive );
1455 if (type == DRIVE_CDROM) type = DRIVE_REMOTE;
1460 /***********************************************************************
1461 * GetDriveTypeA (KERNEL32.@)
1463 * Returns the type of the disk drive specified. If root is NULL the
1464 * root of the current directory is used.
1468 * Type of drive (from Win32 SDK):
1470 * DRIVE_UNKNOWN unable to find out anything about the drive
1471 * DRIVE_NO_ROOT_DIR nonexistent root dir
1472 * DRIVE_REMOVABLE the disk can be removed from the machine
1473 * DRIVE_FIXED the disk can not be removed from the machine
1474 * DRIVE_REMOTE network disk
1475 * DRIVE_CDROM CDROM drive
1476 * DRIVE_RAMDISK virtual disk in RAM
1478 UINT WINAPI GetDriveTypeA(LPCSTR root) /* [in] String describing drive */
1481 TRACE("(%s)\n", debugstr_a(root));
1483 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1486 if ((root[1]) && (root[1] != ':'))
1488 WARN("invalid root %s\n", debugstr_a(root));
1489 return DRIVE_NO_ROOT_DIR;
1491 drive = toupper(root[0]) - 'A';
1493 return DRIVE_GetType(drive);
1497 /***********************************************************************
1498 * GetDriveTypeW (KERNEL32.@)
1500 UINT WINAPI GetDriveTypeW( LPCWSTR root )
1502 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1503 UINT ret = GetDriveTypeA( xpath );
1504 HeapFree( GetProcessHeap(), 0, xpath );
1509 /***********************************************************************
1510 * GetCurrentDirectory (KERNEL.411)
1512 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1514 return (UINT16)DRIVE_GetCurrentDirectory(buflen, buf);
1518 /***********************************************************************
1519 * GetCurrentDirectoryA (KERNEL32.@)
1521 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1524 char longname[MAX_PATHNAME_LEN];
1525 char shortname[MAX_PATHNAME_LEN];
1526 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1527 if ( ret > MAX_PATHNAME_LEN ) {
1528 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1531 GetLongPathNameA(shortname, longname, MAX_PATHNAME_LEN);
1532 ret = strlen( longname ) + 1;
1533 if (ret > buflen) return ret;
1534 strcpy(buf, longname);
1538 /***********************************************************************
1539 * GetCurrentDirectoryW (KERNEL32.@)
1541 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1543 LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
1544 UINT ret = GetCurrentDirectoryA( buflen, xpath );
1545 if (ret < buflen) ret = MultiByteToWideChar( CP_ACP, 0, xpath, -1, buf, buflen ) - 1;
1546 HeapFree( GetProcessHeap(), 0, xpath );
1551 /***********************************************************************
1552 * SetCurrentDirectory (KERNEL.412)
1554 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1556 return SetCurrentDirectoryA( dir );
1560 /***********************************************************************
1561 * SetCurrentDirectoryA (KERNEL32.@)
1563 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1565 int drive, olddrive = DRIVE_GetCurrentDrive();
1568 ERR_(file)("(NULL)!\n");
1571 if (dir[0] && (dir[1]==':'))
1573 drive = toupper( *dir ) - 'A';
1579 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1580 sets pTask->curdir only if pTask->curdrive is drive */
1581 if (!(DRIVE_SetCurrentDrive( drive )))
1583 /* FIXME: what about empty strings? Add a \\ ? */
1584 if (!DRIVE_Chdir( drive, dir )) {
1585 DRIVE_SetCurrentDrive(olddrive);
1592 /***********************************************************************
1593 * SetCurrentDirectoryW (KERNEL32.@)
1595 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1597 LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1598 BOOL res = SetCurrentDirectoryA( dir );
1599 HeapFree( GetProcessHeap(), 0, dir );
1604 /***********************************************************************
1605 * GetLogicalDriveStringsA (KERNEL32.@)
1607 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1611 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1612 if (DRIVE_IsValid(drive)) count++;
1613 if ((count * 4) + 1 <= len)
1616 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1617 if (DRIVE_IsValid(drive))
1628 return (count * 4) + 1; /* account for terminating null */
1629 /* The API tells about these different return values */
1633 /***********************************************************************
1634 * GetLogicalDriveStringsW (KERNEL32.@)
1636 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1640 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1641 if (DRIVE_IsValid(drive)) count++;
1642 if (count * 4 * sizeof(WCHAR) <= len)
1645 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1646 if (DRIVE_IsValid(drive))
1648 *p++ = (WCHAR)('a' + drive);
1655 return count * 4 * sizeof(WCHAR);
1659 /***********************************************************************
1660 * GetLogicalDrives (KERNEL32.@)
1662 DWORD WINAPI GetLogicalDrives(void)
1667 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1669 if ( (DRIVE_IsValid(drive)) ||
1670 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1671 ret |= (1 << drive);
1677 /***********************************************************************
1678 * GetVolumeInformationA (KERNEL32.@)
1680 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1681 DWORD label_len, DWORD *serial,
1682 DWORD *filename_len, DWORD *flags,
1683 LPSTR fsname, DWORD fsname_len )
1688 /* FIXME, SetLastError()s missing */
1690 if (!root) drive = DRIVE_GetCurrentDrive();
1693 if ((root[1]) && (root[1] != ':'))
1695 WARN("invalid root '%s'\n",root);
1698 drive = toupper(root[0]) - 'A';
1700 if (!DRIVE_IsValid( drive )) return FALSE;
1703 lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1704 cp = label + strlen(label);
1705 while (cp != label && *(cp-1) == ' ') cp--;
1708 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1710 /* Set the filesystem information */
1711 /* Note: we only emulate a FAT fs at present */
1714 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1717 *filename_len = 255;
1722 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1723 *flags|=FS_CASE_SENSITIVE;
1724 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1725 *flags|=FS_CASE_IS_PRESERVED;
1728 /* Diablo checks that return code ... */
1729 if (DOSDrives[drive].type == DRIVE_CDROM)
1730 lstrcpynA( fsname, "CDFS", fsname_len );
1732 lstrcpynA( fsname, "FAT", fsname_len );
1738 /***********************************************************************
1739 * GetVolumeInformationW (KERNEL32.@)
1741 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1742 DWORD label_len, DWORD *serial,
1743 DWORD *filename_len, DWORD *flags,
1744 LPWSTR fsname, DWORD fsname_len )
1746 LPSTR xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1747 LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1748 LPSTR xfsname = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1749 BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1750 filename_len, flags, xfsname,
1754 if (label) MultiByteToWideChar( CP_ACP, 0, xvolname, -1, label, label_len );
1755 if (fsname) MultiByteToWideChar( CP_ACP, 0, xfsname, -1, fsname, fsname_len );
1757 HeapFree( GetProcessHeap(), 0, xroot );
1758 HeapFree( GetProcessHeap(), 0, xvolname );
1759 HeapFree( GetProcessHeap(), 0, xfsname );
1763 /***********************************************************************
1764 * SetVolumeLabelA (KERNEL32.@)
1766 BOOL WINAPI SetVolumeLabelA( LPCSTR root, LPCSTR volname )
1770 /* FIXME, SetLastErrors missing */
1772 if (!root) drive = DRIVE_GetCurrentDrive();
1775 if ((root[1]) && (root[1] != ':'))
1777 WARN("invalid root '%s'\n",root);
1780 drive = toupper(root[0]) - 'A';
1782 if (!DRIVE_IsValid( drive )) return FALSE;
1784 /* some copy protection stuff check this */
1785 if (DOSDrives[drive].type == DRIVE_CDROM) return FALSE;
1787 FIXME("(%s,%s),stub!\n", root, volname);
1791 /***********************************************************************
1792 * SetVolumeLabelW (KERNEL32.@)
1794 BOOL WINAPI SetVolumeLabelW(LPCWSTR rootpath,LPCWSTR volname)
1799 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath);
1800 xvol = HEAP_strdupWtoA( GetProcessHeap(), 0, volname);
1801 ret = SetVolumeLabelA( xroot, xvol );
1802 HeapFree( GetProcessHeap(), 0, xroot );
1803 HeapFree( GetProcessHeap(), 0, xvol );