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)
19 #include <sys/types.h>
25 #ifdef HAVE_SYS_PARAM_H
26 # include <sys/param.h>
28 #ifdef STATFS_DEFINED_BY_SYS_VFS
31 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
32 # include <sys/mount.h>
34 # ifdef STATFS_DEFINED_BY_SYS_STATFS
35 # include <sys/statfs.h>
41 #include "wine/winbase16.h" /* for GetCurrentTask */
42 #include "wine/winestring.h" /* for lstrcpyAtoW */
50 #include "wine/port.h"
52 #include "debugtools.h"
54 DEFAULT_DEBUG_CHANNEL(dosfs)
55 DECLARE_DEBUG_CHANNEL(file)
59 char *root; /* root dir in Unix format without trailing / */
60 char *dos_cwd; /* cwd in DOS format without leading or trailing \ */
61 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
62 char *device; /* raw device path */
63 BOOL read_volinfo; /* read the volume info from the device ? */
64 char label_conf[12]; /* drive label as cfg'd in wine.conf */
65 char label_read[12]; /* drive label as read from device */
66 DWORD serial_conf; /* drive serial number as cfg'd in wine.conf */
67 DRIVETYPE 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 "floppy", /* TYPE_FLOPPY */
78 "cdrom", /* TYPE_CDROM */
79 "network" /* TYPE_NETWORK */
83 /* Known filesystem types */
91 static const FS_DESCR DRIVE_Filesystems[] =
93 { "unix", DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
94 { "msdos", DRIVE_SHORT_NAMES },
95 { "dos", DRIVE_SHORT_NAMES },
96 { "fat", DRIVE_SHORT_NAMES },
97 { "vfat", DRIVE_CASE_PRESERVING },
98 { "win95", DRIVE_CASE_PRESERVING },
103 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
104 static int DRIVE_CurDrive = -1;
106 static HTASK16 DRIVE_LastTask = 0;
109 /***********************************************************************
112 static DRIVETYPE DRIVE_GetDriveType( const char *name )
117 PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
118 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
120 if (!strcasecmp( buffer, DRIVE_Types[i] )) return (DRIVETYPE)i;
122 MESSAGE("%s: unknown drive type '%s', defaulting to 'hd'.\n",
128 /***********************************************************************
131 static UINT DRIVE_GetFSFlags( const char *name, const char *value )
133 const FS_DESCR *descr;
135 for (descr = DRIVE_Filesystems; descr->name; descr++)
136 if (!strcasecmp( value, descr->name )) return descr->flags;
137 MESSAGE("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
139 return DRIVE_CASE_PRESERVING;
143 /***********************************************************************
148 int i, len, count = 0;
149 char name[] = "Drive A";
150 char path[MAX_PATHNAME_LEN];
152 struct stat drive_stat_buffer;
156 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
158 PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
161 p = path + strlen(path) - 1;
162 while ((p > path) && ((*p == '/') || (*p == '\\'))) *p-- = '\0';
163 if (!path[0]) strcpy( path, "/" );
165 if (stat( path, &drive_stat_buffer ))
167 MESSAGE("Could not stat %s, ignoring drive %c:\n", path, 'A' + i );
170 if (!S_ISDIR(drive_stat_buffer.st_mode))
172 MESSAGE("%s is not a directory, ignoring drive %c:\n",
177 drive->root = HEAP_strdupA( GetProcessHeap(), 0, path );
178 drive->dos_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
179 drive->unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
180 drive->type = DRIVE_GetDriveType( name );
181 drive->device = NULL;
183 drive->dev = drive_stat_buffer.st_dev;
184 drive->ino = drive_stat_buffer.st_ino;
186 /* Get the drive label */
187 PROFILE_GetWineIniString( name, "Label", name, drive->label_conf, 12 );
188 if ((len = strlen(drive->label_conf)) < 11)
190 /* Pad label with spaces */
191 memset( drive->label_conf + len, ' ', 11 - len );
192 drive->label_conf[11] = '\0';
195 /* Get the serial number */
196 PROFILE_GetWineIniString( name, "Serial", "12345678",
197 buffer, sizeof(buffer) );
198 drive->serial_conf = strtoul( buffer, NULL, 16 );
200 /* Get the filesystem type */
201 PROFILE_GetWineIniString( name, "Filesystem", "win95",
202 buffer, sizeof(buffer) );
203 drive->flags = DRIVE_GetFSFlags( name, buffer );
206 PROFILE_GetWineIniString( name, "Device", "",
207 buffer, sizeof(buffer) );
210 drive->device = HEAP_strdupA( GetProcessHeap(), 0, buffer );
211 drive->read_volinfo =
212 (BOOL)PROFILE_GetWineIniInt( name, "ReadVolInfo", 1);
215 drive->read_volinfo = FALSE;
217 /* Make the first hard disk the current drive */
218 if ((DRIVE_CurDrive == -1) && (drive->type == TYPE_HD))
222 TRACE("%s: path=%s type=%s label='%s' serial=%08lx "
223 "flags=%08x dev=%x ino=%x\n",
224 name, path, DRIVE_Types[drive->type],
225 drive->label_conf, drive->serial_conf, drive->flags,
226 (int)drive->dev, (int)drive->ino );
228 else WARN("%s: not defined\n", name );
233 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
234 /* Create a C drive pointing to Unix root dir */
235 DOSDrives[2].root = HEAP_strdupA( GetProcessHeap(), 0, "/" );
236 DOSDrives[2].dos_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
237 DOSDrives[2].unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, "" );
238 strcpy( DOSDrives[2].label_conf, "Drive C " );
239 DOSDrives[2].serial_conf = 12345678;
240 DOSDrives[2].type = TYPE_HD;
241 DOSDrives[2].flags = 0;
245 /* Make sure the current drive is valid */
246 if (DRIVE_CurDrive == -1)
248 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
250 if (drive->root && !(drive->flags & DRIVE_DISABLED))
262 /***********************************************************************
265 int DRIVE_IsValid( int drive )
267 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
268 return (DOSDrives[drive].root &&
269 !(DOSDrives[drive].flags & DRIVE_DISABLED));
273 /***********************************************************************
274 * DRIVE_GetCurrentDrive
276 int DRIVE_GetCurrentDrive(void)
278 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
279 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
280 return DRIVE_CurDrive;
284 /***********************************************************************
285 * DRIVE_SetCurrentDrive
287 int DRIVE_SetCurrentDrive( int drive )
289 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
290 if (!DRIVE_IsValid( drive ))
292 SetLastError( ERROR_INVALID_DRIVE );
295 TRACE("%c:\n", 'A' + drive );
296 DRIVE_CurDrive = drive;
297 if (pTask) pTask->curdrive = drive | 0x80;
302 /***********************************************************************
303 * DRIVE_FindDriveRoot
305 * Find a drive for which the root matches the beginning of the given path.
306 * This can be used to translate a Unix path into a drive + DOS path.
307 * Return value is the drive, or -1 on error. On success, path is modified
308 * to point to the beginning of the DOS path.
310 int DRIVE_FindDriveRoot( const char **path )
312 /* idea: check at all '/' positions.
313 * If the device and inode of that path is identical with the
314 * device and inode of the current drive then we found a solution.
315 * If there is another drive pointing to a deeper position in
316 * the file tree, we want to find that one, not the earlier solution.
318 int drive, rootdrive = -1;
319 char buffer[MAX_PATHNAME_LEN];
321 const char *p = *path;
324 strcpy( buffer, "/" );
327 if (stat( buffer, &st ) || !S_ISDIR( st.st_mode )) break;
331 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
333 if (!DOSDrives[drive].root ||
334 (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
336 if ((DOSDrives[drive].dev == st.st_dev) &&
337 (DOSDrives[drive].ino == st.st_ino))
345 /* Get the next path component */
348 while ((*p == '/') || (*p == '\\')) p++;
350 while (!IS_END_OF_NAME(*p)) *next++ = *p++;
356 TRACE("%s -> drive %c:, root='%s', name='%s'\n",
357 buffer, 'A' + rootdrive, DOSDrives[rootdrive].root, *path );
362 /***********************************************************************
365 const char * DRIVE_GetRoot( int drive )
367 if (!DRIVE_IsValid( drive )) return NULL;
368 return DOSDrives[drive].root;
372 /***********************************************************************
375 const char * DRIVE_GetDosCwd( int drive )
377 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
378 if (!DRIVE_IsValid( drive )) return NULL;
380 /* Check if we need to change the directory to the new task. */
381 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
382 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
383 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
385 /* Perform the task-switch */
386 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
387 DRIVE_LastTask = GetCurrentTask();
389 return DOSDrives[drive].dos_cwd;
393 /***********************************************************************
396 const char * DRIVE_GetUnixCwd( int drive )
398 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
399 if (!DRIVE_IsValid( drive )) return NULL;
401 /* Check if we need to change the directory to the new task. */
402 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
403 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
404 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
406 /* Perform the task-switch */
407 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
408 DRIVE_LastTask = GetCurrentTask();
410 return DOSDrives[drive].unix_cwd;
414 /***********************************************************************
417 const char * DRIVE_GetDevice( int drive )
419 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
423 /***********************************************************************
424 * DRIVE_ReadSuperblock
427 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
428 * to check, that they are writing on a FAT filesystem !
430 int DRIVE_ReadSuperblock (int drive, char * buff)
432 #define DRIVE_SUPER 96
436 if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
437 if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
440 if (!DOSDrives[drive].device)
441 ERR("No device configured for drive %c: !\n", 'A'+drive);
443 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
444 (stat(DOSDrives[drive].device, &st)) ?
445 "not available or symlink not valid ?" : "no permission");
446 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
447 PROFILE_UsageWineIni();
451 switch(DOSDrives[drive].type)
458 /* FIXME: Maybe we should search for the first data track on the CD,
459 not just assume that it is the first track */
460 offs = (off_t)2048*(16+0);
467 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs)) return -4;
468 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER) return -2;
470 switch(DOSDrives[drive].type)
474 if ((buff[0x26]!=0x29) || /* Check for FAT present */
475 /* FIXME: do really all Fat have their name beginning with
476 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
477 memcmp( buff+0x36,"FAT",3))
479 ERR("The filesystem is not FAT !! (device=%s)\n",
480 DOSDrives[drive].device);
485 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
487 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
498 /***********************************************************************
499 * DRIVE_WriteSuperblockEntry
502 * We are writing as little as possible (ie. not the whole SuperBlock)
503 * not to interfere with kernel. The drive can be mounted !
505 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
509 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
511 ERR("Cannot open the device %s (for writing)\n",
512 DOSDrives[drive].device);
515 if (lseek(fd,ofs,SEEK_SET)!=ofs)
517 ERR("lseek failed on device %s !\n",
518 DOSDrives[drive].device);
522 if (write(fd,buff,len)!=len)
525 ERR("Cannot write on %s !\n",
526 DOSDrives[drive].device);
534 /***********************************************************************
537 const char * DRIVE_GetLabel( int drive )
540 char buff[DRIVE_SUPER];
543 if (!DRIVE_IsValid( drive )) return NULL;
544 if (DRIVE_GetType(drive) == TYPE_CDROM)
548 if (!(CDROM_Open(&wcda, drive)))
550 int media = CDROM_GetMediaType(&wcda);
552 if (media == CDS_AUDIO)
554 strcpy(DOSDrives[drive].label_read, "Audio CD ");
558 if (media == CDS_NO_INFO)
560 strcpy(DOSDrives[drive].label_read, " ");
567 if ((!read) && (DOSDrives[drive].read_volinfo))
569 if (DRIVE_ReadSuperblock(drive,(char *) buff))
570 ERR("Invalid or unreadable superblock on %s (%c:).\n",
571 DOSDrives[drive].device, (char)(drive+'A'));
573 if (DOSDrives[drive].type == TYPE_CDROM)
576 if (DOSDrives[drive].type == TYPE_FLOPPY ||
577 DOSDrives[drive].type == TYPE_HD)
580 /* FIXME: ISO9660 uses 32-bytes long label. Should we do also? */
581 if (offs != -1) memcpy(DOSDrives[drive].label_read,buff+offs,11);
582 DOSDrives[drive].label_read[11]='\0';
588 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
592 /***********************************************************************
593 * DRIVE_GetSerialNumber
595 DWORD DRIVE_GetSerialNumber( int drive )
598 char buff[DRIVE_SUPER];
600 if (!DRIVE_IsValid( drive )) return 0;
602 if (DOSDrives[drive].read_volinfo)
604 switch(DOSDrives[drive].type)
608 if (DRIVE_ReadSuperblock(drive,(char *) buff))
609 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
610 " Maybe not FAT?\n" ,
611 DOSDrives[drive].device, 'A'+drive);
613 serial = *((DWORD*)(buff+0x27));
616 serial = CDROM_GetSerial(drive);
619 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
623 return (serial) ? serial : DOSDrives[drive].serial_conf;
627 /***********************************************************************
628 * DRIVE_SetSerialNumber
630 int DRIVE_SetSerialNumber( int drive, DWORD serial )
632 char buff[DRIVE_SUPER];
634 if (!DRIVE_IsValid( drive )) return 0;
636 if (DOSDrives[drive].read_volinfo)
638 if ((DOSDrives[drive].type != TYPE_FLOPPY) &&
639 (DOSDrives[drive].type != TYPE_HD)) return 0;
640 /* check, if the drive has a FAT filesystem */
641 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
642 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
646 if (DOSDrives[drive].type == TYPE_CDROM) return 0;
647 DOSDrives[drive].serial_conf = serial;
652 /***********************************************************************
655 DRIVETYPE DRIVE_GetType( int drive )
657 if (!DRIVE_IsValid( drive )) return TYPE_INVALID;
658 return DOSDrives[drive].type;
662 /***********************************************************************
665 UINT DRIVE_GetFlags( int drive )
667 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
668 return DOSDrives[drive].flags;
672 /***********************************************************************
675 int DRIVE_Chdir( int drive, const char *path )
677 DOS_FULL_NAME full_name;
678 char buffer[MAX_PATHNAME_LEN];
680 BY_HANDLE_FILE_INFORMATION info;
681 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
683 strcpy( buffer, "A:" );
685 TRACE("(%c:,%s)\n", buffer[0], path );
686 lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
688 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
689 if (!FILE_Stat( full_name.long_name, &info )) return 0;
690 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
692 SetLastError( ERROR_FILE_NOT_FOUND );
695 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
696 while (*unix_cwd == '/') unix_cwd++;
698 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
699 'A' + drive, unix_cwd, full_name.short_name + 3 );
701 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
702 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
703 DOSDrives[drive].dos_cwd = HEAP_strdupA( GetProcessHeap(), 0,
704 full_name.short_name + 3 );
705 DOSDrives[drive].unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, unix_cwd );
707 if (pTask && (pTask->curdrive & 0x80) &&
708 ((pTask->curdrive & ~0x80) == drive))
710 lstrcpynA( pTask->curdir, full_name.short_name + 2,
711 sizeof(pTask->curdir) );
712 DRIVE_LastTask = GetCurrentTask();
718 /***********************************************************************
721 int DRIVE_Disable( int drive )
723 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
725 SetLastError( ERROR_INVALID_DRIVE );
728 DOSDrives[drive].flags |= DRIVE_DISABLED;
733 /***********************************************************************
736 int DRIVE_Enable( int drive )
738 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
740 SetLastError( ERROR_INVALID_DRIVE );
743 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
748 /***********************************************************************
749 * DRIVE_SetLogicalMapping
751 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
753 /* If new_drive is already valid, do nothing and return 0
754 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
758 old = DOSDrives + existing_drive;
759 new = DOSDrives + new_drive;
761 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
763 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
765 SetLastError( ERROR_INVALID_DRIVE );
771 TRACE("Can\'t map drive %c to drive %c - drive %c already exists\n",
772 'A' + existing_drive, 'A' + new_drive, 'A' + new_drive );
773 /* it is already mapped there, so return success */
774 if (!strcmp(old->root,new->root))
779 new->root = HEAP_strdupA( GetProcessHeap(), 0, old->root );
780 new->dos_cwd = HEAP_strdupA( GetProcessHeap(), 0, old->dos_cwd );
781 new->unix_cwd = HEAP_strdupA( GetProcessHeap(), 0, old->unix_cwd );
782 memcpy ( new->label_conf, old->label_conf, 12 );
783 new->serial_conf = old->serial_conf;
784 new->type = old->type;
785 new->flags = old->flags;
789 TRACE("Drive %c is now equal to drive %c\n",
790 'A' + new_drive, 'A' + existing_drive );
796 /***********************************************************************
799 * Open the drive raw device and return a Unix fd (or -1 on error).
801 int DRIVE_OpenDevice( int drive, int flags )
803 if (!DRIVE_IsValid( drive )) return -1;
804 return open( DOSDrives[drive].device, flags );
808 /***********************************************************************
811 * Read raw sectors from a device
813 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
817 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
819 lseek( fd, begin * 512, SEEK_SET );
820 /* FIXME: check errors */
821 read( fd, dataptr, nr_sect * 512 );
826 memset(dataptr, 0, nr_sect * 512);
829 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
830 if (begin == 1) *dataptr = 0xf8;
839 /***********************************************************************
842 * Write raw sectors to a device
844 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
848 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
850 lseek( fd, begin * 512, SEEK_SET );
851 /* FIXME: check errors */
852 write( fd, dataptr, nr_sect * 512 );
863 /***********************************************************************
866 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
867 PULARGE_INTEGER available )
870 unsigned long long bigsize,bigavail=0;
872 if (!DRIVE_IsValid(drive))
874 SetLastError( ERROR_INVALID_DRIVE );
878 /* FIXME: add autoconf check for this */
879 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
880 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
882 if (statfs( DOSDrives[drive].root, &info) < 0)
886 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
890 bigsize = (unsigned long long)info.f_bsize
891 * (unsigned long long)info.f_blocks;
892 #ifdef STATFS_HAS_BAVAIL
893 bigavail = (unsigned long long)info.f_bavail
894 * (unsigned long long)info.f_bsize;
896 # ifdef STATFS_HAS_BFREE
897 bigavail = (unsigned long long)info.f_bfree
898 * (unsigned long long)info.f_bsize;
900 # error "statfs has no bfree/bavail member!"
903 size->s.LowPart = (DWORD)bigsize;
904 size->s.HighPart = (DWORD)(bigsize>>32);
905 available->s.LowPart = (DWORD)bigavail;
906 available->s.HighPart = (DWORD)(bigavail>>32);
910 /***********************************************************************
911 * DRIVE_GetCurrentDirectory
912 * Returns "X:\\path\\etc\\".
914 * Despite the API description, return required length including the
915 * terminating null when buffer too small. This is the real behaviour.
918 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPSTR buf )
921 const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
924 ret = strlen(s) + 3; /* length of WHOLE current directory */
925 if (ret >= buflen) return ret + 1;
926 lstrcpynA( buf, "A:\\", MIN( 4, buflen ) );
927 if (buflen) buf[0] += DRIVE_GetCurrentDrive();
928 if (buflen > 3) lstrcpynA( buf + 3, s, buflen - 3 );
932 /***********************************************************************
933 * GetDiskFreeSpace16 (KERNEL.422)
935 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
936 LPDWORD sector_bytes, LPDWORD free_clusters,
937 LPDWORD total_clusters )
939 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
940 free_clusters, total_clusters );
944 /***********************************************************************
945 * GetDiskFreeSpace32A (KERNEL32.206)
947 * Fails if expression resulting from current drive's dir and "root"
948 * is not a root dir of the target drive.
950 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
951 * if the corresponding info is unneeded.
953 * FIXME: needs to support UNC names from Win95 OSR2 on.
955 * Behaviour under Win95a:
956 * CurrDir root result
957 * "E:\\TEST" "E:" FALSE
961 * "E:\\TEST" "\\" TRUE
962 * "E:\\TEST" ":\\" FALSE
963 * "E:\\TEST" "E:\\" TRUE
964 * "E:\\TEST" "" FALSE
965 * "E:\\" "" FALSE (!)
967 * "E:\\TEST" 0x0 TRUE (!)
968 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
969 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
971 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
972 LPDWORD sector_bytes, LPDWORD free_clusters,
973 LPDWORD total_clusters )
976 ULARGE_INTEGER size,available;
980 if ((!root) || (strcmp(root,"\\") == 0))
981 drive = DRIVE_GetCurrentDrive();
983 if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
985 drive = toupper(root[0]) - 'A';
988 path = DRIVE_GetDosCwd(drive);
992 if (strlen(path)) /* oops, we are in a subdir */
998 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1000 /* Cap the size and available at 2GB as per specs. */
1001 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1003 size.s.HighPart = 0;
1004 size.s.LowPart = 0x7fffffff;
1006 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1008 available.s.HighPart =0;
1009 available.s.LowPart = 0x7fffffff;
1011 if (DRIVE_GetType(drive)==TYPE_CDROM) {
1013 *sector_bytes = 2048;
1014 size.s.LowPart /= 2048;
1015 available.s.LowPart /= 2048;
1018 *sector_bytes = 512;
1019 size.s.LowPart /= 512;
1020 available.s.LowPart /= 512;
1022 /* fixme: probably have to adjust those variables too for CDFS */
1024 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1026 if (cluster_sectors)
1027 *cluster_sectors = cluster_sec;
1029 *free_clusters = available.s.LowPart / cluster_sec;
1031 *total_clusters = size.s.LowPart / cluster_sec;
1036 /***********************************************************************
1037 * GetDiskFreeSpace32W (KERNEL32.207)
1039 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1040 LPDWORD sector_bytes, LPDWORD free_clusters,
1041 LPDWORD total_clusters )
1046 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1047 ret = GetDiskFreeSpaceA( xroot,cluster_sectors, sector_bytes,
1048 free_clusters, total_clusters );
1049 HeapFree( GetProcessHeap(), 0, xroot );
1054 /***********************************************************************
1055 * GetDiskFreeSpaceEx32A (KERNEL32.871)
1057 * This function is used to aquire the size of the available and
1058 * total space on a logical volume.
1062 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1063 * detailed error information.
1066 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
1067 PULARGE_INTEGER avail,
1068 PULARGE_INTEGER total,
1069 PULARGE_INTEGER totalfree)
1072 ULARGE_INTEGER size,available;
1074 if (!root) drive = DRIVE_GetCurrentDrive();
1077 if ((root[1]) && ((root[1] != ':') || (root[2] != '\\')))
1079 FIXME("there are valid root names which are not supported yet\n");
1080 /* ..like UNC names, for instance. */
1082 WARN("invalid root '%s'\n", root );
1085 drive = toupper(root[0]) - 'A';
1088 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1092 total->s.HighPart = size.s.HighPart;
1093 total->s.LowPart = size.s.LowPart ;
1098 totalfree->s.HighPart = available.s.HighPart;
1099 totalfree->s.LowPart = available.s.LowPart ;
1104 if (FIXME_ON(dosfs))
1106 /* On Windows2000, we need to check the disk quota
1107 allocated for the user owning the calling process. We
1108 don't want to be more obtrusive than necessary with the
1109 FIXME messages, so don't print the FIXME unless Wine is
1110 actually masquerading as Windows2000. */
1113 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1114 if (GetVersionExA(&ovi))
1116 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1117 FIXME("no per-user quota support yet\n");
1121 /* Quick hack, should eventually be fixed to work 100% with
1122 Windows2000 (see comment above). */
1123 avail->s.HighPart = available.s.HighPart;
1124 avail->s.LowPart = available.s.LowPart ;
1130 /***********************************************************************
1131 * GetDiskFreeSpaceEx32W (KERNEL32.873)
1133 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1134 PULARGE_INTEGER total,
1135 PULARGE_INTEGER totalfree)
1140 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1141 ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
1142 HeapFree( GetProcessHeap(), 0, xroot );
1146 /***********************************************************************
1147 * GetDriveType16 (KERNEL.136)
1148 * This function returns the type of a drive in Win16.
1149 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1150 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1151 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1152 * do any pseudo-clever changes.
1155 * drivetype DRIVE_xxx
1157 UINT16 WINAPI GetDriveType16(
1158 UINT16 drive /* [in] number (NOT letter) of drive */
1160 TRACE("(%c:)\n", 'A' + drive );
1161 switch(DRIVE_GetType(drive))
1163 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
1164 case TYPE_HD: return DRIVE_FIXED;
1165 case TYPE_CDROM: return DRIVE_REMOTE;
1166 case TYPE_NETWORK: return DRIVE_REMOTE;
1168 default: return DRIVE_CANNOTDETERMINE;
1173 /***********************************************************************
1174 * GetDriveType32A (KERNEL32.208)
1176 * Returns the type of the disk drive specified. If root is NULL the
1177 * root of the current directory is used.
1181 * Type of drive (from Win32 SDK):
1183 * DRIVE_UNKNOWN unable to find out anything about the drive
1184 * DRIVE_NO_ROOT_DIR nonexistand root dir
1185 * DRIVE_REMOVABLE the disk can be removed from the machine
1186 * DRIVE_FIXED the disk can not be removed from the machine
1187 * DRIVE_REMOTE network disk
1188 * DRIVE_CDROM CDROM drive
1189 * DRIVE_RAMDISK virtual disk in ram
1191 * DRIVE_DOESNOTEXIST XXX Not valid return value
1192 * DRIVE_CANNOTDETERMINE XXX Not valid return value
1196 * Currently returns DRIVE_DOESNOTEXIST and DRIVE_CANNOTDETERMINE
1197 * when it really should return DRIVE_NO_ROOT_DIR and DRIVE_UNKNOWN.
1198 * Why were the former defines used?
1200 * DRIVE_RAMDISK is unsupported.
1202 UINT WINAPI GetDriveTypeA(LPCSTR root /* String describing drive */)
1205 TRACE("(%s)\n", debugstr_a(root));
1207 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1210 if ((root[1]) && (root[1] != ':'))
1212 WARN("invalid root '%s'\n", debugstr_a(root));
1213 return DRIVE_DOESNOTEXIST;
1215 drive = toupper(root[0]) - 'A';
1217 switch(DRIVE_GetType(drive))
1219 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
1220 case TYPE_HD: return DRIVE_FIXED;
1221 case TYPE_CDROM: return DRIVE_CDROM;
1222 case TYPE_NETWORK: return DRIVE_REMOTE;
1223 case TYPE_INVALID: return DRIVE_DOESNOTEXIST;
1224 default: return DRIVE_CANNOTDETERMINE;
1229 /***********************************************************************
1230 * GetDriveType32W (KERNEL32.209)
1232 UINT WINAPI GetDriveTypeW( LPCWSTR root )
1234 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1235 UINT ret = GetDriveTypeA( xpath );
1236 HeapFree( GetProcessHeap(), 0, xpath );
1241 /***********************************************************************
1242 * GetCurrentDirectory16 (KERNEL.411)
1244 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1246 return (UINT16)DRIVE_GetCurrentDirectory(buflen, buf);
1250 /***********************************************************************
1251 * GetCurrentDirectory32A (KERNEL32.196)
1253 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1256 char longname[MAX_PATHNAME_LEN];
1257 char shortname[MAX_PATHNAME_LEN];
1258 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1259 if ( ret > MAX_PATHNAME_LEN ) {
1260 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1263 GetLongPathNameA(shortname, longname, MAX_PATHNAME_LEN);
1264 ret = lstrlenA( longname ) + 1;
1265 if (ret > buflen) return ret;
1266 lstrcpyA(buf, longname);
1270 /***********************************************************************
1271 * GetCurrentDirectory32W (KERNEL32.197)
1273 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1275 LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
1276 UINT ret = GetCurrentDirectoryA( buflen, xpath );
1277 if (ret < buflen) lstrcpyAtoW ( buf, xpath );
1278 HeapFree( GetProcessHeap(), 0, xpath );
1283 /***********************************************************************
1284 * SetCurrentDirectory (KERNEL.412)
1286 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1288 return SetCurrentDirectoryA( dir );
1292 /***********************************************************************
1293 * SetCurrentDirectory32A (KERNEL32.479)
1295 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1297 int olddrive, drive = DRIVE_GetCurrentDrive();
1300 ERR_(file)("(NULL)!\n");
1303 if (dir[0] && (dir[1]==':'))
1305 drive = tolower( *dir ) - 'a';
1309 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1310 sets pTask->curdir only if pTask->curdrive is drive */
1311 olddrive = drive; /* in case DRIVE_Chdir fails */
1312 if (!(DRIVE_SetCurrentDrive( drive )))
1314 /* FIXME: what about empty strings? Add a \\ ? */
1315 if (!DRIVE_Chdir( drive, dir )) {
1316 DRIVE_SetCurrentDrive(olddrive);
1323 /***********************************************************************
1324 * SetCurrentDirectory32W (KERNEL32.480)
1326 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1328 LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1329 BOOL res = SetCurrentDirectoryA( dir );
1330 HeapFree( GetProcessHeap(), 0, dir );
1335 /***********************************************************************
1336 * GetLogicalDriveStrings32A (KERNEL32.231)
1338 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1342 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1343 if (DRIVE_IsValid(drive)) count++;
1344 if ((count * 4) + 1 <= len)
1347 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1348 if (DRIVE_IsValid(drive))
1359 return (count * 4) + 1;/* account for terminating null */
1360 /* The API tells about these different return values */
1364 /***********************************************************************
1365 * GetLogicalDriveStrings32W (KERNEL32.232)
1367 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1371 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1372 if (DRIVE_IsValid(drive)) count++;
1373 if (count * 4 * sizeof(WCHAR) <= len)
1376 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1377 if (DRIVE_IsValid(drive))
1379 *p++ = (WCHAR)('a' + drive);
1386 return count * 4 * sizeof(WCHAR);
1390 /***********************************************************************
1391 * GetLogicalDrives (KERNEL32.233)
1393 DWORD WINAPI GetLogicalDrives(void)
1398 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1400 if ( (DRIVE_IsValid(drive)) ||
1401 (DOSDrives[drive].type == TYPE_CDROM)) /* audio CD is also valid */
1402 ret |= (1 << drive);
1408 /***********************************************************************
1409 * GetVolumeInformation32A (KERNEL32.309)
1411 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1412 DWORD label_len, DWORD *serial,
1413 DWORD *filename_len, DWORD *flags,
1414 LPSTR fsname, DWORD fsname_len )
1419 /* FIXME, SetLastError()s missing */
1421 if (!root) drive = DRIVE_GetCurrentDrive();
1424 if ((root[1]) && (root[1] != ':'))
1426 WARN("invalid root '%s'\n",root);
1429 drive = toupper(root[0]) - 'A';
1431 if (!DRIVE_IsValid( drive )) return FALSE;
1434 lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1435 for (cp = label; *cp; cp++);
1436 while (cp != label && *(cp-1) == ' ') cp--;
1439 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1441 /* Set the filesystem information */
1442 /* Note: we only emulate a FAT fs at present */
1445 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1448 *filename_len = 255;
1453 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1454 *flags|=FS_CASE_SENSITIVE;
1455 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1456 *flags|=FS_CASE_IS_PRESERVED;
1459 /* Diablo checks that return code ... */
1460 if (DRIVE_GetType(drive)==TYPE_CDROM)
1461 lstrcpynA( fsname, "CDFS", fsname_len );
1463 lstrcpynA( fsname, "FAT", fsname_len );
1469 /***********************************************************************
1470 * GetVolumeInformation32W (KERNEL32.310)
1472 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1473 DWORD label_len, DWORD *serial,
1474 DWORD *filename_len, DWORD *flags,
1475 LPWSTR fsname, DWORD fsname_len )
1477 LPSTR xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1478 LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1479 LPSTR xfsname = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1480 BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1481 filename_len, flags, xfsname,
1485 if (label) lstrcpyAtoW( label, xvolname );
1486 if (fsname) lstrcpyAtoW( fsname, xfsname );
1488 HeapFree( GetProcessHeap(), 0, xroot );
1489 HeapFree( GetProcessHeap(), 0, xvolname );
1490 HeapFree( GetProcessHeap(), 0, xfsname );
1494 /***********************************************************************
1495 * SetVolumeLabelA (KERNEL32.675)
1497 BOOL WINAPI SetVolumeLabelA( LPCSTR root, LPCSTR volname )
1501 /* FIXME, SetLastErrors missing */
1503 if (!root) drive = DRIVE_GetCurrentDrive();
1506 if ((root[1]) && (root[1] != ':'))
1508 WARN("invalid root '%s'\n",root);
1511 drive = toupper(root[0]) - 'A';
1513 if (!DRIVE_IsValid( drive )) return FALSE;
1515 /* some copy protection stuff check this */
1516 if (DRIVE_GetType( drive ) == TYPE_CDROM) return FALSE;
1518 FIXME("(%s,%s),stub!\n", root, volname);
1522 /***********************************************************************
1523 * SetVolumeLabelW (KERNEL32.676)
1525 BOOL WINAPI SetVolumeLabelW(LPCWSTR rootpath,LPCWSTR volname)
1530 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath);
1531 xvol = HEAP_strdupWtoA( GetProcessHeap(), 0, volname);
1532 ret = SetVolumeLabelA( xroot, xvol );
1533 HeapFree( GetProcessHeap(), 0, xroot );
1534 HeapFree( GetProcessHeap(), 0, xvol );