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].device
= NULL
;
243 DOSDrives
[2].flags
= 0;
247 /* Make sure the current drive is valid */
248 if (DRIVE_CurDrive
== -1)
250 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
252 if (drive
->root
&& !(drive
->flags
& DRIVE_DISABLED
))
264 /***********************************************************************
267 int DRIVE_IsValid( int drive
)
269 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
270 return (DOSDrives
[drive
].root
&&
271 !(DOSDrives
[drive
].flags
& DRIVE_DISABLED
));
275 /***********************************************************************
276 * DRIVE_GetCurrentDrive
278 int DRIVE_GetCurrentDrive(void)
280 TDB
*pTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
281 if (pTask
&& (pTask
->curdrive
& 0x80)) return pTask
->curdrive
& ~0x80;
282 return DRIVE_CurDrive
;
286 /***********************************************************************
287 * DRIVE_SetCurrentDrive
289 int DRIVE_SetCurrentDrive( int drive
)
291 TDB
*pTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
292 if (!DRIVE_IsValid( drive
))
294 SetLastError( ERROR_INVALID_DRIVE
);
297 TRACE("%c:\n", 'A' + drive
);
298 DRIVE_CurDrive
= drive
;
299 if (pTask
) pTask
->curdrive
= drive
| 0x80;
304 /***********************************************************************
305 * DRIVE_FindDriveRoot
307 * Find a drive for which the root matches the beginning of the given path.
308 * This can be used to translate a Unix path into a drive + DOS path.
309 * Return value is the drive, or -1 on error. On success, path is modified
310 * to point to the beginning of the DOS path.
312 int DRIVE_FindDriveRoot( const char **path
)
314 /* idea: check at all '/' positions.
315 * If the device and inode of that path is identical with the
316 * device and inode of the current drive then we found a solution.
317 * If there is another drive pointing to a deeper position in
318 * the file tree, we want to find that one, not the earlier solution.
320 int drive
, rootdrive
= -1;
321 char buffer
[MAX_PATHNAME_LEN
];
323 const char *p
= *path
;
326 strcpy( buffer
, "/" );
329 if (stat( buffer
, &st
) || !S_ISDIR( st
.st_mode
)) break;
333 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
335 if (!DOSDrives
[drive
].root
||
336 (DOSDrives
[drive
].flags
& DRIVE_DISABLED
)) continue;
338 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
339 (DOSDrives
[drive
].ino
== st
.st_ino
))
347 /* Get the next path component */
350 while ((*p
== '/') || (*p
== '\\')) p
++;
352 while (!IS_END_OF_NAME(*p
)) *next
++ = *p
++;
358 TRACE("%s -> drive %c:, root='%s', name='%s'\n",
359 buffer
, 'A' + rootdrive
, DOSDrives
[rootdrive
].root
, *path
);
364 /***********************************************************************
367 const char * DRIVE_GetRoot( int drive
)
369 if (!DRIVE_IsValid( drive
)) return NULL
;
370 return DOSDrives
[drive
].root
;
374 /***********************************************************************
377 const char * DRIVE_GetDosCwd( int drive
)
379 TDB
*pTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
380 if (!DRIVE_IsValid( drive
)) return NULL
;
382 /* Check if we need to change the directory to the new task. */
383 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
384 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
385 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
387 /* Perform the task-switch */
388 if (!DRIVE_Chdir( drive
, pTask
->curdir
)) DRIVE_Chdir( drive
, "\\" );
389 DRIVE_LastTask
= GetCurrentTask();
391 return DOSDrives
[drive
].dos_cwd
;
395 /***********************************************************************
398 const char * DRIVE_GetUnixCwd( int drive
)
400 TDB
*pTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
401 if (!DRIVE_IsValid( drive
)) return NULL
;
403 /* Check if we need to change the directory to the new task. */
404 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
405 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
406 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
408 /* Perform the task-switch */
409 if (!DRIVE_Chdir( drive
, pTask
->curdir
)) DRIVE_Chdir( drive
, "\\" );
410 DRIVE_LastTask
= GetCurrentTask();
412 return DOSDrives
[drive
].unix_cwd
;
416 /***********************************************************************
419 const char * DRIVE_GetDevice( int drive
)
421 return (DRIVE_IsValid( drive
)) ? DOSDrives
[drive
].device
: NULL
;
425 /***********************************************************************
426 * DRIVE_ReadSuperblock
429 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
430 * to check, that they are writing on a FAT filesystem !
432 int DRIVE_ReadSuperblock (int drive
, char * buff
)
434 #define DRIVE_SUPER 96
438 if (memset(buff
,0,DRIVE_SUPER
)!=buff
) return -1;
439 if ((fd
=open(DOSDrives
[drive
].device
,O_RDONLY
)) == -1)
442 if (!DOSDrives
[drive
].device
)
443 ERR("No device configured for drive %c: !\n", 'A'+drive
);
445 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives
[drive
].device
, 'A'+drive
,
446 (stat(DOSDrives
[drive
].device
, &st
)) ?
447 "not available or symlink not valid ?" : "no permission");
448 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
449 PROFILE_UsageWineIni();
453 switch(DOSDrives
[drive
].type
)
460 offs
= CDROM_Data_FindBestVoldesc(fd
);
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
)
546 read
= CDROM_GetLabel(drive
, DOSDrives
[drive
].label_read
);
549 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
551 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
552 ERR("Invalid or unreadable superblock on %s (%c:).\n",
553 DOSDrives
[drive
].device
, (char)(drive
+'A'));
555 if (DOSDrives
[drive
].type
== TYPE_FLOPPY
||
556 DOSDrives
[drive
].type
== TYPE_HD
)
559 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
560 if (offs
!= -1) memcpy(DOSDrives
[drive
].label_read
,buff
+offs
,11);
561 DOSDrives
[drive
].label_read
[11]='\0';
567 DOSDrives
[drive
].label_read
: DOSDrives
[drive
].label_conf
;
571 /***********************************************************************
572 * DRIVE_GetSerialNumber
574 DWORD
DRIVE_GetSerialNumber( int drive
)
577 char buff
[DRIVE_SUPER
];
579 if (!DRIVE_IsValid( drive
)) return 0;
581 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
583 switch(DOSDrives
[drive
].type
)
587 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
588 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
589 " Maybe not FAT?\n" ,
590 DOSDrives
[drive
].device
, 'A'+drive
);
592 serial
= *((DWORD
*)(buff
+0x27));
595 serial
= CDROM_GetSerial(drive
);
598 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive
+'A');
602 return (serial
) ? serial
: DOSDrives
[drive
].serial_conf
;
606 /***********************************************************************
607 * DRIVE_SetSerialNumber
609 int DRIVE_SetSerialNumber( int drive
, DWORD serial
)
611 char buff
[DRIVE_SUPER
];
613 if (!DRIVE_IsValid( drive
)) return 0;
615 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
617 if ((DOSDrives
[drive
].type
!= TYPE_FLOPPY
) &&
618 (DOSDrives
[drive
].type
!= TYPE_HD
)) return 0;
619 /* check, if the drive has a FAT filesystem */
620 if (DRIVE_ReadSuperblock(drive
, buff
)) return 0;
621 if (DRIVE_WriteSuperblockEntry(drive
, 0x27, 4, (char *) &serial
)) return 0;
625 if (DOSDrives
[drive
].type
== TYPE_CDROM
) return 0;
626 DOSDrives
[drive
].serial_conf
= serial
;
631 /***********************************************************************
634 DRIVETYPE
DRIVE_GetType( int drive
)
636 if (!DRIVE_IsValid( drive
)) return TYPE_INVALID
;
637 return DOSDrives
[drive
].type
;
641 /***********************************************************************
644 UINT
DRIVE_GetFlags( int drive
)
646 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
647 return DOSDrives
[drive
].flags
;
651 /***********************************************************************
654 int DRIVE_Chdir( int drive
, const char *path
)
656 DOS_FULL_NAME full_name
;
657 char buffer
[MAX_PATHNAME_LEN
];
659 BY_HANDLE_FILE_INFORMATION info
;
660 TDB
*pTask
= (TDB
*)GlobalLock16( GetCurrentTask() );
662 strcpy( buffer
, "A:" );
664 TRACE("(%s,%s)\n", buffer
, path
);
665 lstrcpynA( buffer
+ 2, path
, sizeof(buffer
) - 2 );
667 if (!DOSFS_GetFullName( buffer
, TRUE
, &full_name
)) return 0;
668 if (!FILE_Stat( full_name
.long_name
, &info
)) return 0;
669 if (!(info
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
671 SetLastError( ERROR_FILE_NOT_FOUND
);
674 unix_cwd
= full_name
.long_name
+ strlen( DOSDrives
[drive
].root
);
675 while (*unix_cwd
== '/') unix_cwd
++;
677 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
678 'A' + drive
, unix_cwd
, full_name
.short_name
+ 3 );
680 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].dos_cwd
);
681 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].unix_cwd
);
682 DOSDrives
[drive
].dos_cwd
= HEAP_strdupA( GetProcessHeap(), 0,
683 full_name
.short_name
+ 3 );
684 DOSDrives
[drive
].unix_cwd
= HEAP_strdupA( GetProcessHeap(), 0, unix_cwd
);
686 if (pTask
&& (pTask
->curdrive
& 0x80) &&
687 ((pTask
->curdrive
& ~0x80) == drive
))
689 lstrcpynA( pTask
->curdir
, full_name
.short_name
+ 2,
690 sizeof(pTask
->curdir
) );
691 DRIVE_LastTask
= GetCurrentTask();
697 /***********************************************************************
700 int DRIVE_Disable( int drive
)
702 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
704 SetLastError( ERROR_INVALID_DRIVE
);
707 DOSDrives
[drive
].flags
|= DRIVE_DISABLED
;
712 /***********************************************************************
715 int DRIVE_Enable( int drive
)
717 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
719 SetLastError( ERROR_INVALID_DRIVE
);
722 DOSDrives
[drive
].flags
&= ~DRIVE_DISABLED
;
727 /***********************************************************************
728 * DRIVE_SetLogicalMapping
730 int DRIVE_SetLogicalMapping ( int existing_drive
, int new_drive
)
732 /* If new_drive is already valid, do nothing and return 0
733 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
737 old
= DOSDrives
+ existing_drive
;
738 new = DOSDrives
+ new_drive
;
740 if ((existing_drive
< 0) || (existing_drive
>= MAX_DOS_DRIVES
) ||
742 (new_drive
< 0) || (new_drive
>= MAX_DOS_DRIVES
))
744 SetLastError( ERROR_INVALID_DRIVE
);
750 TRACE("Can't map drive %c: to already existing drive %c:\n",
751 'A' + existing_drive
, 'A' + new_drive
);
752 /* it is already mapped there, so return success */
753 if (!strcmp(old
->root
,new->root
))
758 new->root
= HEAP_strdupA( GetProcessHeap(), 0, old
->root
);
759 new->dos_cwd
= HEAP_strdupA( GetProcessHeap(), 0, old
->dos_cwd
);
760 new->unix_cwd
= HEAP_strdupA( GetProcessHeap(), 0, old
->unix_cwd
);
761 new->device
= HEAP_strdupA( GetProcessHeap(), 0, old
->device
);
762 memcpy ( new->label_conf
, old
->label_conf
, 12 );
763 memcpy ( new->label_read
, old
->label_read
, 12 );
764 new->serial_conf
= old
->serial_conf
;
765 new->type
= old
->type
;
766 new->flags
= old
->flags
;
770 TRACE("Drive %c: is now equal to drive %c:\n",
771 'A' + new_drive
, 'A' + existing_drive
);
777 /***********************************************************************
780 * Open the drive raw device and return a Unix fd (or -1 on error).
782 int DRIVE_OpenDevice( int drive
, int flags
)
784 if (!DRIVE_IsValid( drive
)) return -1;
785 return open( DOSDrives
[drive
].device
, flags
);
789 /***********************************************************************
792 * Read raw sectors from a device
794 int DRIVE_RawRead(BYTE drive
, DWORD begin
, DWORD nr_sect
, BYTE
*dataptr
, BOOL fake_success
)
798 if ((fd
= DRIVE_OpenDevice( drive
, O_RDONLY
)) != -1)
800 lseek( fd
, begin
* 512, SEEK_SET
);
801 /* FIXME: check errors */
802 read( fd
, dataptr
, nr_sect
* 512 );
807 memset(dataptr
, 0, nr_sect
* 512);
810 if (begin
== 0 && nr_sect
> 1) *(dataptr
+ 512) = 0xf8;
811 if (begin
== 1) *dataptr
= 0xf8;
820 /***********************************************************************
823 * Write raw sectors to a device
825 int DRIVE_RawWrite(BYTE drive
, DWORD begin
, DWORD nr_sect
, BYTE
*dataptr
, BOOL fake_success
)
829 if ((fd
= DRIVE_OpenDevice( drive
, O_RDONLY
)) != -1)
831 lseek( fd
, begin
* 512, SEEK_SET
);
832 /* FIXME: check errors */
833 write( fd
, dataptr
, nr_sect
* 512 );
844 /***********************************************************************
847 static int DRIVE_GetFreeSpace( int drive
, PULARGE_INTEGER size
,
848 PULARGE_INTEGER available
)
851 unsigned long long bigsize
,bigavail
=0;
853 if (!DRIVE_IsValid(drive
))
855 SetLastError( ERROR_INVALID_DRIVE
);
859 /* FIXME: add autoconf check for this */
860 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
861 if (statfs( DOSDrives
[drive
].root
, &info
, 0, 0) < 0)
863 if (statfs( DOSDrives
[drive
].root
, &info
) < 0)
867 WARN("cannot do statfs(%s)\n", DOSDrives
[drive
].root
);
871 bigsize
= (unsigned long long)info
.f_bsize
872 * (unsigned long long)info
.f_blocks
;
873 #ifdef STATFS_HAS_BAVAIL
874 bigavail
= (unsigned long long)info
.f_bavail
875 * (unsigned long long)info
.f_bsize
;
877 # ifdef STATFS_HAS_BFREE
878 bigavail
= (unsigned long long)info
.f_bfree
879 * (unsigned long long)info
.f_bsize
;
881 # error "statfs has no bfree/bavail member!"
884 size
->s
.LowPart
= (DWORD
)bigsize
;
885 size
->s
.HighPart
= (DWORD
)(bigsize
>>32);
886 if (DRIVE_GetType(drive
) == TYPE_CDROM
)
887 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
888 available
->s
.LowPart
= 0;
889 available
->s
.HighPart
= 0;
893 available
->s
.LowPart
= (DWORD
)bigavail
;
894 available
->s
.HighPart
= (DWORD
)(bigavail
>>32);
899 /***********************************************************************
900 * DRIVE_GetCurrentDirectory
901 * Returns "X:\\path\\etc\\".
903 * Despite the API description, return required length including the
904 * terminating null when buffer too small. This is the real behaviour.
907 static UINT
DRIVE_GetCurrentDirectory( UINT buflen
, LPSTR buf
)
910 const char *s
= DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
913 ret
= strlen(s
) + 3; /* length of WHOLE current directory */
914 if (ret
>= buflen
) return ret
+ 1;
915 lstrcpynA( buf
, "A:\\", min( 4, buflen
) );
916 if (buflen
) buf
[0] += DRIVE_GetCurrentDrive();
917 if (buflen
> 3) lstrcpynA( buf
+ 3, s
, buflen
- 3 );
921 /***********************************************************************
922 * GetDiskFreeSpace16 (KERNEL.422)
924 BOOL16 WINAPI
GetDiskFreeSpace16( LPCSTR root
, LPDWORD cluster_sectors
,
925 LPDWORD sector_bytes
, LPDWORD free_clusters
,
926 LPDWORD total_clusters
)
928 return GetDiskFreeSpaceA( root
, cluster_sectors
, sector_bytes
,
929 free_clusters
, total_clusters
);
933 /***********************************************************************
934 * GetDiskFreeSpaceA (KERNEL32.206)
936 * Fails if expression resulting from current drive's dir and "root"
937 * is not a root dir of the target drive.
939 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
940 * if the corresponding info is unneeded.
942 * FIXME: needs to support UNC names from Win95 OSR2 on.
944 * Behaviour under Win95a:
945 * CurrDir root result
946 * "E:\\TEST" "E:" FALSE
950 * "E:\\TEST" "\\" TRUE
951 * "E:\\TEST" ":\\" FALSE
952 * "E:\\TEST" "E:\\" TRUE
953 * "E:\\TEST" "" FALSE
954 * "E:\\" "" FALSE (!)
956 * "E:\\TEST" 0x0 TRUE (!)
957 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
958 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
960 BOOL WINAPI
GetDiskFreeSpaceA( LPCSTR root
, LPDWORD cluster_sectors
,
961 LPDWORD sector_bytes
, LPDWORD free_clusters
,
962 LPDWORD total_clusters
)
965 ULARGE_INTEGER size
,available
;
969 if ((!root
) || (strcmp(root
,"\\") == 0))
970 drive
= DRIVE_GetCurrentDrive();
972 if ( (strlen(root
) >= 2) && (root
[1] == ':')) /* root contains drive tag */
974 drive
= toupper(root
[0]) - 'A';
977 path
= DRIVE_GetDosCwd(drive
);
981 if (strlen(path
)) /* oops, we are in a subdir */
987 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
989 /* Cap the size and available at 2GB as per specs. */
990 if ((size
.s
.HighPart
) ||(size
.s
.LowPart
> 0x7fffffff))
993 size
.s
.LowPart
= 0x7fffffff;
995 if ((available
.s
.HighPart
) ||(available
.s
.LowPart
> 0x7fffffff))
997 available
.s
.HighPart
=0;
998 available
.s
.LowPart
= 0x7fffffff;
1000 sec_size
= (DRIVE_GetType(drive
)==TYPE_CDROM
) ? 2048 : 512;
1001 size
.s
.LowPart
/= sec_size
;
1002 available
.s
.LowPart
/= sec_size
;
1003 /* fixme: probably have to adjust those variables too for CDFS */
1005 while (cluster_sec
* 65536 < size
.s
.LowPart
) cluster_sec
*= 2;
1007 if (cluster_sectors
)
1008 *cluster_sectors
= cluster_sec
;
1010 *sector_bytes
= sec_size
;
1012 *free_clusters
= available
.s
.LowPart
/ cluster_sec
;
1014 *total_clusters
= size
.s
.LowPart
/ cluster_sec
;
1019 /***********************************************************************
1020 * GetDiskFreeSpaceW (KERNEL32.207)
1022 BOOL WINAPI
GetDiskFreeSpaceW( LPCWSTR root
, LPDWORD cluster_sectors
,
1023 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1024 LPDWORD total_clusters
)
1029 xroot
= HEAP_strdupWtoA( GetProcessHeap(), 0, root
);
1030 ret
= GetDiskFreeSpaceA( xroot
,cluster_sectors
, sector_bytes
,
1031 free_clusters
, total_clusters
);
1032 HeapFree( GetProcessHeap(), 0, xroot
);
1037 /***********************************************************************
1038 * GetDiskFreeSpaceExA (KERNEL32.871)
1040 * This function is used to aquire the size of the available and
1041 * total space on a logical volume.
1045 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1046 * detailed error information.
1049 BOOL WINAPI
GetDiskFreeSpaceExA( LPCSTR root
,
1050 PULARGE_INTEGER avail
,
1051 PULARGE_INTEGER total
,
1052 PULARGE_INTEGER totalfree
)
1055 ULARGE_INTEGER size
,available
;
1057 if (!root
) drive
= DRIVE_GetCurrentDrive();
1060 if ((root
[1]) && ((root
[1] != ':') || (root
[2] != '\\')))
1062 FIXME("there are valid root names which are not supported yet\n");
1063 /* ..like UNC names, for instance. */
1065 WARN("invalid root '%s'\n", root
);
1068 drive
= toupper(root
[0]) - 'A';
1071 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1075 total
->s
.HighPart
= size
.s
.HighPart
;
1076 total
->s
.LowPart
= size
.s
.LowPart
;
1081 totalfree
->s
.HighPart
= available
.s
.HighPart
;
1082 totalfree
->s
.LowPart
= available
.s
.LowPart
;
1087 if (FIXME_ON(dosfs
))
1089 /* On Windows2000, we need to check the disk quota
1090 allocated for the user owning the calling process. We
1091 don't want to be more obtrusive than necessary with the
1092 FIXME messages, so don't print the FIXME unless Wine is
1093 actually masquerading as Windows2000. */
1096 ovi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOA
);
1097 if (GetVersionExA(&ovi
))
1099 if (ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
&& ovi
.dwMajorVersion
> 4)
1100 FIXME("no per-user quota support yet\n");
1104 /* Quick hack, should eventually be fixed to work 100% with
1105 Windows2000 (see comment above). */
1106 avail
->s
.HighPart
= available
.s
.HighPart
;
1107 avail
->s
.LowPart
= available
.s
.LowPart
;
1113 /***********************************************************************
1114 * GetDiskFreeSpaceExW (KERNEL32.873)
1116 BOOL WINAPI
GetDiskFreeSpaceExW( LPCWSTR root
, PULARGE_INTEGER avail
,
1117 PULARGE_INTEGER total
,
1118 PULARGE_INTEGER totalfree
)
1123 xroot
= HEAP_strdupWtoA( GetProcessHeap(), 0, root
);
1124 ret
= GetDiskFreeSpaceExA( xroot
, avail
, total
, totalfree
);
1125 HeapFree( GetProcessHeap(), 0, xroot
);
1129 /***********************************************************************
1130 * GetDriveType16 (KERNEL.136)
1131 * This function returns the type of a drive in Win16.
1132 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1133 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1134 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1135 * do any pseudo-clever changes.
1138 * drivetype DRIVE_xxx
1140 UINT16 WINAPI
GetDriveType16(
1141 UINT16 drive
/* [in] number (NOT letter) of drive */
1143 TRACE("(%c:)\n", 'A' + drive
);
1144 switch(DRIVE_GetType(drive
))
1146 case TYPE_FLOPPY
: return DRIVE_REMOVABLE
;
1147 case TYPE_HD
: return DRIVE_FIXED
;
1148 case TYPE_CDROM
: return DRIVE_REMOTE
;
1149 case TYPE_NETWORK
: return DRIVE_REMOTE
;
1151 default: return DRIVE_CANNOTDETERMINE
;
1156 /***********************************************************************
1157 * GetDriveTypeA (KERNEL32.208)
1159 * Returns the type of the disk drive specified. If root is NULL the
1160 * root of the current directory is used.
1164 * Type of drive (from Win32 SDK):
1166 * DRIVE_UNKNOWN unable to find out anything about the drive
1167 * DRIVE_NO_ROOT_DIR nonexistent root dir
1168 * DRIVE_REMOVABLE the disk can be removed from the machine
1169 * DRIVE_FIXED the disk can not be removed from the machine
1170 * DRIVE_REMOTE network disk
1171 * DRIVE_CDROM CDROM drive
1172 * DRIVE_RAMDISK virtual disk in RAM
1174 * DRIVE_DOESNOTEXIST FIXME Not valid return value
1175 * DRIVE_CANNOTDETERMINE FIXME Not valid return value
1179 * Currently returns DRIVE_DOESNOTEXIST and DRIVE_CANNOTDETERMINE
1180 * when it really should return DRIVE_NO_ROOT_DIR and DRIVE_UNKNOWN.
1181 * Why were the former defines used?
1183 * DRIVE_RAMDISK is unsupported.
1185 UINT WINAPI
GetDriveTypeA(LPCSTR root
/* String describing drive */)
1188 TRACE("(%s)\n", debugstr_a(root
));
1190 if (NULL
== root
) drive
= DRIVE_GetCurrentDrive();
1193 if ((root
[1]) && (root
[1] != ':'))
1195 WARN("invalid root '%s'\n", debugstr_a(root
));
1196 return DRIVE_DOESNOTEXIST
;
1198 drive
= toupper(root
[0]) - 'A';
1200 switch(DRIVE_GetType(drive
))
1202 case TYPE_FLOPPY
: return DRIVE_REMOVABLE
;
1203 case TYPE_HD
: return DRIVE_FIXED
;
1204 case TYPE_CDROM
: return DRIVE_CDROM
;
1205 case TYPE_NETWORK
: return DRIVE_REMOTE
;
1206 case TYPE_INVALID
: return DRIVE_DOESNOTEXIST
;
1207 default: return DRIVE_CANNOTDETERMINE
;
1212 /***********************************************************************
1213 * GetDriveTypeW (KERNEL32.209)
1215 UINT WINAPI
GetDriveTypeW( LPCWSTR root
)
1217 LPSTR xpath
= HEAP_strdupWtoA( GetProcessHeap(), 0, root
);
1218 UINT ret
= GetDriveTypeA( xpath
);
1219 HeapFree( GetProcessHeap(), 0, xpath
);
1224 /***********************************************************************
1225 * GetCurrentDirectory16 (KERNEL.411)
1227 UINT16 WINAPI
GetCurrentDirectory16( UINT16 buflen
, LPSTR buf
)
1229 return (UINT16
)DRIVE_GetCurrentDirectory(buflen
, buf
);
1233 /***********************************************************************
1234 * GetCurrentDirectoryA (KERNEL32.196)
1236 UINT WINAPI
GetCurrentDirectoryA( UINT buflen
, LPSTR buf
)
1239 char longname
[MAX_PATHNAME_LEN
];
1240 char shortname
[MAX_PATHNAME_LEN
];
1241 ret
= DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN
, shortname
);
1242 if ( ret
> MAX_PATHNAME_LEN
) {
1243 ERR_(file
)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret
);
1246 GetLongPathNameA(shortname
, longname
, MAX_PATHNAME_LEN
);
1247 ret
= strlen( longname
) + 1;
1248 if (ret
> buflen
) return ret
;
1249 strcpy(buf
, longname
);
1253 /***********************************************************************
1254 * GetCurrentDirectoryW (KERNEL32.197)
1256 UINT WINAPI
GetCurrentDirectoryW( UINT buflen
, LPWSTR buf
)
1258 LPSTR xpath
= HeapAlloc( GetProcessHeap(), 0, buflen
+1 );
1259 UINT ret
= GetCurrentDirectoryA( buflen
, xpath
);
1260 if (ret
< buflen
) lstrcpyAtoW ( buf
, xpath
);
1261 HeapFree( GetProcessHeap(), 0, xpath
);
1266 /***********************************************************************
1267 * SetCurrentDirectory (KERNEL.412)
1269 BOOL16 WINAPI
SetCurrentDirectory16( LPCSTR dir
)
1271 return SetCurrentDirectoryA( dir
);
1275 /***********************************************************************
1276 * SetCurrentDirectoryA (KERNEL32.479)
1278 BOOL WINAPI
SetCurrentDirectoryA( LPCSTR dir
)
1280 int drive
, olddrive
= DRIVE_GetCurrentDrive();
1283 ERR_(file
)("(NULL)!\n");
1286 if (dir
[0] && (dir
[1]==':'))
1288 drive
= toupper( *dir
) - 'A';
1294 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1295 sets pTask->curdir only if pTask->curdrive is drive */
1296 if (!(DRIVE_SetCurrentDrive( drive
)))
1298 /* FIXME: what about empty strings? Add a \\ ? */
1299 if (!DRIVE_Chdir( drive
, dir
)) {
1300 DRIVE_SetCurrentDrive(olddrive
);
1307 /***********************************************************************
1308 * SetCurrentDirectoryW (KERNEL32.480)
1310 BOOL WINAPI
SetCurrentDirectoryW( LPCWSTR dirW
)
1312 LPSTR dir
= HEAP_strdupWtoA( GetProcessHeap(), 0, dirW
);
1313 BOOL res
= SetCurrentDirectoryA( dir
);
1314 HeapFree( GetProcessHeap(), 0, dir
);
1319 /***********************************************************************
1320 * GetLogicalDriveStringsA (KERNEL32.231)
1322 UINT WINAPI
GetLogicalDriveStringsA( UINT len
, LPSTR buffer
)
1326 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1327 if (DRIVE_IsValid(drive
)) count
++;
1328 if ((count
* 4) + 1 <= len
)
1331 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1332 if (DRIVE_IsValid(drive
))
1343 return (count
* 4) + 1; /* account for terminating null */
1344 /* The API tells about these different return values */
1348 /***********************************************************************
1349 * GetLogicalDriveStringsW (KERNEL32.232)
1351 UINT WINAPI
GetLogicalDriveStringsW( UINT len
, LPWSTR buffer
)
1355 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1356 if (DRIVE_IsValid(drive
)) count
++;
1357 if (count
* 4 * sizeof(WCHAR
) <= len
)
1360 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1361 if (DRIVE_IsValid(drive
))
1363 *p
++ = (WCHAR
)('a' + drive
);
1370 return count
* 4 * sizeof(WCHAR
);
1374 /***********************************************************************
1375 * GetLogicalDrives (KERNEL32.233)
1377 DWORD WINAPI
GetLogicalDrives(void)
1382 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1384 if ( (DRIVE_IsValid(drive
)) ||
1385 (DOSDrives
[drive
].type
== TYPE_CDROM
)) /* audio CD is also valid */
1386 ret
|= (1 << drive
);
1392 /***********************************************************************
1393 * GetVolumeInformationA (KERNEL32.309)
1395 BOOL WINAPI
GetVolumeInformationA( LPCSTR root
, LPSTR label
,
1396 DWORD label_len
, DWORD
*serial
,
1397 DWORD
*filename_len
, DWORD
*flags
,
1398 LPSTR fsname
, DWORD fsname_len
)
1403 /* FIXME, SetLastError()s missing */
1405 if (!root
) drive
= DRIVE_GetCurrentDrive();
1408 if ((root
[1]) && (root
[1] != ':'))
1410 WARN("invalid root '%s'\n",root
);
1413 drive
= toupper(root
[0]) - 'A';
1415 if (!DRIVE_IsValid( drive
)) return FALSE
;
1418 lstrcpynA( label
, DRIVE_GetLabel(drive
), label_len
);
1419 for (cp
= label
; *cp
; cp
++);
1420 while (cp
!= label
&& *(cp
-1) == ' ') cp
--;
1423 if (serial
) *serial
= DRIVE_GetSerialNumber(drive
);
1425 /* Set the filesystem information */
1426 /* Note: we only emulate a FAT fs at present */
1429 if (DOSDrives
[drive
].flags
& DRIVE_SHORT_NAMES
)
1432 *filename_len
= 255;
1437 if (DOSDrives
[drive
].flags
& DRIVE_CASE_SENSITIVE
)
1438 *flags
|=FS_CASE_SENSITIVE
;
1439 if (DOSDrives
[drive
].flags
& DRIVE_CASE_PRESERVING
)
1440 *flags
|=FS_CASE_IS_PRESERVED
;
1443 /* Diablo checks that return code ... */
1444 if (DRIVE_GetType(drive
)==TYPE_CDROM
)
1445 lstrcpynA( fsname
, "CDFS", fsname_len
);
1447 lstrcpynA( fsname
, "FAT", fsname_len
);
1453 /***********************************************************************
1454 * GetVolumeInformationW (KERNEL32.310)
1456 BOOL WINAPI
GetVolumeInformationW( LPCWSTR root
, LPWSTR label
,
1457 DWORD label_len
, DWORD
*serial
,
1458 DWORD
*filename_len
, DWORD
*flags
,
1459 LPWSTR fsname
, DWORD fsname_len
)
1461 LPSTR xroot
= HEAP_strdupWtoA( GetProcessHeap(), 0, root
);
1462 LPSTR xvolname
= label
? HeapAlloc(GetProcessHeap(),0,label_len
) : NULL
;
1463 LPSTR xfsname
= fsname
? HeapAlloc(GetProcessHeap(),0,fsname_len
) : NULL
;
1464 BOOL ret
= GetVolumeInformationA( xroot
, xvolname
, label_len
, serial
,
1465 filename_len
, flags
, xfsname
,
1469 if (label
) lstrcpyAtoW( label
, xvolname
);
1470 if (fsname
) lstrcpyAtoW( fsname
, xfsname
);
1472 HeapFree( GetProcessHeap(), 0, xroot
);
1473 HeapFree( GetProcessHeap(), 0, xvolname
);
1474 HeapFree( GetProcessHeap(), 0, xfsname
);
1478 /***********************************************************************
1479 * SetVolumeLabelA (KERNEL32.675)
1481 BOOL WINAPI
SetVolumeLabelA( LPCSTR root
, LPCSTR volname
)
1485 /* FIXME, SetLastErrors missing */
1487 if (!root
) drive
= DRIVE_GetCurrentDrive();
1490 if ((root
[1]) && (root
[1] != ':'))
1492 WARN("invalid root '%s'\n",root
);
1495 drive
= toupper(root
[0]) - 'A';
1497 if (!DRIVE_IsValid( drive
)) return FALSE
;
1499 /* some copy protection stuff check this */
1500 if (DRIVE_GetType( drive
) == TYPE_CDROM
) return FALSE
;
1502 FIXME("(%s,%s),stub!\n", root
, volname
);
1506 /***********************************************************************
1507 * SetVolumeLabelW (KERNEL32.676)
1509 BOOL WINAPI
SetVolumeLabelW(LPCWSTR rootpath
,LPCWSTR volname
)
1514 xroot
= HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath
);
1515 xvol
= HEAP_strdupWtoA( GetProcessHeap(), 0, volname
);
1516 ret
= SetVolumeLabelA( xroot
, xvol
);
1517 HeapFree( GetProcessHeap(), 0, xroot
);
1518 HeapFree( GetProcessHeap(), 0, xvol
);