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 */
44 #include "wine/winestring.h" /* for lstrcpyAtoW */
52 #include "wine/port.h"
54 #include "debugtools.h"
56 DEFAULT_DEBUG_CHANNEL(dosfs
)
57 DECLARE_DEBUG_CHANNEL(file
)
61 char *root
; /* root dir in Unix format without trailing / */
62 char *dos_cwd
; /* cwd in DOS format without leading or trailing \ */
63 char *unix_cwd
; /* cwd in Unix format without leading or trailing / */
64 char *device
; /* raw device path */
65 char label_conf
[12]; /* drive label as cfg'd in wine.conf */
66 char label_read
[12]; /* drive label as read from device */
67 DWORD serial_conf
; /* drive serial number as cfg'd in wine.conf */
68 DRIVETYPE type
; /* drive type */
69 UINT flags
; /* drive flags */
70 dev_t dev
; /* unix device number */
71 ino_t ino
; /* unix inode number */
75 static const char * const DRIVE_Types
[] =
77 "floppy", /* TYPE_FLOPPY */
79 "cdrom", /* TYPE_CDROM */
80 "network" /* TYPE_NETWORK */
84 /* Known filesystem types */
92 static const FS_DESCR DRIVE_Filesystems
[] =
94 { "unix", DRIVE_CASE_SENSITIVE
| DRIVE_CASE_PRESERVING
},
95 { "msdos", DRIVE_SHORT_NAMES
},
96 { "dos", DRIVE_SHORT_NAMES
},
97 { "fat", DRIVE_SHORT_NAMES
},
98 { "vfat", DRIVE_CASE_PRESERVING
},
99 { "win95", DRIVE_CASE_PRESERVING
},
104 static DOSDRIVE DOSDrives
[MAX_DOS_DRIVES
];
105 static int DRIVE_CurDrive
= -1;
107 static HTASK16 DRIVE_LastTask
= 0;
110 /***********************************************************************
113 static DRIVETYPE
DRIVE_GetDriveType( const char *name
)
118 PROFILE_GetWineIniString( name
, "Type", "hd", buffer
, sizeof(buffer
) );
119 for (i
= 0; i
< sizeof(DRIVE_Types
)/sizeof(DRIVE_Types
[0]); i
++)
121 if (!strcasecmp( buffer
, DRIVE_Types
[i
] )) return (DRIVETYPE
)i
;
123 MESSAGE("%s: unknown drive type '%s', defaulting to 'hd'.\n",
129 /***********************************************************************
132 static UINT
DRIVE_GetFSFlags( const char *name
, const char *value
)
134 const FS_DESCR
*descr
;
136 for (descr
= DRIVE_Filesystems
; descr
->name
; descr
++)
137 if (!strcasecmp( value
, descr
->name
)) return descr
->flags
;
138 MESSAGE("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
140 return DRIVE_CASE_PRESERVING
;
144 /***********************************************************************
149 int i
, len
, count
= 0;
150 char name
[] = "Drive A";
151 char drive_env
[] = "=A:";
152 char path
[MAX_PATHNAME_LEN
];
154 struct stat drive_stat_buffer
;
158 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, name
[6]++, drive
++)
160 PROFILE_GetWineIniString( name
, "Path", "", path
, sizeof(path
)-1 );
163 p
= path
+ strlen(path
) - 1;
164 while ((p
> path
) && ((*p
== '/') || (*p
== '\\'))) *p
-- = '\0';
165 if (!path
[0]) strcpy( path
, "/" );
167 if (stat( path
, &drive_stat_buffer
))
169 MESSAGE("Could not stat %s, ignoring drive %c:\n", path
, 'A' + i
);
172 if (!S_ISDIR(drive_stat_buffer
.st_mode
))
174 MESSAGE("%s is not a directory, ignoring drive %c:\n",
179 drive
->root
= HEAP_strdupA( GetProcessHeap(), 0, path
);
180 drive
->dos_cwd
= HEAP_strdupA( GetProcessHeap(), 0, "" );
181 drive
->unix_cwd
= HEAP_strdupA( GetProcessHeap(), 0, "" );
182 drive
->type
= DRIVE_GetDriveType( name
);
183 drive
->device
= NULL
;
185 drive
->dev
= drive_stat_buffer
.st_dev
;
186 drive
->ino
= drive_stat_buffer
.st_ino
;
188 /* Get the drive label */
189 PROFILE_GetWineIniString( name
, "Label", name
, drive
->label_conf
, 12 );
190 if ((len
= strlen(drive
->label_conf
)) < 11)
192 /* Pad label with spaces */
193 memset( drive
->label_conf
+ len
, ' ', 11 - len
);
194 drive
->label_conf
[11] = '\0';
197 /* Get the serial number */
198 PROFILE_GetWineIniString( name
, "Serial", "12345678",
199 buffer
, sizeof(buffer
) );
200 drive
->serial_conf
= strtoul( buffer
, NULL
, 16 );
202 /* Get the filesystem type */
203 PROFILE_GetWineIniString( name
, "Filesystem", "win95",
204 buffer
, sizeof(buffer
) );
205 drive
->flags
= DRIVE_GetFSFlags( name
, buffer
);
208 PROFILE_GetWineIniString( name
, "Device", "",
209 buffer
, sizeof(buffer
) );
212 drive
->device
= HEAP_strdupA( GetProcessHeap(), 0, buffer
);
213 if (PROFILE_GetWineIniBool( name
, "ReadVolInfo", 1))
214 drive
->flags
|= DRIVE_READ_VOL_INFO
;
217 /* Get the FailReadOnly flag */
218 if (PROFILE_GetWineIniBool( name
, "FailReadOnly", 0 ))
219 drive
->flags
|= DRIVE_FAIL_READ_ONLY
;
221 /* Make the first hard disk the current drive */
222 if ((DRIVE_CurDrive
== -1) && (drive
->type
== TYPE_HD
))
226 TRACE("%s: path=%s type=%s label='%s' serial=%08lx "
227 "flags=%08x dev=%x ino=%x\n",
228 name
, path
, DRIVE_Types
[drive
->type
],
229 drive
->label_conf
, drive
->serial_conf
, drive
->flags
,
230 (int)drive
->dev
, (int)drive
->ino
);
232 else WARN("%s: not defined\n", name
);
237 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
238 /* Create a C drive pointing to Unix root dir */
239 DOSDrives
[2].root
= HEAP_strdupA( GetProcessHeap(), 0, "/" );
240 DOSDrives
[2].dos_cwd
= HEAP_strdupA( GetProcessHeap(), 0, "" );
241 DOSDrives
[2].unix_cwd
= HEAP_strdupA( GetProcessHeap(), 0, "" );
242 strcpy( DOSDrives
[2].label_conf
, "Drive C " );
243 DOSDrives
[2].serial_conf
= 12345678;
244 DOSDrives
[2].type
= TYPE_HD
;
245 DOSDrives
[2].device
= NULL
;
246 DOSDrives
[2].flags
= 0;
250 /* Make sure the current drive is valid */
251 if (DRIVE_CurDrive
== -1)
253 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
255 if (drive
->root
&& !(drive
->flags
& DRIVE_DISABLED
))
263 /* get current working directory info for all drives */
264 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++, drive_env
[1]++)
266 if (!GetEnvironmentVariableA(drive_env
, path
, sizeof(path
))) continue;
268 if (toupper(path
[0]) != drive_env
[1] || path
[1] != ':') continue;
269 DRIVE_Chdir( i
, path
+ 2 );
275 /***********************************************************************
278 int DRIVE_IsValid( int drive
)
280 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
281 return (DOSDrives
[drive
].root
&&
282 !(DOSDrives
[drive
].flags
& DRIVE_DISABLED
));
286 /***********************************************************************
287 * DRIVE_GetCurrentDrive
289 int DRIVE_GetCurrentDrive(void)
291 TDB
*pTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
292 if (pTask
&& (pTask
->curdrive
& 0x80)) return pTask
->curdrive
& ~0x80;
293 return DRIVE_CurDrive
;
297 /***********************************************************************
298 * DRIVE_SetCurrentDrive
300 int DRIVE_SetCurrentDrive( int drive
)
302 TDB
*pTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
303 if (!DRIVE_IsValid( drive
))
305 SetLastError( ERROR_INVALID_DRIVE
);
308 TRACE("%c:\n", 'A' + drive
);
309 DRIVE_CurDrive
= drive
;
310 if (pTask
) pTask
->curdrive
= drive
| 0x80;
311 chdir(DRIVE_GetUnixCwd(drive
));
316 /***********************************************************************
317 * DRIVE_FindDriveRoot
319 * Find a drive for which the root matches the beginning of the given path.
320 * This can be used to translate a Unix path into a drive + DOS path.
321 * Return value is the drive, or -1 on error. On success, path is modified
322 * to point to the beginning of the DOS path.
324 int DRIVE_FindDriveRoot( const char **path
)
326 /* idea: check at all '/' positions.
327 * If the device and inode of that path is identical with the
328 * device and inode of the current drive then we found a solution.
329 * If there is another drive pointing to a deeper position in
330 * the file tree, we want to find that one, not the earlier solution.
332 int drive
, rootdrive
= -1;
333 char buffer
[MAX_PATHNAME_LEN
];
335 const char *p
= *path
;
338 strcpy( buffer
, "/" );
341 if (stat( buffer
, &st
) || !S_ISDIR( st
.st_mode
)) break;
345 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
347 if (!DOSDrives
[drive
].root
||
348 (DOSDrives
[drive
].flags
& DRIVE_DISABLED
)) continue;
350 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
351 (DOSDrives
[drive
].ino
== st
.st_ino
))
359 /* Get the next path component */
362 while ((*p
== '/') || (*p
== '\\')) p
++;
364 while (!IS_END_OF_NAME(*p
)) *next
++ = *p
++;
370 TRACE("%s -> drive %c:, root='%s', name='%s'\n",
371 buffer
, 'A' + rootdrive
, DOSDrives
[rootdrive
].root
, *path
);
376 /***********************************************************************
379 const char * DRIVE_GetRoot( int drive
)
381 if (!DRIVE_IsValid( drive
)) return NULL
;
382 return DOSDrives
[drive
].root
;
386 /***********************************************************************
389 const char * DRIVE_GetDosCwd( int drive
)
391 TDB
*pTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
392 if (!DRIVE_IsValid( drive
)) return NULL
;
394 /* Check if we need to change the directory to the new task. */
395 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
396 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
397 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
399 /* Perform the task-switch */
400 if (!DRIVE_Chdir( drive
, pTask
->curdir
)) DRIVE_Chdir( drive
, "\\" );
401 DRIVE_LastTask
= GetCurrentTask();
403 return DOSDrives
[drive
].dos_cwd
;
407 /***********************************************************************
410 const char * DRIVE_GetUnixCwd( int drive
)
412 TDB
*pTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
413 if (!DRIVE_IsValid( drive
)) return NULL
;
415 /* Check if we need to change the directory to the new task. */
416 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
417 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
418 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
420 /* Perform the task-switch */
421 if (!DRIVE_Chdir( drive
, pTask
->curdir
)) DRIVE_Chdir( drive
, "\\" );
422 DRIVE_LastTask
= GetCurrentTask();
424 return DOSDrives
[drive
].unix_cwd
;
428 /***********************************************************************
431 const char * DRIVE_GetDevice( int drive
)
433 return (DRIVE_IsValid( drive
)) ? DOSDrives
[drive
].device
: NULL
;
437 /***********************************************************************
438 * DRIVE_ReadSuperblock
441 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
442 * to check, that they are writing on a FAT filesystem !
444 int DRIVE_ReadSuperblock (int drive
, char * buff
)
446 #define DRIVE_SUPER 96
450 if (memset(buff
,0,DRIVE_SUPER
)!=buff
) return -1;
451 if ((fd
=open(DOSDrives
[drive
].device
,O_RDONLY
)) == -1)
454 if (!DOSDrives
[drive
].device
)
455 ERR("No device configured for drive %c: !\n", 'A'+drive
);
457 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives
[drive
].device
, 'A'+drive
,
458 (stat(DOSDrives
[drive
].device
, &st
)) ?
459 "not available or symlink not valid ?" : "no permission");
460 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
461 PROFILE_UsageWineIni();
465 switch(DOSDrives
[drive
].type
)
472 offs
= CDROM_Data_FindBestVoldesc(fd
);
479 if ((offs
) && (lseek(fd
,offs
,SEEK_SET
)!=offs
)) return -4;
480 if (read(fd
,buff
,DRIVE_SUPER
)!=DRIVE_SUPER
) return -2;
482 switch(DOSDrives
[drive
].type
)
486 if ((buff
[0x26]!=0x29) || /* Check for FAT present */
487 /* FIXME: do really all FAT have their name beginning with
488 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
489 memcmp( buff
+0x36,"FAT",3))
491 ERR("The filesystem is not FAT !! (device=%s)\n",
492 DOSDrives
[drive
].device
);
497 if (strncmp(&buff
[1],"CD001",5)) /* Check for iso9660 present */
499 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
510 /***********************************************************************
511 * DRIVE_WriteSuperblockEntry
514 * We are writing as little as possible (ie. not the whole SuperBlock)
515 * not to interfere with kernel. The drive can be mounted !
517 int DRIVE_WriteSuperblockEntry (int drive
, off_t ofs
, size_t len
, char * buff
)
521 if ((fd
=open(DOSDrives
[drive
].device
,O_WRONLY
))==-1)
523 ERR("Cannot open the device %s (for writing)\n",
524 DOSDrives
[drive
].device
);
527 if (lseek(fd
,ofs
,SEEK_SET
)!=ofs
)
529 ERR("lseek failed on device %s !\n",
530 DOSDrives
[drive
].device
);
534 if (write(fd
,buff
,len
)!=len
)
537 ERR("Cannot write on %s !\n",
538 DOSDrives
[drive
].device
);
546 /***********************************************************************
549 const char * DRIVE_GetLabel( int drive
)
552 char buff
[DRIVE_SUPER
];
555 if (!DRIVE_IsValid( drive
)) return NULL
;
556 if (DRIVE_GetType(drive
) == TYPE_CDROM
)
558 read
= CDROM_GetLabel(drive
, DOSDrives
[drive
].label_read
);
561 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
563 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
564 ERR("Invalid or unreadable superblock on %s (%c:).\n",
565 DOSDrives
[drive
].device
, (char)(drive
+'A'));
567 if (DOSDrives
[drive
].type
== TYPE_FLOPPY
||
568 DOSDrives
[drive
].type
== TYPE_HD
)
571 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
572 if (offs
!= -1) memcpy(DOSDrives
[drive
].label_read
,buff
+offs
,11);
573 DOSDrives
[drive
].label_read
[11]='\0';
579 DOSDrives
[drive
].label_read
: DOSDrives
[drive
].label_conf
;
583 /***********************************************************************
584 * DRIVE_GetSerialNumber
586 DWORD
DRIVE_GetSerialNumber( int drive
)
589 char buff
[DRIVE_SUPER
];
591 if (!DRIVE_IsValid( drive
)) return 0;
593 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
595 switch(DOSDrives
[drive
].type
)
599 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
600 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
601 " Maybe not FAT?\n" ,
602 DOSDrives
[drive
].device
, 'A'+drive
);
604 serial
= *((DWORD
*)(buff
+0x27));
607 serial
= CDROM_GetSerial(drive
);
610 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive
+'A');
614 return (serial
) ? serial
: DOSDrives
[drive
].serial_conf
;
618 /***********************************************************************
619 * DRIVE_SetSerialNumber
621 int DRIVE_SetSerialNumber( int drive
, DWORD serial
)
623 char buff
[DRIVE_SUPER
];
625 if (!DRIVE_IsValid( drive
)) return 0;
627 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
629 if ((DOSDrives
[drive
].type
!= TYPE_FLOPPY
) &&
630 (DOSDrives
[drive
].type
!= TYPE_HD
)) return 0;
631 /* check, if the drive has a FAT filesystem */
632 if (DRIVE_ReadSuperblock(drive
, buff
)) return 0;
633 if (DRIVE_WriteSuperblockEntry(drive
, 0x27, 4, (char *) &serial
)) return 0;
637 if (DOSDrives
[drive
].type
== TYPE_CDROM
) return 0;
638 DOSDrives
[drive
].serial_conf
= serial
;
643 /***********************************************************************
646 DRIVETYPE
DRIVE_GetType( int drive
)
648 if (!DRIVE_IsValid( drive
)) return TYPE_INVALID
;
649 return DOSDrives
[drive
].type
;
653 /***********************************************************************
656 UINT
DRIVE_GetFlags( int drive
)
658 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
659 return DOSDrives
[drive
].flags
;
663 /***********************************************************************
666 int DRIVE_Chdir( int drive
, const char *path
)
668 DOS_FULL_NAME full_name
;
669 char buffer
[MAX_PATHNAME_LEN
];
671 BY_HANDLE_FILE_INFORMATION info
;
672 TDB
*pTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
674 strcpy( buffer
, "A:" );
676 TRACE("(%s,%s)\n", buffer
, path
);
677 lstrcpynA( buffer
+ 2, path
, sizeof(buffer
) - 2 );
679 if (!DOSFS_GetFullName( buffer
, TRUE
, &full_name
)) return 0;
680 if (!FILE_Stat( full_name
.long_name
, &info
)) return 0;
681 if (!(info
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
683 SetLastError( ERROR_FILE_NOT_FOUND
);
686 unix_cwd
= full_name
.long_name
+ strlen( DOSDrives
[drive
].root
);
687 while (*unix_cwd
== '/') unix_cwd
++;
689 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
690 'A' + drive
, unix_cwd
, full_name
.short_name
+ 3 );
692 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].dos_cwd
);
693 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].unix_cwd
);
694 DOSDrives
[drive
].dos_cwd
= HEAP_strdupA( GetProcessHeap(), 0,
695 full_name
.short_name
+ 3 );
696 DOSDrives
[drive
].unix_cwd
= HEAP_strdupA( GetProcessHeap(), 0, unix_cwd
);
698 if (pTask
&& (pTask
->curdrive
& 0x80) &&
699 ((pTask
->curdrive
& ~0x80) == drive
))
701 lstrcpynA( pTask
->curdir
, full_name
.short_name
+ 2,
702 sizeof(pTask
->curdir
) );
703 DRIVE_LastTask
= GetCurrentTask();
704 chdir(unix_cwd
); /* Only change if on current drive */
710 /***********************************************************************
713 int DRIVE_Disable( int drive
)
715 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
717 SetLastError( ERROR_INVALID_DRIVE
);
720 DOSDrives
[drive
].flags
|= DRIVE_DISABLED
;
725 /***********************************************************************
728 int DRIVE_Enable( int drive
)
730 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
732 SetLastError( ERROR_INVALID_DRIVE
);
735 DOSDrives
[drive
].flags
&= ~DRIVE_DISABLED
;
740 /***********************************************************************
741 * DRIVE_SetLogicalMapping
743 int DRIVE_SetLogicalMapping ( int existing_drive
, int new_drive
)
745 /* If new_drive is already valid, do nothing and return 0
746 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
750 old
= DOSDrives
+ existing_drive
;
751 new = DOSDrives
+ new_drive
;
753 if ((existing_drive
< 0) || (existing_drive
>= MAX_DOS_DRIVES
) ||
755 (new_drive
< 0) || (new_drive
>= MAX_DOS_DRIVES
))
757 SetLastError( ERROR_INVALID_DRIVE
);
763 TRACE("Can't map drive %c: to already existing drive %c:\n",
764 'A' + existing_drive
, 'A' + new_drive
);
765 /* it is already mapped there, so return success */
766 if (!strcmp(old
->root
,new->root
))
771 new->root
= HEAP_strdupA( GetProcessHeap(), 0, old
->root
);
772 new->dos_cwd
= HEAP_strdupA( GetProcessHeap(), 0, old
->dos_cwd
);
773 new->unix_cwd
= HEAP_strdupA( GetProcessHeap(), 0, old
->unix_cwd
);
774 new->device
= HEAP_strdupA( GetProcessHeap(), 0, old
->device
);
775 memcpy ( new->label_conf
, old
->label_conf
, 12 );
776 memcpy ( new->label_read
, old
->label_read
, 12 );
777 new->serial_conf
= old
->serial_conf
;
778 new->type
= old
->type
;
779 new->flags
= old
->flags
;
783 TRACE("Drive %c: is now equal to drive %c:\n",
784 'A' + new_drive
, 'A' + existing_drive
);
790 /***********************************************************************
793 * Open the drive raw device and return a Unix fd (or -1 on error).
795 int DRIVE_OpenDevice( int drive
, int flags
)
797 if (!DRIVE_IsValid( drive
)) return -1;
798 return open( DOSDrives
[drive
].device
, flags
);
802 /***********************************************************************
805 * Read raw sectors from a device
807 int DRIVE_RawRead(BYTE drive
, DWORD begin
, DWORD nr_sect
, BYTE
*dataptr
, BOOL fake_success
)
811 if ((fd
= DRIVE_OpenDevice( drive
, O_RDONLY
)) != -1)
813 lseek( fd
, begin
* 512, SEEK_SET
);
814 /* FIXME: check errors */
815 read( fd
, dataptr
, nr_sect
* 512 );
820 memset(dataptr
, 0, nr_sect
* 512);
823 if (begin
== 0 && nr_sect
> 1) *(dataptr
+ 512) = 0xf8;
824 if (begin
== 1) *dataptr
= 0xf8;
833 /***********************************************************************
836 * Write raw sectors to a device
838 int DRIVE_RawWrite(BYTE drive
, DWORD begin
, DWORD nr_sect
, BYTE
*dataptr
, BOOL fake_success
)
842 if ((fd
= DRIVE_OpenDevice( drive
, O_RDONLY
)) != -1)
844 lseek( fd
, begin
* 512, SEEK_SET
);
845 /* FIXME: check errors */
846 write( fd
, dataptr
, nr_sect
* 512 );
857 /***********************************************************************
860 static int DRIVE_GetFreeSpace( int drive
, PULARGE_INTEGER size
,
861 PULARGE_INTEGER available
)
865 if (!DRIVE_IsValid(drive
))
867 SetLastError( ERROR_INVALID_DRIVE
);
871 /* FIXME: add autoconf check for this */
872 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
873 if (statfs( DOSDrives
[drive
].root
, &info
, 0, 0) < 0)
875 if (statfs( DOSDrives
[drive
].root
, &info
) < 0)
879 WARN("cannot do statfs(%s)\n", DOSDrives
[drive
].root
);
883 size
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bsize
, info
.f_blocks
);
884 #ifdef STATFS_HAS_BAVAIL
885 available
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bavail
, info
.f_bsize
);
887 # ifdef STATFS_HAS_BFREE
888 available
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bfree
, info
.f_bsize
);
890 # error "statfs has no bfree/bavail member!"
893 if (DRIVE_GetType(drive
) == TYPE_CDROM
)
894 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
895 available
->QuadPart
= 0;
900 /***********************************************************************
901 * DRIVE_GetCurrentDirectory
902 * Returns "X:\\path\\etc\\".
904 * Despite the API description, return required length including the
905 * terminating null when buffer too small. This is the real behaviour.
908 static UINT
DRIVE_GetCurrentDirectory( UINT buflen
, LPSTR buf
)
911 const char *s
= DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
914 ret
= strlen(s
) + 3; /* length of WHOLE current directory */
915 if (ret
>= buflen
) return ret
+ 1;
916 lstrcpynA( buf
, "A:\\", min( 4, buflen
) );
917 if (buflen
) buf
[0] += DRIVE_GetCurrentDrive();
918 if (buflen
> 3) lstrcpynA( buf
+ 3, s
, buflen
- 3 );
923 /***********************************************************************
926 * Build the environment array containing the drives current directories.
927 * Resulting pointer must be freed with HeapFree.
929 char *DRIVE_BuildEnv(void)
932 const char *cwd
[MAX_DOS_DRIVES
];
935 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++)
937 if ((cwd
[i
] = DRIVE_GetDosCwd(i
)) && cwd
[i
][0]) length
+= strlen(cwd
[i
]) + 8;
939 if (!(env
= HeapAlloc( GetProcessHeap(), 0, length
+1 ))) return NULL
;
940 for (i
= 0, p
= env
; i
< MAX_DOS_DRIVES
; i
++)
942 if (cwd
[i
] && cwd
[i
][0])
943 p
+= sprintf( p
, "=%c:=%c:\\%s", 'A'+i
, 'A'+i
, cwd
[i
] ) + 1;
950 /***********************************************************************
951 * GetDiskFreeSpace16 (KERNEL.422)
953 BOOL16 WINAPI
GetDiskFreeSpace16( LPCSTR root
, LPDWORD cluster_sectors
,
954 LPDWORD sector_bytes
, LPDWORD free_clusters
,
955 LPDWORD total_clusters
)
957 return GetDiskFreeSpaceA( root
, cluster_sectors
, sector_bytes
,
958 free_clusters
, total_clusters
);
962 /***********************************************************************
963 * GetDiskFreeSpaceA (KERNEL32.206)
965 * Fails if expression resulting from current drive's dir and "root"
966 * is not a root dir of the target drive.
968 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
969 * if the corresponding info is unneeded.
971 * FIXME: needs to support UNC names from Win95 OSR2 on.
973 * Behaviour under Win95a:
974 * CurrDir root result
975 * "E:\\TEST" "E:" FALSE
979 * "E:\\TEST" "\\" TRUE
980 * "E:\\TEST" ":\\" FALSE
981 * "E:\\TEST" "E:\\" TRUE
982 * "E:\\TEST" "" FALSE
983 * "E:\\" "" FALSE (!)
985 * "E:\\TEST" 0x0 TRUE (!)
986 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
987 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
989 BOOL WINAPI
GetDiskFreeSpaceA( LPCSTR root
, LPDWORD cluster_sectors
,
990 LPDWORD sector_bytes
, LPDWORD free_clusters
,
991 LPDWORD total_clusters
)
994 ULARGE_INTEGER size
,available
;
998 if ((!root
) || (strcmp(root
,"\\") == 0))
999 drive
= DRIVE_GetCurrentDrive();
1001 if ( (strlen(root
) >= 2) && (root
[1] == ':')) /* root contains drive tag */
1003 drive
= toupper(root
[0]) - 'A';
1005 if (path
[0] == '\0')
1006 path
= DRIVE_GetDosCwd(drive
);
1008 if (path
[0] == '\\')
1010 if (strlen(path
)) /* oops, we are in a subdir */
1016 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1018 /* Cap the size and available at 2GB as per specs. */
1019 if ((size
.s
.HighPart
) ||(size
.s
.LowPart
> 0x7fffffff))
1021 size
.s
.HighPart
= 0;
1022 size
.s
.LowPart
= 0x7fffffff;
1024 if ((available
.s
.HighPart
) ||(available
.s
.LowPart
> 0x7fffffff))
1026 available
.s
.HighPart
=0;
1027 available
.s
.LowPart
= 0x7fffffff;
1029 sec_size
= (DRIVE_GetType(drive
)==TYPE_CDROM
) ? 2048 : 512;
1030 size
.s
.LowPart
/= sec_size
;
1031 available
.s
.LowPart
/= sec_size
;
1032 /* fixme: probably have to adjust those variables too for CDFS */
1034 while (cluster_sec
* 65536 < size
.s
.LowPart
) cluster_sec
*= 2;
1036 if (cluster_sectors
)
1037 *cluster_sectors
= cluster_sec
;
1039 *sector_bytes
= sec_size
;
1041 *free_clusters
= available
.s
.LowPart
/ cluster_sec
;
1043 *total_clusters
= size
.s
.LowPart
/ cluster_sec
;
1048 /***********************************************************************
1049 * GetDiskFreeSpaceW (KERNEL32.207)
1051 BOOL WINAPI
GetDiskFreeSpaceW( LPCWSTR root
, LPDWORD cluster_sectors
,
1052 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1053 LPDWORD total_clusters
)
1058 xroot
= HEAP_strdupWtoA( GetProcessHeap(), 0, root
);
1059 ret
= GetDiskFreeSpaceA( xroot
,cluster_sectors
, sector_bytes
,
1060 free_clusters
, total_clusters
);
1061 HeapFree( GetProcessHeap(), 0, xroot
);
1066 /***********************************************************************
1067 * GetDiskFreeSpaceExA (KERNEL32.871)
1069 * This function is used to aquire the size of the available and
1070 * total space on a logical volume.
1074 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1075 * detailed error information.
1078 BOOL WINAPI
GetDiskFreeSpaceExA( LPCSTR root
,
1079 PULARGE_INTEGER avail
,
1080 PULARGE_INTEGER total
,
1081 PULARGE_INTEGER totalfree
)
1084 ULARGE_INTEGER size
,available
;
1086 if (!root
) drive
= DRIVE_GetCurrentDrive();
1089 if ((root
[1]) && ((root
[1] != ':') || (root
[2] != '\\')))
1091 FIXME("there are valid root names which are not supported yet\n");
1092 /* ..like UNC names, for instance. */
1094 WARN("invalid root '%s'\n", root
);
1097 drive
= toupper(root
[0]) - 'A';
1100 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1104 total
->s
.HighPart
= size
.s
.HighPart
;
1105 total
->s
.LowPart
= size
.s
.LowPart
;
1110 totalfree
->s
.HighPart
= available
.s
.HighPart
;
1111 totalfree
->s
.LowPart
= available
.s
.LowPart
;
1116 if (FIXME_ON(dosfs
))
1118 /* On Windows2000, we need to check the disk quota
1119 allocated for the user owning the calling process. We
1120 don't want to be more obtrusive than necessary with the
1121 FIXME messages, so don't print the FIXME unless Wine is
1122 actually masquerading as Windows2000. */
1125 ovi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOA
);
1126 if (GetVersionExA(&ovi
))
1128 if (ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
&& ovi
.dwMajorVersion
> 4)
1129 FIXME("no per-user quota support yet\n");
1133 /* Quick hack, should eventually be fixed to work 100% with
1134 Windows2000 (see comment above). */
1135 avail
->s
.HighPart
= available
.s
.HighPart
;
1136 avail
->s
.LowPart
= available
.s
.LowPart
;
1142 /***********************************************************************
1143 * GetDiskFreeSpaceExW (KERNEL32.873)
1145 BOOL WINAPI
GetDiskFreeSpaceExW( LPCWSTR root
, PULARGE_INTEGER avail
,
1146 PULARGE_INTEGER total
,
1147 PULARGE_INTEGER totalfree
)
1152 xroot
= HEAP_strdupWtoA( GetProcessHeap(), 0, root
);
1153 ret
= GetDiskFreeSpaceExA( xroot
, avail
, total
, totalfree
);
1154 HeapFree( GetProcessHeap(), 0, xroot
);
1158 /***********************************************************************
1159 * GetDriveType16 (KERNEL.136)
1160 * This function returns the type of a drive in Win16.
1161 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1162 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1163 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1164 * do any pseudo-clever changes.
1167 * drivetype DRIVE_xxx
1169 UINT16 WINAPI
GetDriveType16(
1170 UINT16 drive
/* [in] number (NOT letter) of drive */
1172 TRACE("(%c:)\n", 'A' + drive
);
1173 switch(DRIVE_GetType(drive
))
1175 case TYPE_FLOPPY
: return DRIVE_REMOVABLE
;
1176 case TYPE_HD
: return DRIVE_FIXED
;
1177 case TYPE_CDROM
: return DRIVE_REMOTE
;
1178 case TYPE_NETWORK
: return DRIVE_REMOTE
;
1180 default: return DRIVE_CANNOTDETERMINE
;
1185 /***********************************************************************
1186 * GetDriveTypeA (KERNEL32.208)
1188 * Returns the type of the disk drive specified. If root is NULL the
1189 * root of the current directory is used.
1193 * Type of drive (from Win32 SDK):
1195 * DRIVE_UNKNOWN unable to find out anything about the drive
1196 * DRIVE_NO_ROOT_DIR nonexistent root dir
1197 * DRIVE_REMOVABLE the disk can be removed from the machine
1198 * DRIVE_FIXED the disk can not be removed from the machine
1199 * DRIVE_REMOTE network disk
1200 * DRIVE_CDROM CDROM drive
1201 * DRIVE_RAMDISK virtual disk in RAM
1203 * DRIVE_DOESNOTEXIST FIXME Not valid return value
1204 * DRIVE_CANNOTDETERMINE FIXME Not valid return value
1208 * Currently returns DRIVE_DOESNOTEXIST and DRIVE_CANNOTDETERMINE
1209 * when it really should return DRIVE_NO_ROOT_DIR and DRIVE_UNKNOWN.
1210 * Why were the former defines used?
1212 * DRIVE_RAMDISK is unsupported.
1214 UINT WINAPI
GetDriveTypeA(LPCSTR root
/* String describing drive */)
1217 TRACE("(%s)\n", debugstr_a(root
));
1219 if (NULL
== root
) drive
= DRIVE_GetCurrentDrive();
1222 if ((root
[1]) && (root
[1] != ':'))
1224 WARN("invalid root '%s'\n", debugstr_a(root
));
1225 return DRIVE_DOESNOTEXIST
;
1227 drive
= toupper(root
[0]) - 'A';
1229 switch(DRIVE_GetType(drive
))
1231 case TYPE_FLOPPY
: return DRIVE_REMOVABLE
;
1232 case TYPE_HD
: return DRIVE_FIXED
;
1233 case TYPE_CDROM
: return DRIVE_CDROM
;
1234 case TYPE_NETWORK
: return DRIVE_REMOTE
;
1235 case TYPE_INVALID
: return DRIVE_DOESNOTEXIST
;
1236 default: return DRIVE_CANNOTDETERMINE
;
1241 /***********************************************************************
1242 * GetDriveTypeW (KERNEL32.209)
1244 UINT WINAPI
GetDriveTypeW( LPCWSTR root
)
1246 LPSTR xpath
= HEAP_strdupWtoA( GetProcessHeap(), 0, root
);
1247 UINT ret
= GetDriveTypeA( xpath
);
1248 HeapFree( GetProcessHeap(), 0, xpath
);
1253 /***********************************************************************
1254 * GetCurrentDirectory16 (KERNEL.411)
1256 UINT16 WINAPI
GetCurrentDirectory16( UINT16 buflen
, LPSTR buf
)
1258 return (UINT16
)DRIVE_GetCurrentDirectory(buflen
, buf
);
1262 /***********************************************************************
1263 * GetCurrentDirectoryA (KERNEL32.196)
1265 UINT WINAPI
GetCurrentDirectoryA( UINT buflen
, LPSTR buf
)
1268 char longname
[MAX_PATHNAME_LEN
];
1269 char shortname
[MAX_PATHNAME_LEN
];
1270 ret
= DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN
, shortname
);
1271 if ( ret
> MAX_PATHNAME_LEN
) {
1272 ERR_(file
)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret
);
1275 GetLongPathNameA(shortname
, longname
, MAX_PATHNAME_LEN
);
1276 ret
= strlen( longname
) + 1;
1277 if (ret
> buflen
) return ret
;
1278 strcpy(buf
, longname
);
1282 /***********************************************************************
1283 * GetCurrentDirectoryW (KERNEL32.197)
1285 UINT WINAPI
GetCurrentDirectoryW( UINT buflen
, LPWSTR buf
)
1287 LPSTR xpath
= HeapAlloc( GetProcessHeap(), 0, buflen
+1 );
1288 UINT ret
= GetCurrentDirectoryA( buflen
, xpath
);
1289 if (ret
< buflen
) lstrcpyAtoW ( buf
, xpath
);
1290 HeapFree( GetProcessHeap(), 0, xpath
);
1295 /***********************************************************************
1296 * SetCurrentDirectory (KERNEL.412)
1298 BOOL16 WINAPI
SetCurrentDirectory16( LPCSTR dir
)
1300 return SetCurrentDirectoryA( dir
);
1304 /***********************************************************************
1305 * SetCurrentDirectoryA (KERNEL32.479)
1307 BOOL WINAPI
SetCurrentDirectoryA( LPCSTR dir
)
1309 int drive
, olddrive
= DRIVE_GetCurrentDrive();
1312 ERR_(file
)("(NULL)!\n");
1315 if (dir
[0] && (dir
[1]==':'))
1317 drive
= toupper( *dir
) - 'A';
1323 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1324 sets pTask->curdir only if pTask->curdrive is drive */
1325 if (!(DRIVE_SetCurrentDrive( drive
)))
1327 /* FIXME: what about empty strings? Add a \\ ? */
1328 if (!DRIVE_Chdir( drive
, dir
)) {
1329 DRIVE_SetCurrentDrive(olddrive
);
1336 /***********************************************************************
1337 * SetCurrentDirectoryW (KERNEL32.480)
1339 BOOL WINAPI
SetCurrentDirectoryW( LPCWSTR dirW
)
1341 LPSTR dir
= HEAP_strdupWtoA( GetProcessHeap(), 0, dirW
);
1342 BOOL res
= SetCurrentDirectoryA( dir
);
1343 HeapFree( GetProcessHeap(), 0, dir
);
1348 /***********************************************************************
1349 * GetLogicalDriveStringsA (KERNEL32.231)
1351 UINT WINAPI
GetLogicalDriveStringsA( UINT len
, LPSTR buffer
)
1355 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1356 if (DRIVE_IsValid(drive
)) count
++;
1357 if ((count
* 4) + 1 <= len
)
1360 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1361 if (DRIVE_IsValid(drive
))
1372 return (count
* 4) + 1; /* account for terminating null */
1373 /* The API tells about these different return values */
1377 /***********************************************************************
1378 * GetLogicalDriveStringsW (KERNEL32.232)
1380 UINT WINAPI
GetLogicalDriveStringsW( UINT len
, LPWSTR buffer
)
1384 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1385 if (DRIVE_IsValid(drive
)) count
++;
1386 if (count
* 4 * sizeof(WCHAR
) <= len
)
1389 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1390 if (DRIVE_IsValid(drive
))
1392 *p
++ = (WCHAR
)('a' + drive
);
1399 return count
* 4 * sizeof(WCHAR
);
1403 /***********************************************************************
1404 * GetLogicalDrives (KERNEL32.233)
1406 DWORD WINAPI
GetLogicalDrives(void)
1411 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1413 if ( (DRIVE_IsValid(drive
)) ||
1414 (DOSDrives
[drive
].type
== TYPE_CDROM
)) /* audio CD is also valid */
1415 ret
|= (1 << drive
);
1421 /***********************************************************************
1422 * GetVolumeInformationA (KERNEL32.309)
1424 BOOL WINAPI
GetVolumeInformationA( LPCSTR root
, LPSTR label
,
1425 DWORD label_len
, DWORD
*serial
,
1426 DWORD
*filename_len
, DWORD
*flags
,
1427 LPSTR fsname
, DWORD fsname_len
)
1432 /* FIXME, SetLastError()s missing */
1434 if (!root
) drive
= DRIVE_GetCurrentDrive();
1437 if ((root
[1]) && (root
[1] != ':'))
1439 WARN("invalid root '%s'\n",root
);
1442 drive
= toupper(root
[0]) - 'A';
1444 if (!DRIVE_IsValid( drive
)) return FALSE
;
1447 lstrcpynA( label
, DRIVE_GetLabel(drive
), label_len
);
1448 for (cp
= label
; *cp
; cp
++);
1449 while (cp
!= label
&& *(cp
-1) == ' ') cp
--;
1452 if (serial
) *serial
= DRIVE_GetSerialNumber(drive
);
1454 /* Set the filesystem information */
1455 /* Note: we only emulate a FAT fs at present */
1458 if (DOSDrives
[drive
].flags
& DRIVE_SHORT_NAMES
)
1461 *filename_len
= 255;
1466 if (DOSDrives
[drive
].flags
& DRIVE_CASE_SENSITIVE
)
1467 *flags
|=FS_CASE_SENSITIVE
;
1468 if (DOSDrives
[drive
].flags
& DRIVE_CASE_PRESERVING
)
1469 *flags
|=FS_CASE_IS_PRESERVED
;
1472 /* Diablo checks that return code ... */
1473 if (DRIVE_GetType(drive
)==TYPE_CDROM
)
1474 lstrcpynA( fsname
, "CDFS", fsname_len
);
1476 lstrcpynA( fsname
, "FAT", fsname_len
);
1482 /***********************************************************************
1483 * GetVolumeInformationW (KERNEL32.310)
1485 BOOL WINAPI
GetVolumeInformationW( LPCWSTR root
, LPWSTR label
,
1486 DWORD label_len
, DWORD
*serial
,
1487 DWORD
*filename_len
, DWORD
*flags
,
1488 LPWSTR fsname
, DWORD fsname_len
)
1490 LPSTR xroot
= HEAP_strdupWtoA( GetProcessHeap(), 0, root
);
1491 LPSTR xvolname
= label
? HeapAlloc(GetProcessHeap(),0,label_len
) : NULL
;
1492 LPSTR xfsname
= fsname
? HeapAlloc(GetProcessHeap(),0,fsname_len
) : NULL
;
1493 BOOL ret
= GetVolumeInformationA( xroot
, xvolname
, label_len
, serial
,
1494 filename_len
, flags
, xfsname
,
1498 if (label
) lstrcpyAtoW( label
, xvolname
);
1499 if (fsname
) lstrcpyAtoW( fsname
, xfsname
);
1501 HeapFree( GetProcessHeap(), 0, xroot
);
1502 HeapFree( GetProcessHeap(), 0, xvolname
);
1503 HeapFree( GetProcessHeap(), 0, xfsname
);
1507 /***********************************************************************
1508 * SetVolumeLabelA (KERNEL32.675)
1510 BOOL WINAPI
SetVolumeLabelA( LPCSTR root
, LPCSTR volname
)
1514 /* FIXME, SetLastErrors missing */
1516 if (!root
) drive
= DRIVE_GetCurrentDrive();
1519 if ((root
[1]) && (root
[1] != ':'))
1521 WARN("invalid root '%s'\n",root
);
1524 drive
= toupper(root
[0]) - 'A';
1526 if (!DRIVE_IsValid( drive
)) return FALSE
;
1528 /* some copy protection stuff check this */
1529 if (DRIVE_GetType( drive
) == TYPE_CDROM
) return FALSE
;
1531 FIXME("(%s,%s),stub!\n", root
, volname
);
1535 /***********************************************************************
1536 * SetVolumeLabelW (KERNEL32.676)
1538 BOOL WINAPI
SetVolumeLabelW(LPCWSTR rootpath
,LPCWSTR volname
)
1543 xroot
= HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath
);
1544 xvol
= HEAP_strdupWtoA( GetProcessHeap(), 0, volname
);
1545 ret
= SetVolumeLabelA( xroot
, xvol
);
1546 HeapFree( GetProcessHeap(), 0, xroot
);
1547 HeapFree( GetProcessHeap(), 0, xvol
);