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 char label_conf
[12]; /* drive label as cfg'd in wine.conf */
64 char label_read
[12]; /* drive label as read from device */
65 DWORD serial_conf
; /* drive serial number as cfg'd in wine.conf */
66 DRIVETYPE type
; /* drive type */
67 UINT flags
; /* drive flags */
68 dev_t dev
; /* unix device number */
69 ino_t ino
; /* unix inode number */
73 static const char * const DRIVE_Types
[] =
75 "floppy", /* TYPE_FLOPPY */
77 "cdrom", /* TYPE_CDROM */
78 "network" /* TYPE_NETWORK */
82 /* Known filesystem types */
90 static const FS_DESCR DRIVE_Filesystems
[] =
92 { "unix", DRIVE_CASE_SENSITIVE
| DRIVE_CASE_PRESERVING
},
93 { "msdos", DRIVE_SHORT_NAMES
},
94 { "dos", DRIVE_SHORT_NAMES
},
95 { "fat", DRIVE_SHORT_NAMES
},
96 { "vfat", DRIVE_CASE_PRESERVING
},
97 { "win95", DRIVE_CASE_PRESERVING
},
102 static DOSDRIVE DOSDrives
[MAX_DOS_DRIVES
];
103 static int DRIVE_CurDrive
= -1;
105 static HTASK16 DRIVE_LastTask
= 0;
108 /***********************************************************************
111 static DRIVETYPE
DRIVE_GetDriveType( const char *name
)
116 PROFILE_GetWineIniString( name
, "Type", "hd", buffer
, sizeof(buffer
) );
117 for (i
= 0; i
< sizeof(DRIVE_Types
)/sizeof(DRIVE_Types
[0]); i
++)
119 if (!strcasecmp( buffer
, DRIVE_Types
[i
] )) return (DRIVETYPE
)i
;
121 MESSAGE("%s: unknown drive type '%s', defaulting to 'hd'.\n",
127 /***********************************************************************
130 static UINT
DRIVE_GetFSFlags( const char *name
, const char *value
)
132 const FS_DESCR
*descr
;
134 for (descr
= DRIVE_Filesystems
; descr
->name
; descr
++)
135 if (!strcasecmp( value
, descr
->name
)) return descr
->flags
;
136 MESSAGE("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
138 return DRIVE_CASE_PRESERVING
;
142 /***********************************************************************
147 int i
, len
, count
= 0;
148 char name
[] = "Drive A";
149 char path
[MAX_PATHNAME_LEN
];
151 struct stat drive_stat_buffer
;
155 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, name
[6]++, drive
++)
157 PROFILE_GetWineIniString( name
, "Path", "", path
, sizeof(path
)-1 );
160 p
= path
+ strlen(path
) - 1;
161 while ((p
> path
) && ((*p
== '/') || (*p
== '\\'))) *p
-- = '\0';
162 if (!path
[0]) strcpy( path
, "/" );
164 if (stat( path
, &drive_stat_buffer
))
166 MESSAGE("Could not stat %s, ignoring drive %c:\n", path
, 'A' + i
);
169 if (!S_ISDIR(drive_stat_buffer
.st_mode
))
171 MESSAGE("%s is not a directory, ignoring drive %c:\n",
176 drive
->root
= HEAP_strdupA( GetProcessHeap(), 0, path
);
177 drive
->dos_cwd
= HEAP_strdupA( GetProcessHeap(), 0, "" );
178 drive
->unix_cwd
= HEAP_strdupA( GetProcessHeap(), 0, "" );
179 drive
->type
= DRIVE_GetDriveType( name
);
180 drive
->device
= NULL
;
182 drive
->dev
= drive_stat_buffer
.st_dev
;
183 drive
->ino
= drive_stat_buffer
.st_ino
;
185 /* Get the drive label */
186 PROFILE_GetWineIniString( name
, "Label", name
, drive
->label_conf
, 12 );
187 if ((len
= strlen(drive
->label_conf
)) < 11)
189 /* Pad label with spaces */
190 memset( drive
->label_conf
+ len
, ' ', 11 - len
);
191 drive
->label_conf
[11] = '\0';
194 /* Get the serial number */
195 PROFILE_GetWineIniString( name
, "Serial", "12345678",
196 buffer
, sizeof(buffer
) );
197 drive
->serial_conf
= strtoul( buffer
, NULL
, 16 );
199 /* Get the filesystem type */
200 PROFILE_GetWineIniString( name
, "Filesystem", "win95",
201 buffer
, sizeof(buffer
) );
202 drive
->flags
= DRIVE_GetFSFlags( name
, buffer
);
205 PROFILE_GetWineIniString( name
, "Device", "",
206 buffer
, sizeof(buffer
) );
209 drive
->device
= HEAP_strdupA( GetProcessHeap(), 0, buffer
);
210 if (PROFILE_GetWineIniBool( name
, "ReadVolInfo", 1))
211 drive
->flags
|= DRIVE_READ_VOL_INFO
;
214 /* Get the FailReadOnly flag */
215 if (PROFILE_GetWineIniBool( name
, "FailReadOnly", 0 ))
216 drive
->flags
|= DRIVE_FAIL_READ_ONLY
;
218 /* Make the first hard disk the current drive */
219 if ((DRIVE_CurDrive
== -1) && (drive
->type
== TYPE_HD
))
223 TRACE("%s: path=%s type=%s label='%s' serial=%08lx "
224 "flags=%08x dev=%x ino=%x\n",
225 name
, path
, DRIVE_Types
[drive
->type
],
226 drive
->label_conf
, drive
->serial_conf
, drive
->flags
,
227 (int)drive
->dev
, (int)drive
->ino
);
229 else WARN("%s: not defined\n", name
);
234 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
235 /* Create a C drive pointing to Unix root dir */
236 DOSDrives
[2].root
= HEAP_strdupA( GetProcessHeap(), 0, "/" );
237 DOSDrives
[2].dos_cwd
= HEAP_strdupA( GetProcessHeap(), 0, "" );
238 DOSDrives
[2].unix_cwd
= HEAP_strdupA( GetProcessHeap(), 0, "" );
239 strcpy( DOSDrives
[2].label_conf
, "Drive C " );
240 DOSDrives
[2].serial_conf
= 12345678;
241 DOSDrives
[2].type
= TYPE_HD
;
242 DOSDrives
[2].flags
= 0;
246 /* Make sure the current drive is valid */
247 if (DRIVE_CurDrive
== -1)
249 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
251 if (drive
->root
&& !(drive
->flags
& DRIVE_DISABLED
))
263 /***********************************************************************
266 int DRIVE_IsValid( int drive
)
268 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
269 return (DOSDrives
[drive
].root
&&
270 !(DOSDrives
[drive
].flags
& DRIVE_DISABLED
));
274 /***********************************************************************
275 * DRIVE_GetCurrentDrive
277 int DRIVE_GetCurrentDrive(void)
279 TDB
*pTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
280 if (pTask
&& (pTask
->curdrive
& 0x80)) return pTask
->curdrive
& ~0x80;
281 return DRIVE_CurDrive
;
285 /***********************************************************************
286 * DRIVE_SetCurrentDrive
288 int DRIVE_SetCurrentDrive( int drive
)
290 TDB
*pTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
291 if (!DRIVE_IsValid( drive
))
293 SetLastError( ERROR_INVALID_DRIVE
);
296 TRACE("%c:\n", 'A' + drive
);
297 DRIVE_CurDrive
= drive
;
298 if (pTask
) pTask
->curdrive
= drive
| 0x80;
303 /***********************************************************************
304 * DRIVE_FindDriveRoot
306 * Find a drive for which the root matches the beginning of the given path.
307 * This can be used to translate a Unix path into a drive + DOS path.
308 * Return value is the drive, or -1 on error. On success, path is modified
309 * to point to the beginning of the DOS path.
311 int DRIVE_FindDriveRoot( const char **path
)
313 /* idea: check at all '/' positions.
314 * If the device and inode of that path is identical with the
315 * device and inode of the current drive then we found a solution.
316 * If there is another drive pointing to a deeper position in
317 * the file tree, we want to find that one, not the earlier solution.
319 int drive
, rootdrive
= -1;
320 char buffer
[MAX_PATHNAME_LEN
];
322 const char *p
= *path
;
325 strcpy( buffer
, "/" );
328 if (stat( buffer
, &st
) || !S_ISDIR( st
.st_mode
)) break;
332 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
334 if (!DOSDrives
[drive
].root
||
335 (DOSDrives
[drive
].flags
& DRIVE_DISABLED
)) continue;
337 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
338 (DOSDrives
[drive
].ino
== st
.st_ino
))
346 /* Get the next path component */
349 while ((*p
== '/') || (*p
== '\\')) p
++;
351 while (!IS_END_OF_NAME(*p
)) *next
++ = *p
++;
357 TRACE("%s -> drive %c:, root='%s', name='%s'\n",
358 buffer
, 'A' + rootdrive
, DOSDrives
[rootdrive
].root
, *path
);
363 /***********************************************************************
366 const char * DRIVE_GetRoot( int drive
)
368 if (!DRIVE_IsValid( drive
)) return NULL
;
369 return DOSDrives
[drive
].root
;
373 /***********************************************************************
376 const char * DRIVE_GetDosCwd( int drive
)
378 TDB
*pTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
379 if (!DRIVE_IsValid( drive
)) return NULL
;
381 /* Check if we need to change the directory to the new task. */
382 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
383 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
384 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
386 /* Perform the task-switch */
387 if (!DRIVE_Chdir( drive
, pTask
->curdir
)) DRIVE_Chdir( drive
, "\\" );
388 DRIVE_LastTask
= GetCurrentTask();
390 return DOSDrives
[drive
].dos_cwd
;
394 /***********************************************************************
397 const char * DRIVE_GetUnixCwd( int drive
)
399 TDB
*pTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
400 if (!DRIVE_IsValid( drive
)) return NULL
;
402 /* Check if we need to change the directory to the new task. */
403 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
404 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
405 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
407 /* Perform the task-switch */
408 if (!DRIVE_Chdir( drive
, pTask
->curdir
)) DRIVE_Chdir( drive
, "\\" );
409 DRIVE_LastTask
= GetCurrentTask();
411 return DOSDrives
[drive
].unix_cwd
;
415 /***********************************************************************
418 const char * DRIVE_GetDevice( int drive
)
420 return (DRIVE_IsValid( drive
)) ? DOSDrives
[drive
].device
: NULL
;
424 /***********************************************************************
425 * DRIVE_ReadSuperblock
428 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
429 * to check, that they are writing on a FAT filesystem !
431 int DRIVE_ReadSuperblock (int drive
, char * buff
)
433 #define DRIVE_SUPER 96
437 if (memset(buff
,0,DRIVE_SUPER
)!=buff
) return -1;
438 if ((fd
=open(DOSDrives
[drive
].device
,O_RDONLY
)) == -1)
441 if (!DOSDrives
[drive
].device
)
442 ERR("No device configured for drive %c: !\n", 'A'+drive
);
444 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives
[drive
].device
, 'A'+drive
,
445 (stat(DOSDrives
[drive
].device
, &st
)) ?
446 "not available or symlink not valid ?" : "no permission");
447 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
448 PROFILE_UsageWineIni();
452 switch(DOSDrives
[drive
].type
)
459 /* FIXME: Maybe we should search for the first data track on the CD,
460 not just assume that it is the first track */
461 offs
= (off_t
)2048*(16+0);
468 if ((offs
) && (lseek(fd
,offs
,SEEK_SET
)!=offs
)) return -4;
469 if (read(fd
,buff
,DRIVE_SUPER
)!=DRIVE_SUPER
) return -2;
471 switch(DOSDrives
[drive
].type
)
475 if ((buff
[0x26]!=0x29) || /* Check for FAT present */
476 /* FIXME: do really all Fat have their name beginning with
477 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
478 memcmp( buff
+0x36,"FAT",3))
480 ERR("The filesystem is not FAT !! (device=%s)\n",
481 DOSDrives
[drive
].device
);
486 if (strncmp(&buff
[1],"CD001",5)) /* Check for iso9660 present */
488 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
499 /***********************************************************************
500 * DRIVE_WriteSuperblockEntry
503 * We are writing as little as possible (ie. not the whole SuperBlock)
504 * not to interfere with kernel. The drive can be mounted !
506 int DRIVE_WriteSuperblockEntry (int drive
, off_t ofs
, size_t len
, char * buff
)
510 if ((fd
=open(DOSDrives
[drive
].device
,O_WRONLY
))==-1)
512 ERR("Cannot open the device %s (for writing)\n",
513 DOSDrives
[drive
].device
);
516 if (lseek(fd
,ofs
,SEEK_SET
)!=ofs
)
518 ERR("lseek failed on device %s !\n",
519 DOSDrives
[drive
].device
);
523 if (write(fd
,buff
,len
)!=len
)
526 ERR("Cannot write on %s !\n",
527 DOSDrives
[drive
].device
);
535 /***********************************************************************
538 const char * DRIVE_GetLabel( int drive
)
541 char buff
[DRIVE_SUPER
];
544 if (!DRIVE_IsValid( drive
)) return NULL
;
545 if (DRIVE_GetType(drive
) == TYPE_CDROM
)
549 if (!(CDROM_Open(&wcda
, drive
)))
551 int media
= CDROM_GetMediaType(&wcda
);
553 if (media
== CDS_AUDIO
)
555 strcpy(DOSDrives
[drive
].label_read
, "Audio CD ");
559 if (media
== CDS_NO_INFO
)
561 strcpy(DOSDrives
[drive
].label_read
, " ");
568 if ((!read
) && (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
))
570 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
571 ERR("Invalid or unreadable superblock on %s (%c:).\n",
572 DOSDrives
[drive
].device
, (char)(drive
+'A'));
574 if (DOSDrives
[drive
].type
== TYPE_CDROM
)
577 if (DOSDrives
[drive
].type
== TYPE_FLOPPY
||
578 DOSDrives
[drive
].type
== TYPE_HD
)
581 /* FIXME: ISO9660 uses 32-bytes long label. Should we do also? */
582 if (offs
!= -1) memcpy(DOSDrives
[drive
].label_read
,buff
+offs
,11);
583 DOSDrives
[drive
].label_read
[11]='\0';
589 DOSDrives
[drive
].label_read
: DOSDrives
[drive
].label_conf
;
593 /***********************************************************************
594 * DRIVE_GetSerialNumber
596 DWORD
DRIVE_GetSerialNumber( int drive
)
599 char buff
[DRIVE_SUPER
];
601 if (!DRIVE_IsValid( drive
)) return 0;
603 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
605 switch(DOSDrives
[drive
].type
)
609 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
610 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
611 " Maybe not FAT?\n" ,
612 DOSDrives
[drive
].device
, 'A'+drive
);
614 serial
= *((DWORD
*)(buff
+0x27));
617 serial
= CDROM_GetSerial(drive
);
620 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive
+'A');
624 return (serial
) ? serial
: DOSDrives
[drive
].serial_conf
;
628 /***********************************************************************
629 * DRIVE_SetSerialNumber
631 int DRIVE_SetSerialNumber( int drive
, DWORD serial
)
633 char buff
[DRIVE_SUPER
];
635 if (!DRIVE_IsValid( drive
)) return 0;
637 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
639 if ((DOSDrives
[drive
].type
!= TYPE_FLOPPY
) &&
640 (DOSDrives
[drive
].type
!= TYPE_HD
)) return 0;
641 /* check, if the drive has a FAT filesystem */
642 if (DRIVE_ReadSuperblock(drive
, buff
)) return 0;
643 if (DRIVE_WriteSuperblockEntry(drive
, 0x27, 4, (char *) &serial
)) return 0;
647 if (DOSDrives
[drive
].type
== TYPE_CDROM
) return 0;
648 DOSDrives
[drive
].serial_conf
= serial
;
653 /***********************************************************************
656 DRIVETYPE
DRIVE_GetType( int drive
)
658 if (!DRIVE_IsValid( drive
)) return TYPE_INVALID
;
659 return DOSDrives
[drive
].type
;
663 /***********************************************************************
666 UINT
DRIVE_GetFlags( int drive
)
668 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
669 return DOSDrives
[drive
].flags
;
673 /***********************************************************************
676 int DRIVE_Chdir( int drive
, const char *path
)
678 DOS_FULL_NAME full_name
;
679 char buffer
[MAX_PATHNAME_LEN
];
681 BY_HANDLE_FILE_INFORMATION info
;
682 TDB
*pTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
684 strcpy( buffer
, "A:" );
686 TRACE("(%c:,%s)\n", buffer
[0], path
);
687 lstrcpynA( buffer
+ 2, path
, sizeof(buffer
) - 2 );
689 if (!DOSFS_GetFullName( buffer
, TRUE
, &full_name
)) return 0;
690 if (!FILE_Stat( full_name
.long_name
, &info
)) return 0;
691 if (!(info
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
693 SetLastError( ERROR_FILE_NOT_FOUND
);
696 unix_cwd
= full_name
.long_name
+ strlen( DOSDrives
[drive
].root
);
697 while (*unix_cwd
== '/') unix_cwd
++;
699 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
700 'A' + drive
, unix_cwd
, full_name
.short_name
+ 3 );
702 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].dos_cwd
);
703 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].unix_cwd
);
704 DOSDrives
[drive
].dos_cwd
= HEAP_strdupA( GetProcessHeap(), 0,
705 full_name
.short_name
+ 3 );
706 DOSDrives
[drive
].unix_cwd
= HEAP_strdupA( GetProcessHeap(), 0, unix_cwd
);
708 if (pTask
&& (pTask
->curdrive
& 0x80) &&
709 ((pTask
->curdrive
& ~0x80) == drive
))
711 lstrcpynA( pTask
->curdir
, full_name
.short_name
+ 2,
712 sizeof(pTask
->curdir
) );
713 DRIVE_LastTask
= GetCurrentTask();
719 /***********************************************************************
722 int DRIVE_Disable( int drive
)
724 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
726 SetLastError( ERROR_INVALID_DRIVE
);
729 DOSDrives
[drive
].flags
|= DRIVE_DISABLED
;
734 /***********************************************************************
737 int DRIVE_Enable( int drive
)
739 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
741 SetLastError( ERROR_INVALID_DRIVE
);
744 DOSDrives
[drive
].flags
&= ~DRIVE_DISABLED
;
749 /***********************************************************************
750 * DRIVE_SetLogicalMapping
752 int DRIVE_SetLogicalMapping ( int existing_drive
, int new_drive
)
754 /* If new_drive is already valid, do nothing and return 0
755 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
759 old
= DOSDrives
+ existing_drive
;
760 new = DOSDrives
+ new_drive
;
762 if ((existing_drive
< 0) || (existing_drive
>= MAX_DOS_DRIVES
) ||
764 (new_drive
< 0) || (new_drive
>= MAX_DOS_DRIVES
))
766 SetLastError( ERROR_INVALID_DRIVE
);
772 TRACE("Can\'t map drive %c to drive %c - drive %c already exists\n",
773 'A' + existing_drive
, 'A' + new_drive
, 'A' + new_drive
);
774 /* it is already mapped there, so return success */
775 if (!strcmp(old
->root
,new->root
))
780 new->root
= HEAP_strdupA( GetProcessHeap(), 0, old
->root
);
781 new->dos_cwd
= HEAP_strdupA( GetProcessHeap(), 0, old
->dos_cwd
);
782 new->unix_cwd
= HEAP_strdupA( GetProcessHeap(), 0, old
->unix_cwd
);
783 memcpy ( new->label_conf
, old
->label_conf
, 12 );
784 new->serial_conf
= old
->serial_conf
;
785 new->type
= old
->type
;
786 new->flags
= old
->flags
;
790 TRACE("Drive %c is now equal to drive %c\n",
791 'A' + new_drive
, 'A' + existing_drive
);
797 /***********************************************************************
800 * Open the drive raw device and return a Unix fd (or -1 on error).
802 int DRIVE_OpenDevice( int drive
, int flags
)
804 if (!DRIVE_IsValid( drive
)) return -1;
805 return open( DOSDrives
[drive
].device
, flags
);
809 /***********************************************************************
812 * Read raw sectors from a device
814 int DRIVE_RawRead(BYTE drive
, DWORD begin
, DWORD nr_sect
, BYTE
*dataptr
, BOOL fake_success
)
818 if ((fd
= DRIVE_OpenDevice( drive
, O_RDONLY
)) != -1)
820 lseek( fd
, begin
* 512, SEEK_SET
);
821 /* FIXME: check errors */
822 read( fd
, dataptr
, nr_sect
* 512 );
827 memset(dataptr
, 0, nr_sect
* 512);
830 if (begin
== 0 && nr_sect
> 1) *(dataptr
+ 512) = 0xf8;
831 if (begin
== 1) *dataptr
= 0xf8;
840 /***********************************************************************
843 * Write raw sectors to a device
845 int DRIVE_RawWrite(BYTE drive
, DWORD begin
, DWORD nr_sect
, BYTE
*dataptr
, BOOL fake_success
)
849 if ((fd
= DRIVE_OpenDevice( drive
, O_RDONLY
)) != -1)
851 lseek( fd
, begin
* 512, SEEK_SET
);
852 /* FIXME: check errors */
853 write( fd
, dataptr
, nr_sect
* 512 );
864 /***********************************************************************
867 static int DRIVE_GetFreeSpace( int drive
, PULARGE_INTEGER size
,
868 PULARGE_INTEGER available
)
871 unsigned long long bigsize
,bigavail
=0;
873 if (!DRIVE_IsValid(drive
))
875 SetLastError( ERROR_INVALID_DRIVE
);
879 /* FIXME: add autoconf check for this */
880 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
881 if (statfs( DOSDrives
[drive
].root
, &info
, 0, 0) < 0)
883 if (statfs( DOSDrives
[drive
].root
, &info
) < 0)
887 WARN("cannot do statfs(%s)\n", DOSDrives
[drive
].root
);
891 bigsize
= (unsigned long long)info
.f_bsize
892 * (unsigned long long)info
.f_blocks
;
893 #ifdef STATFS_HAS_BAVAIL
894 bigavail
= (unsigned long long)info
.f_bavail
895 * (unsigned long long)info
.f_bsize
;
897 # ifdef STATFS_HAS_BFREE
898 bigavail
= (unsigned long long)info
.f_bfree
899 * (unsigned long long)info
.f_bsize
;
901 # error "statfs has no bfree/bavail member!"
904 size
->s
.LowPart
= (DWORD
)bigsize
;
905 size
->s
.HighPart
= (DWORD
)(bigsize
>>32);
906 available
->s
.LowPart
= (DWORD
)bigavail
;
907 available
->s
.HighPart
= (DWORD
)(bigavail
>>32);
911 /***********************************************************************
912 * DRIVE_GetCurrentDirectory
913 * Returns "X:\\path\\etc\\".
915 * Despite the API description, return required length including the
916 * terminating null when buffer too small. This is the real behaviour.
919 static UINT
DRIVE_GetCurrentDirectory( UINT buflen
, LPSTR buf
)
922 const char *s
= DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
925 ret
= strlen(s
) + 3; /* length of WHOLE current directory */
926 if (ret
>= buflen
) return ret
+ 1;
927 lstrcpynA( buf
, "A:\\", min( 4, buflen
) );
928 if (buflen
) buf
[0] += DRIVE_GetCurrentDrive();
929 if (buflen
> 3) lstrcpynA( buf
+ 3, s
, buflen
- 3 );
933 /***********************************************************************
934 * GetDiskFreeSpace16 (KERNEL.422)
936 BOOL16 WINAPI
GetDiskFreeSpace16( LPCSTR root
, LPDWORD cluster_sectors
,
937 LPDWORD sector_bytes
, LPDWORD free_clusters
,
938 LPDWORD total_clusters
)
940 return GetDiskFreeSpaceA( root
, cluster_sectors
, sector_bytes
,
941 free_clusters
, total_clusters
);
945 /***********************************************************************
946 * GetDiskFreeSpaceA (KERNEL32.206)
948 * Fails if expression resulting from current drive's dir and "root"
949 * is not a root dir of the target drive.
951 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
952 * if the corresponding info is unneeded.
954 * FIXME: needs to support UNC names from Win95 OSR2 on.
956 * Behaviour under Win95a:
957 * CurrDir root result
958 * "E:\\TEST" "E:" FALSE
962 * "E:\\TEST" "\\" TRUE
963 * "E:\\TEST" ":\\" FALSE
964 * "E:\\TEST" "E:\\" TRUE
965 * "E:\\TEST" "" FALSE
966 * "E:\\" "" FALSE (!)
968 * "E:\\TEST" 0x0 TRUE (!)
969 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
970 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
972 BOOL WINAPI
GetDiskFreeSpaceA( LPCSTR root
, LPDWORD cluster_sectors
,
973 LPDWORD sector_bytes
, LPDWORD free_clusters
,
974 LPDWORD total_clusters
)
977 ULARGE_INTEGER size
,available
;
981 if ((!root
) || (strcmp(root
,"\\") == 0))
982 drive
= DRIVE_GetCurrentDrive();
984 if ( (strlen(root
) >= 2) && (root
[1] == ':')) /* root contains drive tag */
986 drive
= toupper(root
[0]) - 'A';
989 path
= DRIVE_GetDosCwd(drive
);
993 if (strlen(path
)) /* oops, we are in a subdir */
999 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1001 /* Cap the size and available at 2GB as per specs. */
1002 if ((size
.s
.HighPart
) ||(size
.s
.LowPart
> 0x7fffffff))
1004 size
.s
.HighPart
= 0;
1005 size
.s
.LowPart
= 0x7fffffff;
1007 if ((available
.s
.HighPart
) ||(available
.s
.LowPart
> 0x7fffffff))
1009 available
.s
.HighPart
=0;
1010 available
.s
.LowPart
= 0x7fffffff;
1012 if (DRIVE_GetType(drive
)==TYPE_CDROM
) {
1014 *sector_bytes
= 2048;
1015 size
.s
.LowPart
/= 2048;
1016 available
.s
.LowPart
/= 2048;
1019 *sector_bytes
= 512;
1020 size
.s
.LowPart
/= 512;
1021 available
.s
.LowPart
/= 512;
1023 /* fixme: probably have to adjust those variables too for CDFS */
1025 while (cluster_sec
* 65536 < size
.s
.LowPart
) cluster_sec
*= 2;
1027 if (cluster_sectors
)
1028 *cluster_sectors
= cluster_sec
;
1030 *free_clusters
= available
.s
.LowPart
/ cluster_sec
;
1032 *total_clusters
= size
.s
.LowPart
/ cluster_sec
;
1037 /***********************************************************************
1038 * GetDiskFreeSpaceW (KERNEL32.207)
1040 BOOL WINAPI
GetDiskFreeSpaceW( LPCWSTR root
, LPDWORD cluster_sectors
,
1041 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1042 LPDWORD total_clusters
)
1047 xroot
= HEAP_strdupWtoA( GetProcessHeap(), 0, root
);
1048 ret
= GetDiskFreeSpaceA( xroot
,cluster_sectors
, sector_bytes
,
1049 free_clusters
, total_clusters
);
1050 HeapFree( GetProcessHeap(), 0, xroot
);
1055 /***********************************************************************
1056 * GetDiskFreeSpaceExA (KERNEL32.871)
1058 * This function is used to aquire the size of the available and
1059 * total space on a logical volume.
1063 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1064 * detailed error information.
1067 BOOL WINAPI
GetDiskFreeSpaceExA( LPCSTR root
,
1068 PULARGE_INTEGER avail
,
1069 PULARGE_INTEGER total
,
1070 PULARGE_INTEGER totalfree
)
1073 ULARGE_INTEGER size
,available
;
1075 if (!root
) drive
= DRIVE_GetCurrentDrive();
1078 if ((root
[1]) && ((root
[1] != ':') || (root
[2] != '\\')))
1080 FIXME("there are valid root names which are not supported yet\n");
1081 /* ..like UNC names, for instance. */
1083 WARN("invalid root '%s'\n", root
);
1086 drive
= toupper(root
[0]) - 'A';
1089 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1093 total
->s
.HighPart
= size
.s
.HighPart
;
1094 total
->s
.LowPart
= size
.s
.LowPart
;
1099 totalfree
->s
.HighPart
= available
.s
.HighPart
;
1100 totalfree
->s
.LowPart
= available
.s
.LowPart
;
1105 if (FIXME_ON(dosfs
))
1107 /* On Windows2000, we need to check the disk quota
1108 allocated for the user owning the calling process. We
1109 don't want to be more obtrusive than necessary with the
1110 FIXME messages, so don't print the FIXME unless Wine is
1111 actually masquerading as Windows2000. */
1114 ovi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOA
);
1115 if (GetVersionExA(&ovi
))
1117 if (ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
&& ovi
.dwMajorVersion
> 4)
1118 FIXME("no per-user quota support yet\n");
1122 /* Quick hack, should eventually be fixed to work 100% with
1123 Windows2000 (see comment above). */
1124 avail
->s
.HighPart
= available
.s
.HighPart
;
1125 avail
->s
.LowPart
= available
.s
.LowPart
;
1131 /***********************************************************************
1132 * GetDiskFreeSpaceExW (KERNEL32.873)
1134 BOOL WINAPI
GetDiskFreeSpaceExW( LPCWSTR root
, PULARGE_INTEGER avail
,
1135 PULARGE_INTEGER total
,
1136 PULARGE_INTEGER totalfree
)
1141 xroot
= HEAP_strdupWtoA( GetProcessHeap(), 0, root
);
1142 ret
= GetDiskFreeSpaceExA( xroot
, avail
, total
, totalfree
);
1143 HeapFree( GetProcessHeap(), 0, xroot
);
1147 /***********************************************************************
1148 * GetDriveType16 (KERNEL.136)
1149 * This function returns the type of a drive in Win16.
1150 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1151 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1152 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1153 * do any pseudo-clever changes.
1156 * drivetype DRIVE_xxx
1158 UINT16 WINAPI
GetDriveType16(
1159 UINT16 drive
/* [in] number (NOT letter) of drive */
1161 TRACE("(%c:)\n", 'A' + drive
);
1162 switch(DRIVE_GetType(drive
))
1164 case TYPE_FLOPPY
: return DRIVE_REMOVABLE
;
1165 case TYPE_HD
: return DRIVE_FIXED
;
1166 case TYPE_CDROM
: return DRIVE_REMOTE
;
1167 case TYPE_NETWORK
: return DRIVE_REMOTE
;
1169 default: return DRIVE_CANNOTDETERMINE
;
1174 /***********************************************************************
1175 * GetDriveTypeA (KERNEL32.208)
1177 * Returns the type of the disk drive specified. If root is NULL the
1178 * root of the current directory is used.
1182 * Type of drive (from Win32 SDK):
1184 * DRIVE_UNKNOWN unable to find out anything about the drive
1185 * DRIVE_NO_ROOT_DIR nonexistand root dir
1186 * DRIVE_REMOVABLE the disk can be removed from the machine
1187 * DRIVE_FIXED the disk can not be removed from the machine
1188 * DRIVE_REMOTE network disk
1189 * DRIVE_CDROM CDROM drive
1190 * DRIVE_RAMDISK virtual disk in ram
1192 * DRIVE_DOESNOTEXIST XXX Not valid return value
1193 * DRIVE_CANNOTDETERMINE XXX Not valid return value
1197 * Currently returns DRIVE_DOESNOTEXIST and DRIVE_CANNOTDETERMINE
1198 * when it really should return DRIVE_NO_ROOT_DIR and DRIVE_UNKNOWN.
1199 * Why were the former defines used?
1201 * DRIVE_RAMDISK is unsupported.
1203 UINT WINAPI
GetDriveTypeA(LPCSTR root
/* String describing drive */)
1206 TRACE("(%s)\n", debugstr_a(root
));
1208 if (NULL
== root
) drive
= DRIVE_GetCurrentDrive();
1211 if ((root
[1]) && (root
[1] != ':'))
1213 WARN("invalid root '%s'\n", debugstr_a(root
));
1214 return DRIVE_DOESNOTEXIST
;
1216 drive
= toupper(root
[0]) - 'A';
1218 switch(DRIVE_GetType(drive
))
1220 case TYPE_FLOPPY
: return DRIVE_REMOVABLE
;
1221 case TYPE_HD
: return DRIVE_FIXED
;
1222 case TYPE_CDROM
: return DRIVE_CDROM
;
1223 case TYPE_NETWORK
: return DRIVE_REMOTE
;
1224 case TYPE_INVALID
: return DRIVE_DOESNOTEXIST
;
1225 default: return DRIVE_CANNOTDETERMINE
;
1230 /***********************************************************************
1231 * GetDriveTypeW (KERNEL32.209)
1233 UINT WINAPI
GetDriveTypeW( LPCWSTR root
)
1235 LPSTR xpath
= HEAP_strdupWtoA( GetProcessHeap(), 0, root
);
1236 UINT ret
= GetDriveTypeA( xpath
);
1237 HeapFree( GetProcessHeap(), 0, xpath
);
1242 /***********************************************************************
1243 * GetCurrentDirectory16 (KERNEL.411)
1245 UINT16 WINAPI
GetCurrentDirectory16( UINT16 buflen
, LPSTR buf
)
1247 return (UINT16
)DRIVE_GetCurrentDirectory(buflen
, buf
);
1251 /***********************************************************************
1252 * GetCurrentDirectoryA (KERNEL32.196)
1254 UINT WINAPI
GetCurrentDirectoryA( UINT buflen
, LPSTR buf
)
1257 char longname
[MAX_PATHNAME_LEN
];
1258 char shortname
[MAX_PATHNAME_LEN
];
1259 ret
= DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN
, shortname
);
1260 if ( ret
> MAX_PATHNAME_LEN
) {
1261 ERR_(file
)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret
);
1264 GetLongPathNameA(shortname
, longname
, MAX_PATHNAME_LEN
);
1265 ret
= lstrlenA( longname
) + 1;
1266 if (ret
> buflen
) return ret
;
1267 lstrcpyA(buf
, longname
);
1271 /***********************************************************************
1272 * GetCurrentDirectoryW (KERNEL32.197)
1274 UINT WINAPI
GetCurrentDirectoryW( UINT buflen
, LPWSTR buf
)
1276 LPSTR xpath
= HeapAlloc( GetProcessHeap(), 0, buflen
+1 );
1277 UINT ret
= GetCurrentDirectoryA( buflen
, xpath
);
1278 if (ret
< buflen
) lstrcpyAtoW ( buf
, xpath
);
1279 HeapFree( GetProcessHeap(), 0, xpath
);
1284 /***********************************************************************
1285 * SetCurrentDirectory (KERNEL.412)
1287 BOOL16 WINAPI
SetCurrentDirectory16( LPCSTR dir
)
1289 return SetCurrentDirectoryA( dir
);
1293 /***********************************************************************
1294 * SetCurrentDirectoryA (KERNEL32.479)
1296 BOOL WINAPI
SetCurrentDirectoryA( LPCSTR dir
)
1298 int olddrive
, drive
= DRIVE_GetCurrentDrive();
1301 ERR_(file
)("(NULL)!\n");
1304 if (dir
[0] && (dir
[1]==':'))
1306 drive
= tolower( *dir
) - 'a';
1310 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1311 sets pTask->curdir only if pTask->curdrive is drive */
1312 olddrive
= drive
; /* in case DRIVE_Chdir fails */
1313 if (!(DRIVE_SetCurrentDrive( drive
)))
1315 /* FIXME: what about empty strings? Add a \\ ? */
1316 if (!DRIVE_Chdir( drive
, dir
)) {
1317 DRIVE_SetCurrentDrive(olddrive
);
1324 /***********************************************************************
1325 * SetCurrentDirectoryW (KERNEL32.480)
1327 BOOL WINAPI
SetCurrentDirectoryW( LPCWSTR dirW
)
1329 LPSTR dir
= HEAP_strdupWtoA( GetProcessHeap(), 0, dirW
);
1330 BOOL res
= SetCurrentDirectoryA( dir
);
1331 HeapFree( GetProcessHeap(), 0, dir
);
1336 /***********************************************************************
1337 * GetLogicalDriveStringsA (KERNEL32.231)
1339 UINT WINAPI
GetLogicalDriveStringsA( UINT len
, LPSTR buffer
)
1343 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1344 if (DRIVE_IsValid(drive
)) count
++;
1345 if ((count
* 4) + 1 <= len
)
1348 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1349 if (DRIVE_IsValid(drive
))
1360 return (count
* 4) + 1;/* account for terminating null */
1361 /* The API tells about these different return values */
1365 /***********************************************************************
1366 * GetLogicalDriveStringsW (KERNEL32.232)
1368 UINT WINAPI
GetLogicalDriveStringsW( UINT len
, LPWSTR buffer
)
1372 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1373 if (DRIVE_IsValid(drive
)) count
++;
1374 if (count
* 4 * sizeof(WCHAR
) <= len
)
1377 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1378 if (DRIVE_IsValid(drive
))
1380 *p
++ = (WCHAR
)('a' + drive
);
1387 return count
* 4 * sizeof(WCHAR
);
1391 /***********************************************************************
1392 * GetLogicalDrives (KERNEL32.233)
1394 DWORD WINAPI
GetLogicalDrives(void)
1399 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1401 if ( (DRIVE_IsValid(drive
)) ||
1402 (DOSDrives
[drive
].type
== TYPE_CDROM
)) /* audio CD is also valid */
1403 ret
|= (1 << drive
);
1409 /***********************************************************************
1410 * GetVolumeInformationA (KERNEL32.309)
1412 BOOL WINAPI
GetVolumeInformationA( LPCSTR root
, LPSTR label
,
1413 DWORD label_len
, DWORD
*serial
,
1414 DWORD
*filename_len
, DWORD
*flags
,
1415 LPSTR fsname
, DWORD fsname_len
)
1420 /* FIXME, SetLastError()s missing */
1422 if (!root
) drive
= DRIVE_GetCurrentDrive();
1425 if ((root
[1]) && (root
[1] != ':'))
1427 WARN("invalid root '%s'\n",root
);
1430 drive
= toupper(root
[0]) - 'A';
1432 if (!DRIVE_IsValid( drive
)) return FALSE
;
1435 lstrcpynA( label
, DRIVE_GetLabel(drive
), label_len
);
1436 for (cp
= label
; *cp
; cp
++);
1437 while (cp
!= label
&& *(cp
-1) == ' ') cp
--;
1440 if (serial
) *serial
= DRIVE_GetSerialNumber(drive
);
1442 /* Set the filesystem information */
1443 /* Note: we only emulate a FAT fs at present */
1446 if (DOSDrives
[drive
].flags
& DRIVE_SHORT_NAMES
)
1449 *filename_len
= 255;
1454 if (DOSDrives
[drive
].flags
& DRIVE_CASE_SENSITIVE
)
1455 *flags
|=FS_CASE_SENSITIVE
;
1456 if (DOSDrives
[drive
].flags
& DRIVE_CASE_PRESERVING
)
1457 *flags
|=FS_CASE_IS_PRESERVED
;
1460 /* Diablo checks that return code ... */
1461 if (DRIVE_GetType(drive
)==TYPE_CDROM
)
1462 lstrcpynA( fsname
, "CDFS", fsname_len
);
1464 lstrcpynA( fsname
, "FAT", fsname_len
);
1470 /***********************************************************************
1471 * GetVolumeInformationW (KERNEL32.310)
1473 BOOL WINAPI
GetVolumeInformationW( LPCWSTR root
, LPWSTR label
,
1474 DWORD label_len
, DWORD
*serial
,
1475 DWORD
*filename_len
, DWORD
*flags
,
1476 LPWSTR fsname
, DWORD fsname_len
)
1478 LPSTR xroot
= HEAP_strdupWtoA( GetProcessHeap(), 0, root
);
1479 LPSTR xvolname
= label
? HeapAlloc(GetProcessHeap(),0,label_len
) : NULL
;
1480 LPSTR xfsname
= fsname
? HeapAlloc(GetProcessHeap(),0,fsname_len
) : NULL
;
1481 BOOL ret
= GetVolumeInformationA( xroot
, xvolname
, label_len
, serial
,
1482 filename_len
, flags
, xfsname
,
1486 if (label
) lstrcpyAtoW( label
, xvolname
);
1487 if (fsname
) lstrcpyAtoW( fsname
, xfsname
);
1489 HeapFree( GetProcessHeap(), 0, xroot
);
1490 HeapFree( GetProcessHeap(), 0, xvolname
);
1491 HeapFree( GetProcessHeap(), 0, xfsname
);
1495 /***********************************************************************
1496 * SetVolumeLabelA (KERNEL32.675)
1498 BOOL WINAPI
SetVolumeLabelA( LPCSTR root
, LPCSTR volname
)
1502 /* FIXME, SetLastErrors missing */
1504 if (!root
) drive
= DRIVE_GetCurrentDrive();
1507 if ((root
[1]) && (root
[1] != ':'))
1509 WARN("invalid root '%s'\n",root
);
1512 drive
= toupper(root
[0]) - 'A';
1514 if (!DRIVE_IsValid( drive
)) return FALSE
;
1516 /* some copy protection stuff check this */
1517 if (DRIVE_GetType( drive
) == TYPE_CDROM
) return FALSE
;
1519 FIXME("(%s,%s),stub!\n", root
, volname
);
1523 /***********************************************************************
1524 * SetVolumeLabelW (KERNEL32.676)
1526 BOOL WINAPI
SetVolumeLabelW(LPCWSTR rootpath
,LPCWSTR volname
)
1531 xroot
= HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath
);
1532 xvol
= HEAP_strdupWtoA( GetProcessHeap(), 0, volname
);
1533 ret
= SetVolumeLabelA( xroot
, xvol
);
1534 HeapFree( GetProcessHeap(), 0, xroot
);
1535 HeapFree( GetProcessHeap(), 0, xvol
);