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)
20 #include <sys/types.h>
26 #ifdef HAVE_SYS_PARAM_H
27 # include <sys/param.h>
29 #ifdef STATFS_DEFINED_BY_SYS_VFS
32 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
33 # include <sys/mount.h>
35 # ifdef STATFS_DEFINED_BY_SYS_STATFS
36 # include <sys/statfs.h>
43 #include "wine/winbase16.h" /* for GetCurrentTask */
51 #include "wine/port.h"
53 #include "debugtools.h"
55 DEFAULT_DEBUG_CHANNEL(dosfs);
56 DECLARE_DEBUG_CHANNEL(file);
60 char *root; /* root dir in Unix format without trailing / */
61 char *dos_cwd; /* cwd in DOS format without leading or trailing \ */
62 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
63 char *device; /* raw device path */
64 char label_conf[12]; /* drive label as cfg'd in wine config */
65 char label_read[12]; /* drive label as read from device */
66 DWORD serial_conf; /* drive serial number as cfg'd in wine config */
67 UINT type; /* drive type */
68 UINT flags; /* drive flags */
69 dev_t dev; /* unix device number */
70 ino_t ino; /* unix inode number */
74 static const char * const DRIVE_Types[] =
76 "", /* DRIVE_UNKNOWN */
77 "", /* DRIVE_NO_ROOT_DIR */
78 "floppy", /* DRIVE_REMOVABLE */
79 "hd", /* DRIVE_FIXED */
80 "network", /* DRIVE_REMOTE */
81 "cdrom", /* DRIVE_CDROM */
82 "ramdisk" /* DRIVE_RAMDISK */
86 /* Known filesystem types */
94 static const FS_DESCR DRIVE_Filesystems[] =
96 { "unix", DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
97 { "msdos", DRIVE_SHORT_NAMES },
98 { "dos", DRIVE_SHORT_NAMES },
99 { "fat", DRIVE_SHORT_NAMES },
100 { "vfat", DRIVE_CASE_PRESERVING },
101 { "win95", DRIVE_CASE_PRESERVING },
106 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
107 static int DRIVE_CurDrive = -1;
109 static HTASK16 DRIVE_LastTask = 0;
112 /***********************************************************************
115 static UINT DRIVE_GetDriveType( const char *name )
120 PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
121 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
123 if (!strcasecmp( buffer, DRIVE_Types[i] )) return i;
125 MESSAGE("%s: unknown drive type '%s', defaulting to 'hd'.\n",
131 /***********************************************************************
134 static UINT DRIVE_GetFSFlags( const char *name, const char *value )
136 const FS_DESCR *descr;
138 for (descr = DRIVE_Filesystems; descr->name; descr++)
139 if (!strcasecmp( value, descr->name )) return descr->flags;
140 MESSAGE("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
142 return DRIVE_CASE_PRESERVING;
146 /***********************************************************************
151 int i, len, count = 0;
152 char name[] = "Drive A";
153 char drive_env[] = "=A:";
154 char path[MAX_PATHNAME_LEN];
156 struct stat drive_stat_buffer;
160 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
162 PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
165 p = path + strlen(path) - 1;
166 while ((p > path) && ((*p == '/') || (*p == '\\'))) *p-- = '\0';
167 if (!path[0]) strcpy( path, "/" );
169 if (stat( path, &drive_stat_buffer ))
171 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
172 path, strerror(errno), 'A' + i);
175 if (!S_ISDIR(drive_stat_buffer.st_mode))
177 MESSAGE("%s is not a directory, ignoring drive %c:\n",
182 drive->root = HEAP_strdupA( GetProcessHeap(), 0, path );
183 drive->dos_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
184 drive->unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
185 drive->type = DRIVE_GetDriveType( name );
186 drive->device = NULL;
188 drive->dev = drive_stat_buffer.st_dev;
189 drive->ino = drive_stat_buffer.st_ino;
191 /* Get the drive label */
192 PROFILE_GetWineIniString( name, "Label", "", drive->label_conf, 12 );
193 if ((len = strlen(drive->label_conf)) < 11)
195 /* Pad label with spaces */
196 memset( drive->label_conf + len, ' ', 11 - len );
197 drive->label_conf[11] = '\0';
200 /* Get the serial number */
201 PROFILE_GetWineIniString( name, "Serial", "12345678",
202 buffer, sizeof(buffer) );
203 drive->serial_conf = strtoul( buffer, NULL, 16 );
205 /* Get the filesystem type */
206 PROFILE_GetWineIniString( name, "Filesystem", "win95",
207 buffer, sizeof(buffer) );
208 drive->flags = DRIVE_GetFSFlags( name, buffer );
211 PROFILE_GetWineIniString( name, "Device", "",
212 buffer, sizeof(buffer) );
215 drive->device = HEAP_strdupA( GetProcessHeap(), 0, buffer );
216 if (PROFILE_GetWineIniBool( name, "ReadVolInfo", 1))
217 drive->flags |= DRIVE_READ_VOL_INFO;
220 /* Get the FailReadOnly flag */
221 if (PROFILE_GetWineIniBool( name, "FailReadOnly", 0 ))
222 drive->flags |= DRIVE_FAIL_READ_ONLY;
224 /* Make the first hard disk the current drive */
225 if ((DRIVE_CurDrive == -1) && (drive->type == DRIVE_FIXED))
229 TRACE("%s: path=%s type=%s label='%s' serial=%08lx "
230 "flags=%08x dev=%x ino=%x\n",
231 name, path, DRIVE_Types[drive->type],
232 drive->label_conf, drive->serial_conf, drive->flags,
233 (int)drive->dev, (int)drive->ino );
235 else WARN("%s: not defined\n", name );
240 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
241 /* Create a C drive pointing to Unix root dir */
242 DOSDrives[2].root = HEAP_strdupA( GetProcessHeap(), 0, "/" );
243 DOSDrives[2].dos_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
244 DOSDrives[2].unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
245 strcpy( DOSDrives[2].label_conf, "Drive C " );
246 DOSDrives[2].serial_conf = 12345678;
247 DOSDrives[2].type = DRIVE_FIXED;
248 DOSDrives[2].device = NULL;
249 DOSDrives[2].flags = 0;
253 /* Make sure the current drive is valid */
254 if (DRIVE_CurDrive == -1)
256 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
258 if (drive->root && !(drive->flags & DRIVE_DISABLED))
266 /* get current working directory info for all drives */
267 for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
269 if (!GetEnvironmentVariableA(drive_env, path, sizeof(path))) continue;
271 if (toupper(path[0]) != drive_env[1] || path[1] != ':') continue;
272 DRIVE_Chdir( i, path + 2 );
278 /***********************************************************************
281 int DRIVE_IsValid( int drive )
283 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
284 return (DOSDrives[drive].root &&
285 !(DOSDrives[drive].flags & DRIVE_DISABLED));
289 /***********************************************************************
290 * DRIVE_GetCurrentDrive
292 int DRIVE_GetCurrentDrive(void)
294 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
295 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
296 return DRIVE_CurDrive;
300 /***********************************************************************
301 * DRIVE_SetCurrentDrive
303 int DRIVE_SetCurrentDrive( int drive )
305 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
306 if (!DRIVE_IsValid( drive ))
308 SetLastError( ERROR_INVALID_DRIVE );
311 TRACE("%c:\n", 'A' + drive );
312 DRIVE_CurDrive = drive;
313 if (pTask) pTask->curdrive = drive | 0x80;
314 chdir(DRIVE_GetUnixCwd(drive));
319 /***********************************************************************
320 * DRIVE_FindDriveRoot
322 * Find a drive for which the root matches the beginning of the given path.
323 * This can be used to translate a Unix path into a drive + DOS path.
324 * Return value is the drive, or -1 on error. On success, path is modified
325 * to point to the beginning of the DOS path.
327 int DRIVE_FindDriveRoot( const char **path )
329 /* idea: check at all '/' positions.
330 * If the device and inode of that path is identical with the
331 * device and inode of the current drive then we found a solution.
332 * If there is another drive pointing to a deeper position in
333 * the file tree, we want to find that one, not the earlier solution.
335 int drive, rootdrive = -1;
336 char buffer[MAX_PATHNAME_LEN];
338 const char *p = *path;
341 strcpy( buffer, "/" );
344 if (stat( buffer, &st ) || !S_ISDIR( st.st_mode )) break;
348 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
350 if (!DOSDrives[drive].root ||
351 (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
353 if ((DOSDrives[drive].dev == st.st_dev) &&
354 (DOSDrives[drive].ino == st.st_ino))
362 /* Get the next path component */
365 while ((*p == '/') || (*p == '\\')) p++;
367 while (!IS_END_OF_NAME(*p)) *next++ = *p++;
373 TRACE("%s -> drive %c:, root='%s', name='%s'\n",
374 buffer, 'A' + rootdrive, DOSDrives[rootdrive].root, *path );
379 /***********************************************************************
382 const char * DRIVE_GetRoot( int drive )
384 if (!DRIVE_IsValid( drive )) return NULL;
385 return DOSDrives[drive].root;
389 /***********************************************************************
392 const char * DRIVE_GetDosCwd( int drive )
394 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
395 if (!DRIVE_IsValid( drive )) return NULL;
397 /* Check if we need to change the directory to the new task. */
398 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
399 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
400 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
402 /* Perform the task-switch */
403 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
404 DRIVE_LastTask = GetCurrentTask();
406 return DOSDrives[drive].dos_cwd;
410 /***********************************************************************
413 const char * DRIVE_GetUnixCwd( int drive )
415 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
416 if (!DRIVE_IsValid( drive )) return NULL;
418 /* Check if we need to change the directory to the new task. */
419 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
420 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
421 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
423 /* Perform the task-switch */
424 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
425 DRIVE_LastTask = GetCurrentTask();
427 return DOSDrives[drive].unix_cwd;
431 /***********************************************************************
434 const char * DRIVE_GetDevice( int drive )
436 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
440 /***********************************************************************
441 * DRIVE_ReadSuperblock
444 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
445 * to check, that they are writing on a FAT filesystem !
447 int DRIVE_ReadSuperblock (int drive, char * buff)
449 #define DRIVE_SUPER 96
453 if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
454 if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
457 if (!DOSDrives[drive].device)
458 ERR("No device configured for drive %c: !\n", 'A'+drive);
460 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
461 (stat(DOSDrives[drive].device, &st)) ?
462 "not available or symlink not valid ?" : "no permission");
463 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
464 PROFILE_UsageWineIni();
468 switch(DOSDrives[drive].type)
470 case DRIVE_REMOVABLE:
475 offs = CDROM_Data_FindBestVoldesc(fd);
482 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs)) return -4;
483 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER) return -2;
485 switch(DOSDrives[drive].type)
487 case DRIVE_REMOVABLE:
489 if ((buff[0x26]!=0x29) || /* Check for FAT present */
490 /* FIXME: do really all FAT have their name beginning with
491 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
492 memcmp( buff+0x36,"FAT",3))
494 ERR("The filesystem is not FAT !! (device=%s)\n",
495 DOSDrives[drive].device);
500 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
502 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
513 /***********************************************************************
514 * DRIVE_WriteSuperblockEntry
517 * We are writing as little as possible (ie. not the whole SuperBlock)
518 * not to interfere with kernel. The drive can be mounted !
520 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
524 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
526 ERR("Cannot open the device %s (for writing)\n",
527 DOSDrives[drive].device);
530 if (lseek(fd,ofs,SEEK_SET)!=ofs)
532 ERR("lseek failed on device %s !\n",
533 DOSDrives[drive].device);
537 if (write(fd,buff,len)!=len)
540 ERR("Cannot write on %s !\n",
541 DOSDrives[drive].device);
549 /***********************************************************************
552 const char * DRIVE_GetLabel( int drive )
555 char buff[DRIVE_SUPER];
558 if (!DRIVE_IsValid( drive )) return NULL;
559 if (DOSDrives[drive].type == DRIVE_CDROM)
561 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
564 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
566 if (DRIVE_ReadSuperblock(drive,(char *) buff))
567 ERR("Invalid or unreadable superblock on %s (%c:).\n",
568 DOSDrives[drive].device, (char)(drive+'A'));
570 if (DOSDrives[drive].type == DRIVE_REMOVABLE ||
571 DOSDrives[drive].type == DRIVE_FIXED)
574 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
575 if (offs != -1) memcpy(DOSDrives[drive].label_read,buff+offs,11);
576 DOSDrives[drive].label_read[11]='\0';
582 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
586 /***********************************************************************
587 * DRIVE_GetSerialNumber
589 DWORD DRIVE_GetSerialNumber( int drive )
592 char buff[DRIVE_SUPER];
594 if (!DRIVE_IsValid( drive )) return 0;
596 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
598 switch(DOSDrives[drive].type)
600 case DRIVE_REMOVABLE:
602 if (DRIVE_ReadSuperblock(drive,(char *) buff))
603 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
604 " Maybe not FAT?\n" ,
605 DOSDrives[drive].device, 'A'+drive);
607 serial = *((DWORD*)(buff+0x27));
610 serial = CDROM_GetSerial(drive);
613 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
617 return (serial) ? serial : DOSDrives[drive].serial_conf;
621 /***********************************************************************
622 * DRIVE_SetSerialNumber
624 int DRIVE_SetSerialNumber( int drive, DWORD serial )
626 char buff[DRIVE_SUPER];
628 if (!DRIVE_IsValid( drive )) return 0;
630 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
632 if ((DOSDrives[drive].type != DRIVE_REMOVABLE) &&
633 (DOSDrives[drive].type != DRIVE_FIXED)) return 0;
634 /* check, if the drive has a FAT filesystem */
635 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
636 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
640 if (DOSDrives[drive].type == DRIVE_CDROM) return 0;
641 DOSDrives[drive].serial_conf = serial;
646 /***********************************************************************
649 static UINT DRIVE_GetType( int drive )
651 if (!DRIVE_IsValid( drive )) return DRIVE_UNKNOWN;
652 return DOSDrives[drive].type;
656 /***********************************************************************
659 UINT DRIVE_GetFlags( int drive )
661 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
662 return DOSDrives[drive].flags;
666 /***********************************************************************
669 int DRIVE_Chdir( int drive, const char *path )
671 DOS_FULL_NAME full_name;
672 char buffer[MAX_PATHNAME_LEN];
674 BY_HANDLE_FILE_INFORMATION info;
675 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
677 strcpy( buffer, "A:" );
679 TRACE("(%s,%s)\n", buffer, path );
680 lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
682 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
683 if (!FILE_Stat( full_name.long_name, &info )) return 0;
684 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
686 SetLastError( ERROR_FILE_NOT_FOUND );
689 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
690 while (*unix_cwd == '/') unix_cwd++;
692 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
693 'A' + drive, unix_cwd, full_name.short_name + 3 );
695 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
696 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
697 DOSDrives[drive].dos_cwd = HEAP_strdupA( GetProcessHeap(), 0,
698 full_name.short_name + 3 );
699 DOSDrives[drive].unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, unix_cwd );
701 if (pTask && (pTask->curdrive & 0x80) &&
702 ((pTask->curdrive & ~0x80) == drive))
704 lstrcpynA( pTask->curdir, full_name.short_name + 2,
705 sizeof(pTask->curdir) );
706 DRIVE_LastTask = GetCurrentTask();
707 chdir(unix_cwd); /* Only change if on current drive */
713 /***********************************************************************
716 int DRIVE_Disable( int drive )
718 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
720 SetLastError( ERROR_INVALID_DRIVE );
723 DOSDrives[drive].flags |= DRIVE_DISABLED;
728 /***********************************************************************
731 int DRIVE_Enable( int drive )
733 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
735 SetLastError( ERROR_INVALID_DRIVE );
738 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
743 /***********************************************************************
744 * DRIVE_SetLogicalMapping
746 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
748 /* If new_drive is already valid, do nothing and return 0
749 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
753 old = DOSDrives + existing_drive;
754 new = DOSDrives + new_drive;
756 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
758 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
760 SetLastError( ERROR_INVALID_DRIVE );
766 TRACE("Can't map drive %c: to already existing drive %c:\n",
767 'A' + existing_drive, 'A' + new_drive );
768 /* it is already mapped there, so return success */
769 if (!strcmp(old->root,new->root))
774 new->root = HEAP_strdupA( GetProcessHeap(), 0, old->root );
775 new->dos_cwd = HEAP_strdupA( GetProcessHeap(), 0, old->dos_cwd );
776 new->unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, old->unix_cwd );
777 new->device = HEAP_strdupA( GetProcessHeap(), 0, old->device );
778 memcpy ( new->label_conf, old->label_conf, 12 );
779 memcpy ( new->label_read, old->label_read, 12 );
780 new->serial_conf = old->serial_conf;
781 new->type = old->type;
782 new->flags = old->flags;
786 TRACE("Drive %c: is now equal to drive %c:\n",
787 'A' + new_drive, 'A' + existing_drive );
793 /***********************************************************************
796 * Open the drive raw device and return a Unix fd (or -1 on error).
798 int DRIVE_OpenDevice( int drive, int flags )
800 if (!DRIVE_IsValid( drive )) return -1;
801 return open( DOSDrives[drive].device, flags );
805 /***********************************************************************
808 * Read raw sectors from a device
810 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
814 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
816 lseek( fd, begin * 512, SEEK_SET );
817 /* FIXME: check errors */
818 read( fd, dataptr, nr_sect * 512 );
823 memset(dataptr, 0, nr_sect * 512);
826 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
827 if (begin == 1) *dataptr = 0xf8;
836 /***********************************************************************
839 * Write raw sectors to a device
841 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
845 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
847 lseek( fd, begin * 512, SEEK_SET );
848 /* FIXME: check errors */
849 write( fd, dataptr, nr_sect * 512 );
860 /***********************************************************************
863 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
864 PULARGE_INTEGER available )
868 if (!DRIVE_IsValid(drive))
870 SetLastError( ERROR_INVALID_DRIVE );
874 /* FIXME: add autoconf check for this */
875 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
876 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
878 if (statfs( DOSDrives[drive].root, &info) < 0)
882 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
886 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bsize, info.f_blocks );
887 #ifdef STATFS_HAS_BAVAIL
888 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bavail, info.f_bsize );
890 # ifdef STATFS_HAS_BFREE
891 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bfree, info.f_bsize );
893 # error "statfs has no bfree/bavail member!"
896 if (DOSDrives[drive].type == DRIVE_CDROM)
897 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
898 available->QuadPart = 0;
903 /***********************************************************************
904 * DRIVE_GetCurrentDirectory
905 * Returns "X:\\path\\etc\\".
907 * Despite the API description, return required length including the
908 * terminating null when buffer too small. This is the real behaviour.
911 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPSTR buf )
914 const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
917 ret = strlen(s) + 3; /* length of WHOLE current directory */
918 if (ret >= buflen) return ret + 1;
919 lstrcpynA( buf, "A:\\", min( 4, buflen ) );
920 if (buflen) buf[0] += DRIVE_GetCurrentDrive();
921 if (buflen > 3) lstrcpynA( buf + 3, s, buflen - 3 );
926 /***********************************************************************
929 * Build the environment array containing the drives current directories.
930 * Resulting pointer must be freed with HeapFree.
932 char *DRIVE_BuildEnv(void)
935 const char *cwd[MAX_DOS_DRIVES];
938 for (i = 0; i < MAX_DOS_DRIVES; i++)
940 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0]) length += strlen(cwd[i]) + 8;
942 if (!(env = HeapAlloc( GetProcessHeap(), 0, length+1 ))) return NULL;
943 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
945 if (cwd[i] && cwd[i][0])
946 p += sprintf( p, "=%c:=%c:\\%s", 'A'+i, 'A'+i, cwd[i] ) + 1;
953 /***********************************************************************
954 * GetDiskFreeSpace16 (KERNEL.422)
956 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
957 LPDWORD sector_bytes, LPDWORD free_clusters,
958 LPDWORD total_clusters )
960 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
961 free_clusters, total_clusters );
965 /***********************************************************************
966 * GetDiskFreeSpaceA (KERNEL32.206)
968 * Fails if expression resulting from current drive's dir and "root"
969 * is not a root dir of the target drive.
971 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
972 * if the corresponding info is unneeded.
974 * FIXME: needs to support UNC names from Win95 OSR2 on.
976 * Behaviour under Win95a:
977 * CurrDir root result
978 * "E:\\TEST" "E:" FALSE
982 * "E:\\TEST" "\\" TRUE
983 * "E:\\TEST" ":\\" FALSE
984 * "E:\\TEST" "E:\\" TRUE
985 * "E:\\TEST" "" FALSE
986 * "E:\\" "" FALSE (!)
988 * "E:\\TEST" 0x0 TRUE (!)
989 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
990 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
992 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
993 LPDWORD sector_bytes, LPDWORD free_clusters,
994 LPDWORD total_clusters )
997 ULARGE_INTEGER size,available;
1001 if ((!root) || (strcmp(root,"\\") == 0))
1002 drive = DRIVE_GetCurrentDrive();
1004 if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
1006 drive = toupper(root[0]) - 'A';
1008 if (path[0] == '\0')
1009 path = DRIVE_GetDosCwd(drive);
1011 if (path[0] == '\\')
1013 if (strlen(path)) /* oops, we are in a subdir */
1019 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1021 /* Cap the size and available at 2GB as per specs. */
1022 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1024 size.s.HighPart = 0;
1025 size.s.LowPart = 0x7fffffff;
1027 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1029 available.s.HighPart =0;
1030 available.s.LowPart = 0x7fffffff;
1032 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
1033 size.s.LowPart /= sec_size;
1034 available.s.LowPart /= sec_size;
1035 /* fixme: probably have to adjust those variables too for CDFS */
1037 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1039 if (cluster_sectors)
1040 *cluster_sectors = cluster_sec;
1042 *sector_bytes = sec_size;
1044 *free_clusters = available.s.LowPart / cluster_sec;
1046 *total_clusters = size.s.LowPart / cluster_sec;
1051 /***********************************************************************
1052 * GetDiskFreeSpaceW (KERNEL32.207)
1054 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1055 LPDWORD sector_bytes, LPDWORD free_clusters,
1056 LPDWORD total_clusters )
1061 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1062 ret = GetDiskFreeSpaceA( xroot,cluster_sectors, sector_bytes,
1063 free_clusters, total_clusters );
1064 HeapFree( GetProcessHeap(), 0, xroot );
1069 /***********************************************************************
1070 * GetDiskFreeSpaceExA (KERNEL32.871)
1072 * This function is used to aquire the size of the available and
1073 * total space on a logical volume.
1077 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1078 * detailed error information.
1081 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
1082 PULARGE_INTEGER avail,
1083 PULARGE_INTEGER total,
1084 PULARGE_INTEGER totalfree)
1087 ULARGE_INTEGER size,available;
1089 if (!root) drive = DRIVE_GetCurrentDrive();
1092 if ((root[1]) && ((root[1] != ':') || (root[2] != '\\')))
1094 FIXME("there are valid root names which are not supported yet\n");
1095 /* ..like UNC names, for instance. */
1097 WARN("invalid root '%s'\n", root );
1100 drive = toupper(root[0]) - 'A';
1103 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1107 total->s.HighPart = size.s.HighPart;
1108 total->s.LowPart = size.s.LowPart;
1113 totalfree->s.HighPart = available.s.HighPart;
1114 totalfree->s.LowPart = available.s.LowPart;
1119 if (FIXME_ON(dosfs))
1121 /* On Windows2000, we need to check the disk quota
1122 allocated for the user owning the calling process. We
1123 don't want to be more obtrusive than necessary with the
1124 FIXME messages, so don't print the FIXME unless Wine is
1125 actually masquerading as Windows2000. */
1128 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1129 if (GetVersionExA(&ovi))
1131 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1132 FIXME("no per-user quota support yet\n");
1136 /* Quick hack, should eventually be fixed to work 100% with
1137 Windows2000 (see comment above). */
1138 avail->s.HighPart = available.s.HighPart;
1139 avail->s.LowPart = available.s.LowPart;
1145 /***********************************************************************
1146 * GetDiskFreeSpaceExW (KERNEL32.873)
1148 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1149 PULARGE_INTEGER total,
1150 PULARGE_INTEGER totalfree)
1155 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1156 ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
1157 HeapFree( GetProcessHeap(), 0, xroot );
1161 /***********************************************************************
1162 * GetDriveType16 (KERNEL.136)
1163 * This function returns the type of a drive in Win16.
1164 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1165 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1166 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1167 * do any pseudo-clever changes.
1170 * drivetype DRIVE_xxx
1172 UINT16 WINAPI GetDriveType16( UINT16 drive ) /* [in] number (NOT letter) of drive */
1174 UINT type = DRIVE_GetType(drive);
1175 TRACE("(%c:)\n", 'A' + drive );
1176 if (type == DRIVE_CDROM) type = DRIVE_REMOTE;
1181 /***********************************************************************
1182 * GetDriveTypeA (KERNEL32.208)
1184 * Returns the type of the disk drive specified. If root is NULL the
1185 * root of the current directory is used.
1189 * Type of drive (from Win32 SDK):
1191 * DRIVE_UNKNOWN unable to find out anything about the drive
1192 * DRIVE_NO_ROOT_DIR nonexistent root dir
1193 * DRIVE_REMOVABLE the disk can be removed from the machine
1194 * DRIVE_FIXED the disk can not be removed from the machine
1195 * DRIVE_REMOTE network disk
1196 * DRIVE_CDROM CDROM drive
1197 * DRIVE_RAMDISK virtual disk in RAM
1199 UINT WINAPI GetDriveTypeA(LPCSTR root) /* [in] String describing drive */
1202 TRACE("(%s)\n", debugstr_a(root));
1204 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1207 if ((root[1]) && (root[1] != ':'))
1209 WARN("invalid root '%s'\n", debugstr_a(root));
1210 return DRIVE_NO_ROOT_DIR;
1212 drive = toupper(root[0]) - 'A';
1214 return DRIVE_GetType(drive);
1218 /***********************************************************************
1219 * GetDriveTypeW (KERNEL32.209)
1221 UINT WINAPI GetDriveTypeW( LPCWSTR root )
1223 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1224 UINT ret = GetDriveTypeA( xpath );
1225 HeapFree( GetProcessHeap(), 0, xpath );
1230 /***********************************************************************
1231 * GetCurrentDirectory16 (KERNEL.411)
1233 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1235 return (UINT16)DRIVE_GetCurrentDirectory(buflen, buf);
1239 /***********************************************************************
1240 * GetCurrentDirectoryA (KERNEL32.196)
1242 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1245 char longname[MAX_PATHNAME_LEN];
1246 char shortname[MAX_PATHNAME_LEN];
1247 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1248 if ( ret > MAX_PATHNAME_LEN ) {
1249 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1252 GetLongPathNameA(shortname, longname, MAX_PATHNAME_LEN);
1253 ret = strlen( longname ) + 1;
1254 if (ret > buflen) return ret;
1255 strcpy(buf, longname);
1259 /***********************************************************************
1260 * GetCurrentDirectoryW (KERNEL32.197)
1262 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1264 LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
1265 UINT ret = GetCurrentDirectoryA( buflen, xpath );
1266 if (ret < buflen) ret = MultiByteToWideChar( CP_ACP, 0, xpath, -1, buf, buflen ) - 1;
1267 HeapFree( GetProcessHeap(), 0, xpath );
1272 /***********************************************************************
1273 * SetCurrentDirectory (KERNEL.412)
1275 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1277 return SetCurrentDirectoryA( dir );
1281 /***********************************************************************
1282 * SetCurrentDirectoryA (KERNEL32.479)
1284 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1286 int drive, olddrive = DRIVE_GetCurrentDrive();
1289 ERR_(file)("(NULL)!\n");
1292 if (dir[0] && (dir[1]==':'))
1294 drive = toupper( *dir ) - 'A';
1300 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1301 sets pTask->curdir only if pTask->curdrive is drive */
1302 if (!(DRIVE_SetCurrentDrive( drive )))
1304 /* FIXME: what about empty strings? Add a \\ ? */
1305 if (!DRIVE_Chdir( drive, dir )) {
1306 DRIVE_SetCurrentDrive(olddrive);
1313 /***********************************************************************
1314 * SetCurrentDirectoryW (KERNEL32.480)
1316 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1318 LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1319 BOOL res = SetCurrentDirectoryA( dir );
1320 HeapFree( GetProcessHeap(), 0, dir );
1325 /***********************************************************************
1326 * GetLogicalDriveStringsA (KERNEL32.231)
1328 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1332 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1333 if (DRIVE_IsValid(drive)) count++;
1334 if ((count * 4) + 1 <= len)
1337 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1338 if (DRIVE_IsValid(drive))
1349 return (count * 4) + 1; /* account for terminating null */
1350 /* The API tells about these different return values */
1354 /***********************************************************************
1355 * GetLogicalDriveStringsW (KERNEL32.232)
1357 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1361 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1362 if (DRIVE_IsValid(drive)) count++;
1363 if (count * 4 * sizeof(WCHAR) <= len)
1366 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1367 if (DRIVE_IsValid(drive))
1369 *p++ = (WCHAR)('a' + drive);
1376 return count * 4 * sizeof(WCHAR);
1380 /***********************************************************************
1381 * GetLogicalDrives (KERNEL32.233)
1383 DWORD WINAPI GetLogicalDrives(void)
1388 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1390 if ( (DRIVE_IsValid(drive)) ||
1391 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1392 ret |= (1 << drive);
1398 /***********************************************************************
1399 * GetVolumeInformationA (KERNEL32.309)
1401 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1402 DWORD label_len, DWORD *serial,
1403 DWORD *filename_len, DWORD *flags,
1404 LPSTR fsname, DWORD fsname_len )
1409 /* FIXME, SetLastError()s missing */
1411 if (!root) drive = DRIVE_GetCurrentDrive();
1414 if ((root[1]) && (root[1] != ':'))
1416 WARN("invalid root '%s'\n",root);
1419 drive = toupper(root[0]) - 'A';
1421 if (!DRIVE_IsValid( drive )) return FALSE;
1424 lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1425 for (cp = label; *cp; cp++);
1426 while (cp != label && *(cp-1) == ' ') cp--;
1429 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1431 /* Set the filesystem information */
1432 /* Note: we only emulate a FAT fs at present */
1435 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1438 *filename_len = 255;
1443 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1444 *flags|=FS_CASE_SENSITIVE;
1445 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1446 *flags|=FS_CASE_IS_PRESERVED;
1449 /* Diablo checks that return code ... */
1450 if (DOSDrives[drive].type == DRIVE_CDROM)
1451 lstrcpynA( fsname, "CDFS", fsname_len );
1453 lstrcpynA( fsname, "FAT", fsname_len );
1459 /***********************************************************************
1460 * GetVolumeInformationW (KERNEL32.310)
1462 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1463 DWORD label_len, DWORD *serial,
1464 DWORD *filename_len, DWORD *flags,
1465 LPWSTR fsname, DWORD fsname_len )
1467 LPSTR xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1468 LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1469 LPSTR xfsname = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1470 BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1471 filename_len, flags, xfsname,
1475 if (label) MultiByteToWideChar( CP_ACP, 0, xvolname, -1, label, label_len );
1476 if (fsname) MultiByteToWideChar( CP_ACP, 0, xfsname, -1, fsname, fsname_len );
1478 HeapFree( GetProcessHeap(), 0, xroot );
1479 HeapFree( GetProcessHeap(), 0, xvolname );
1480 HeapFree( GetProcessHeap(), 0, xfsname );
1484 /***********************************************************************
1485 * SetVolumeLabelA (KERNEL32.675)
1487 BOOL WINAPI SetVolumeLabelA( LPCSTR root, LPCSTR volname )
1491 /* FIXME, SetLastErrors missing */
1493 if (!root) drive = DRIVE_GetCurrentDrive();
1496 if ((root[1]) && (root[1] != ':'))
1498 WARN("invalid root '%s'\n",root);
1501 drive = toupper(root[0]) - 'A';
1503 if (!DRIVE_IsValid( drive )) return FALSE;
1505 /* some copy protection stuff check this */
1506 if (DOSDrives[drive].type == DRIVE_CDROM) return FALSE;
1508 FIXME("(%s,%s),stub!\n", root, volname);
1512 /***********************************************************************
1513 * SetVolumeLabelW (KERNEL32.676)
1515 BOOL WINAPI SetVolumeLabelW(LPCWSTR rootpath,LPCWSTR volname)
1520 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath);
1521 xvol = HEAP_strdupWtoA( GetProcessHeap(), 0, volname);
1522 ret = SetVolumeLabelA( xroot, xvol );
1523 HeapFree( GetProcessHeap(), 0, xroot );
1524 HeapFree( GetProcessHeap(), 0, xvol );