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)
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include "wine/port.h"
34 #include <sys/types.h>
40 #ifdef HAVE_SYS_PARAM_H
41 # include <sys/param.h>
43 #ifdef STATFS_DEFINED_BY_SYS_VFS
46 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
47 # include <sys/mount.h>
49 # ifdef STATFS_DEFINED_BY_SYS_STATFS
50 # include <sys/statfs.h>
57 #include "wine/winbase16.h" /* for GetCurrentTask */
65 #include "wine/debug.h"
66 #include "wine/server.h"
71 WINE_DEFAULT_DEBUG_CHANNEL(dosfs
);
72 WINE_DECLARE_DEBUG_CHANNEL(file
);
76 char *root
; /* root dir in Unix format without trailing / */
77 char *dos_cwd
; /* cwd in DOS format without leading or trailing \ */
78 char *unix_cwd
; /* cwd in Unix format without leading or trailing / */
79 char *device
; /* raw device path */
80 char label_conf
[12]; /* drive label as cfg'd in wine config */
81 char label_read
[12]; /* drive label as read from device */
82 DWORD serial_conf
; /* drive serial number as cfg'd in wine config */
83 UINT type
; /* drive type */
84 UINT flags
; /* drive flags */
85 dev_t dev
; /* unix device number */
86 ino_t ino
; /* unix inode number */
90 static const char * const DRIVE_Types
[] =
92 "", /* DRIVE_UNKNOWN */
93 "", /* DRIVE_NO_ROOT_DIR */
94 "floppy", /* DRIVE_REMOVABLE */
95 "hd", /* DRIVE_FIXED */
96 "network", /* DRIVE_REMOTE */
97 "cdrom", /* DRIVE_CDROM */
98 "ramdisk" /* DRIVE_RAMDISK */
102 /* Known filesystem types */
110 static const FS_DESCR DRIVE_Filesystems
[] =
112 { "unix", DRIVE_CASE_SENSITIVE
| DRIVE_CASE_PRESERVING
},
113 { "msdos", DRIVE_SHORT_NAMES
},
114 { "dos", DRIVE_SHORT_NAMES
},
115 { "fat", DRIVE_SHORT_NAMES
},
116 { "vfat", DRIVE_CASE_PRESERVING
},
117 { "win95", DRIVE_CASE_PRESERVING
},
122 static DOSDRIVE DOSDrives
[MAX_DOS_DRIVES
];
123 static int DRIVE_CurDrive
= -1;
125 static HTASK16 DRIVE_LastTask
= 0;
127 /* strdup on the process heap */
128 inline static char *heap_strdup( const char *str
)
130 INT len
= strlen(str
) + 1;
131 LPSTR p
= HeapAlloc( GetProcessHeap(), 0, len
);
132 if (p
) memcpy( p
, str
, len
);
136 /***********************************************************************
139 static UINT
DRIVE_GetDriveType( const char *name
)
144 PROFILE_GetWineIniString( name
, "Type", "hd", buffer
, sizeof(buffer
) );
145 for (i
= 0; i
< sizeof(DRIVE_Types
)/sizeof(DRIVE_Types
[0]); i
++)
147 if (!strcasecmp( buffer
, DRIVE_Types
[i
] )) return i
;
149 MESSAGE("%s: unknown drive type '%s', defaulting to 'hd'.\n",
155 /***********************************************************************
158 static UINT
DRIVE_GetFSFlags( const char *name
, const char *value
)
160 const FS_DESCR
*descr
;
162 for (descr
= DRIVE_Filesystems
; descr
->name
; descr
++)
163 if (!strcasecmp( value
, descr
->name
)) return descr
->flags
;
164 MESSAGE("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
166 return DRIVE_CASE_PRESERVING
;
170 /***********************************************************************
175 int i
, len
, count
= 0;
176 char name
[] = "Drive A";
177 char drive_env
[] = "=A:";
178 char path
[MAX_PATHNAME_LEN
];
180 struct stat drive_stat_buffer
;
184 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, name
[6]++, drive
++)
186 PROFILE_GetWineIniString( name
, "Path", "", path
, sizeof(path
)-1 );
189 p
= path
+ strlen(path
) - 1;
190 while ((p
> path
) && (*p
== '/')) *p
-- = '\0';
194 drive
->root
= heap_strdup( path
);
198 /* relative paths are relative to config dir */
199 const char *config
= get_config_dir();
200 drive
->root
= HeapAlloc( GetProcessHeap(), 0, strlen(config
) + strlen(path
) + 2 );
201 sprintf( drive
->root
, "%s/%s", config
, path
);
204 if (stat( drive
->root
, &drive_stat_buffer
))
206 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
207 drive
->root
, strerror(errno
), 'A' + i
);
208 HeapFree( GetProcessHeap(), 0, drive
->root
);
212 if (!S_ISDIR(drive_stat_buffer
.st_mode
))
214 MESSAGE("%s is not a directory, ignoring drive %c:\n",
215 drive
->root
, 'A' + i
);
216 HeapFree( GetProcessHeap(), 0, drive
->root
);
221 drive
->dos_cwd
= heap_strdup( "" );
222 drive
->unix_cwd
= heap_strdup( "" );
223 drive
->type
= DRIVE_GetDriveType( name
);
224 drive
->device
= NULL
;
226 drive
->dev
= drive_stat_buffer
.st_dev
;
227 drive
->ino
= drive_stat_buffer
.st_ino
;
229 /* Get the drive label */
230 PROFILE_GetWineIniString( name
, "Label", "", drive
->label_conf
, 12 );
231 if ((len
= strlen(drive
->label_conf
)) < 11)
233 /* Pad label with spaces */
234 memset( drive
->label_conf
+ len
, ' ', 11 - len
);
235 drive
->label_conf
[11] = '\0';
238 /* Get the serial number */
239 PROFILE_GetWineIniString( name
, "Serial", "12345678",
240 buffer
, sizeof(buffer
) );
241 drive
->serial_conf
= strtoul( buffer
, NULL
, 16 );
243 /* Get the filesystem type */
244 PROFILE_GetWineIniString( name
, "Filesystem", "win95",
245 buffer
, sizeof(buffer
) );
246 drive
->flags
= DRIVE_GetFSFlags( name
, buffer
);
249 PROFILE_GetWineIniString( name
, "Device", "",
250 buffer
, sizeof(buffer
) );
253 drive
->device
= heap_strdup( buffer
);
254 if (PROFILE_GetWineIniBool( name
, "ReadVolInfo", 1))
255 drive
->flags
|= DRIVE_READ_VOL_INFO
;
258 /* Get the FailReadOnly flag */
259 if (PROFILE_GetWineIniBool( name
, "FailReadOnly", 0 ))
260 drive
->flags
|= DRIVE_FAIL_READ_ONLY
;
262 /* Make the first hard disk the current drive */
263 if ((DRIVE_CurDrive
== -1) && (drive
->type
== DRIVE_FIXED
))
267 TRACE("%s: path=%s type=%s label='%s' serial=%08lx "
268 "flags=%08x dev=%x ino=%x\n",
269 name
, drive
->root
, DRIVE_Types
[drive
->type
],
270 drive
->label_conf
, drive
->serial_conf
, drive
->flags
,
271 (int)drive
->dev
, (int)drive
->ino
);
273 else WARN("%s: not defined\n", name
);
278 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
279 /* Create a C drive pointing to Unix root dir */
280 DOSDrives
[2].root
= heap_strdup( "/" );
281 DOSDrives
[2].dos_cwd
= heap_strdup( "" );
282 DOSDrives
[2].unix_cwd
= heap_strdup( "" );
283 strcpy( DOSDrives
[2].label_conf
, "Drive C " );
284 DOSDrives
[2].serial_conf
= 12345678;
285 DOSDrives
[2].type
= DRIVE_FIXED
;
286 DOSDrives
[2].device
= NULL
;
287 DOSDrives
[2].flags
= 0;
291 /* Make sure the current drive is valid */
292 if (DRIVE_CurDrive
== -1)
294 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
296 if (drive
->root
&& !(drive
->flags
& DRIVE_DISABLED
))
304 /* get current working directory info for all drives */
305 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++, drive_env
[1]++)
307 if (!GetEnvironmentVariableA(drive_env
, path
, sizeof(path
))) continue;
309 if (toupper(path
[0]) != drive_env
[1] || path
[1] != ':') continue;
310 DRIVE_Chdir( i
, path
+ 2 );
316 /***********************************************************************
319 int DRIVE_IsValid( int drive
)
321 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
322 return (DOSDrives
[drive
].root
&&
323 !(DOSDrives
[drive
].flags
& DRIVE_DISABLED
));
327 /***********************************************************************
328 * DRIVE_GetCurrentDrive
330 int DRIVE_GetCurrentDrive(void)
332 TDB
*pTask
= TASK_GetCurrent();
333 if (pTask
&& (pTask
->curdrive
& 0x80)) return pTask
->curdrive
& ~0x80;
334 return DRIVE_CurDrive
;
338 /***********************************************************************
339 * DRIVE_SetCurrentDrive
341 int DRIVE_SetCurrentDrive( int drive
)
343 TDB
*pTask
= TASK_GetCurrent();
344 if (!DRIVE_IsValid( drive
))
346 SetLastError( ERROR_INVALID_DRIVE
);
349 TRACE("%c:\n", 'A' + drive
);
350 DRIVE_CurDrive
= drive
;
351 if (pTask
) pTask
->curdrive
= drive
| 0x80;
352 chdir(DRIVE_GetUnixCwd(drive
));
357 /***********************************************************************
358 * DRIVE_FindDriveRoot
360 * Find a drive for which the root matches the beginning of the given path.
361 * This can be used to translate a Unix path into a drive + DOS path.
362 * Return value is the drive, or -1 on error. On success, path is modified
363 * to point to the beginning of the DOS path.
365 int DRIVE_FindDriveRoot( const char **path
)
367 /* idea: check at all '/' positions.
368 * If the device and inode of that path is identical with the
369 * device and inode of the current drive then we found a solution.
370 * If there is another drive pointing to a deeper position in
371 * the file tree, we want to find that one, not the earlier solution.
373 int drive
, rootdrive
= -1;
374 char buffer
[MAX_PATHNAME_LEN
];
376 const char *p
= *path
;
379 strcpy( buffer
, "/" );
382 if (stat( buffer
, &st
) || !S_ISDIR( st
.st_mode
)) break;
386 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
388 if (!DOSDrives
[drive
].root
||
389 (DOSDrives
[drive
].flags
& DRIVE_DISABLED
)) continue;
391 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
392 (DOSDrives
[drive
].ino
== st
.st_ino
))
400 /* Get the next path component */
403 while ((*p
== '/') || (*p
== '\\')) p
++;
405 while (!IS_END_OF_NAME(*p
)) *next
++ = *p
++;
411 TRACE("%s -> drive %c:, root='%s', name='%s'\n",
412 buffer
, 'A' + rootdrive
, DOSDrives
[rootdrive
].root
, *path
);
417 /***********************************************************************
420 const char * DRIVE_GetRoot( int drive
)
422 if (!DRIVE_IsValid( drive
)) return NULL
;
423 return DOSDrives
[drive
].root
;
427 /***********************************************************************
430 const char * DRIVE_GetDosCwd( int drive
)
432 TDB
*pTask
= TASK_GetCurrent();
433 if (!DRIVE_IsValid( drive
)) return NULL
;
435 /* Check if we need to change the directory to the new task. */
436 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
437 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
438 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
440 /* Perform the task-switch */
441 if (!DRIVE_Chdir( drive
, pTask
->curdir
)) DRIVE_Chdir( drive
, "\\" );
442 DRIVE_LastTask
= GetCurrentTask();
444 return DOSDrives
[drive
].dos_cwd
;
448 /***********************************************************************
451 const char * DRIVE_GetUnixCwd( int drive
)
453 TDB
*pTask
= TASK_GetCurrent();
454 if (!DRIVE_IsValid( drive
)) return NULL
;
456 /* Check if we need to change the directory to the new task. */
457 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
458 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
459 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
461 /* Perform the task-switch */
462 if (!DRIVE_Chdir( drive
, pTask
->curdir
)) DRIVE_Chdir( drive
, "\\" );
463 DRIVE_LastTask
= GetCurrentTask();
465 return DOSDrives
[drive
].unix_cwd
;
469 /***********************************************************************
472 const char * DRIVE_GetDevice( int drive
)
474 return (DRIVE_IsValid( drive
)) ? DOSDrives
[drive
].device
: NULL
;
477 /******************************************************************
478 * static WORD CDROM_Data_FindBestVoldesc
482 static WORD
CDROM_Data_FindBestVoldesc(int fd
)
484 BYTE cur_vd_type
, max_vd_type
= 0;
485 unsigned int offs
, best_offs
= 0, extra_offs
= 0;
488 for (offs
= 0x8000; offs
<= 0x9800; offs
+= 0x800)
490 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
491 * the volume label is displaced forward by 8
493 lseek(fd
, offs
+ 11, SEEK_SET
); /* check for non-ISO9660 signature */
495 if ((sig
[0] == 'R') && (sig
[1] == 'O') && (sig
[2]=='M'))
499 lseek(fd
, offs
+ extra_offs
, SEEK_SET
);
500 read(fd
, &cur_vd_type
, 1);
501 if (cur_vd_type
== 0xff) /* voldesc set terminator */
503 if (cur_vd_type
> max_vd_type
)
505 max_vd_type
= cur_vd_type
;
506 best_offs
= offs
+ extra_offs
;
512 /***********************************************************************
513 * DRIVE_ReadSuperblock
516 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
517 * to check, that they are writing on a FAT filesystem !
519 int DRIVE_ReadSuperblock (int drive
, char * buff
)
521 #define DRIVE_SUPER 96
525 if (memset(buff
,0,DRIVE_SUPER
)!=buff
) return -1;
526 if ((fd
=open(DOSDrives
[drive
].device
,O_RDONLY
)) == -1)
529 if (!DOSDrives
[drive
].device
)
530 ERR("No device configured for drive %c: !\n", 'A'+drive
);
532 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives
[drive
].device
, 'A'+drive
,
533 (stat(DOSDrives
[drive
].device
, &st
)) ?
534 "not available or symlink not valid ?" : "no permission");
535 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
536 PROFILE_UsageWineIni();
540 switch(DOSDrives
[drive
].type
)
542 case DRIVE_REMOVABLE
:
547 offs
= CDROM_Data_FindBestVoldesc(fd
);
554 if ((offs
) && (lseek(fd
,offs
,SEEK_SET
)!=offs
)) return -4;
555 if (read(fd
,buff
,DRIVE_SUPER
)!=DRIVE_SUPER
) return -2;
557 switch(DOSDrives
[drive
].type
)
559 case DRIVE_REMOVABLE
:
561 if ((buff
[0x26]!=0x29) || /* Check for FAT present */
562 /* FIXME: do really all FAT have their name beginning with
563 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
564 memcmp( buff
+0x36,"FAT",3))
566 ERR("The filesystem is not FAT !! (device=%s)\n",
567 DOSDrives
[drive
].device
);
572 if (strncmp(&buff
[1],"CD001",5)) /* Check for iso9660 present */
574 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
585 /***********************************************************************
586 * DRIVE_WriteSuperblockEntry
589 * We are writing as little as possible (ie. not the whole SuperBlock)
590 * not to interfere with kernel. The drive can be mounted !
592 int DRIVE_WriteSuperblockEntry (int drive
, off_t ofs
, size_t len
, char * buff
)
596 if ((fd
=open(DOSDrives
[drive
].device
,O_WRONLY
))==-1)
598 ERR("Cannot open the device %s (for writing)\n",
599 DOSDrives
[drive
].device
);
602 if (lseek(fd
,ofs
,SEEK_SET
)!=ofs
)
604 ERR("lseek failed on device %s !\n",
605 DOSDrives
[drive
].device
);
609 if (write(fd
,buff
,len
)!=len
)
612 ERR("Cannot write on %s !\n",
613 DOSDrives
[drive
].device
);
619 /******************************************************************
620 * static HANDLE CDROM_Open
624 static HANDLE
CDROM_Open(int drive
)
628 strcpy(root
, "\\\\.\\A:");
631 return CreateFileA(root
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, 0);
634 /**************************************************************************
635 * CDROM_Data_GetLabel [internal]
637 DWORD
CDROM_Data_GetLabel(int drive
, char *label
)
639 #define LABEL_LEN 32+1
640 int dev
= open(DOSDrives
[drive
].device
, O_RDONLY
|O_NONBLOCK
);
641 WORD offs
= CDROM_Data_FindBestVoldesc(dev
);
642 WCHAR label_read
[LABEL_LEN
]; /* Unicode possible, too */
643 DWORD unicode_id
= 0;
647 if ((lseek(dev
, offs
+0x58, SEEK_SET
) == offs
+0x58)
648 && (read(dev
, &unicode_id
, 3) == 3))
650 int ver
= (unicode_id
& 0xff0000) >> 16;
652 if ((lseek(dev
, offs
+0x28, SEEK_SET
) != offs
+0x28)
653 || (read(dev
, &label_read
, LABEL_LEN
) != LABEL_LEN
))
657 if ((LOWORD(unicode_id
) == 0x2f25) /* Unicode ID */
658 && ((ver
== 0x40) || (ver
== 0x43) || (ver
== 0x45)))
659 { /* yippee, unicode */
662 for (i
=0; i
<LABEL_LEN
;i
++)
663 { /* Motorola -> Intel Unicode conversion :-\ */
665 label_read
[i
] = (ch
<< 8) | (ch
>> 8);
667 WideCharToMultiByte( CP_ACP
, 0, label_read
, -1, label
, 12, NULL
, NULL
);
672 strncpy(label
, (LPSTR
)label_read
, 11);
680 ERR("error reading label !\n");
684 /**************************************************************************
685 * CDROM_GetLabel [internal]
687 static DWORD
CDROM_GetLabel(int drive
, char *label
)
689 HANDLE h
= CDROM_Open(drive
);
694 if (!h
|| !DeviceIoControl(h
, IOCTL_CDROM_DISK_TYPE
, NULL
, 0, &cdd
, sizeof(cdd
), &br
, 0))
697 switch (cdd
.DiskData
& 0x03)
699 case CDROM_DISK_DATA_TRACK
:
700 if (!CDROM_Data_GetLabel(drive
, label
))
703 case CDROM_DISK_AUDIO_TRACK
:
704 strcpy(label
, "Audio CD ");
706 case CDROM_DISK_DATA_TRACK
|CDROM_DISK_AUDIO_TRACK
:
707 FIXME("Need to get the label of a mixed mode CD: not implemented yet !\n");
713 TRACE("CD: label is '%s'.\n", label
);
717 /***********************************************************************
720 const char * DRIVE_GetLabel( int drive
)
723 char buff
[DRIVE_SUPER
];
726 if (!DRIVE_IsValid( drive
)) return NULL
;
727 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
729 read
= CDROM_GetLabel(drive
, DOSDrives
[drive
].label_read
);
732 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
734 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
735 ERR("Invalid or unreadable superblock on %s (%c:).\n",
736 DOSDrives
[drive
].device
, (char)(drive
+'A'));
738 if (DOSDrives
[drive
].type
== DRIVE_REMOVABLE
||
739 DOSDrives
[drive
].type
== DRIVE_FIXED
)
742 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
743 if (offs
!= -1) memcpy(DOSDrives
[drive
].label_read
,buff
+offs
,11);
744 DOSDrives
[drive
].label_read
[11]='\0';
750 DOSDrives
[drive
].label_read
: DOSDrives
[drive
].label_conf
;
753 #define CDFRAMES_PERSEC 75
754 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
755 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
756 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
758 /**************************************************************************
759 * CDROM_Audio_GetSerial [internal]
761 static DWORD
CDROM_Audio_GetSerial(HANDLE h
)
763 unsigned long serial
= 0;
766 DWORD dwStart
, dwEnd
, br
;
769 if (!DeviceIoControl(h
, IOCTL_CDROM_READ_TOC
, NULL
, 0, &toc
, sizeof(toc
), &br
, 0))
773 * wMagic collects the wFrames from track 1
774 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
776 * There it is collected for correcting the serial when there are less than
779 wMagic
= toc
.TrackData
[0].Address
[2];
780 dwStart
= FRAME_OF_TOC(toc
, toc
.FirstTrack
);
782 for (i
= 0; i
<= toc
.LastTrack
- toc
.FirstTrack
; i
++) {
783 serial
+= (toc
.TrackData
[i
].Address
[0] << 16) |
784 (toc
.TrackData
[i
].Address
[1] << 8) | toc
.TrackData
[i
].Address
[2];
786 dwEnd
= FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1);
788 if (toc
.LastTrack
- toc
.FirstTrack
+ 1 < 3)
789 serial
+= wMagic
+ (dwEnd
- dwStart
);
794 /**************************************************************************
795 * CDROM_Data_GetSerial [internal]
797 static DWORD
CDROM_Data_GetSerial(int drive
)
799 int dev
= open(DOSDrives
[drive
].device
, O_RDONLY
|O_NONBLOCK
);
805 BYTE b0
= 0, b1
= 1, b2
= 2, b3
= 3;
808 if (dev
== -1) return 0;
809 offs
= CDROM_Data_FindBestVoldesc(dev
);
818 lseek(dev
, offs
, SEEK_SET
);
819 read(dev
, buf
, 2048);
821 * OK, another braindead one... argh. Just believe it.
822 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
823 * It's true and nobody will ever be able to change it.
825 ovi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOA
);
827 if ((ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) && (ovi
.dwMajorVersion
>= 4))
829 b0
= 3; b1
= 2; b2
= 1; b3
= 0;
831 for (i
= 0; i
< 2048; i
+= 4)
833 /* DON'T optimize this into DWORD !! (breaks overflow) */
834 serial
.p
[b0
] += buf
[i
+b0
];
835 serial
.p
[b1
] += buf
[i
+b1
];
836 serial
.p
[b2
] += buf
[i
+b2
];
837 serial
.p
[b3
] += buf
[i
+b3
];
844 /**************************************************************************
845 * CDROM_GetSerial [internal]
847 static DWORD
CDROM_GetSerial(int drive
)
850 HANDLE h
= CDROM_Open(drive
);
854 if (!h
|| ! !DeviceIoControl(h
, IOCTL_CDROM_DISK_TYPE
, NULL
, 0, &cdd
, sizeof(cdd
), &br
, 0))
857 switch (cdd
.DiskData
& 0x03)
859 case CDROM_DISK_DATA_TRACK
:
860 /* hopefully a data CD */
861 serial
= CDROM_Data_GetSerial(drive
);
863 case CDROM_DISK_AUDIO_TRACK
:
865 case CDROM_DISK_DATA_TRACK
|CDROM_DISK_AUDIO_TRACK
:
866 serial
= CDROM_Audio_GetSerial(h
);
873 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial
), LOWORD(serial
));
880 /***********************************************************************
881 * DRIVE_GetSerialNumber
883 DWORD
DRIVE_GetSerialNumber( int drive
)
886 char buff
[DRIVE_SUPER
];
888 if (!DRIVE_IsValid( drive
)) return 0;
890 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
892 switch(DOSDrives
[drive
].type
)
894 case DRIVE_REMOVABLE
:
896 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
897 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
898 " Maybe not FAT?\n" ,
899 DOSDrives
[drive
].device
, 'A'+drive
);
901 serial
= *((DWORD
*)(buff
+0x27));
904 serial
= CDROM_GetSerial(drive
);
907 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive
+'A');
911 return (serial
) ? serial
: DOSDrives
[drive
].serial_conf
;
915 /***********************************************************************
916 * DRIVE_SetSerialNumber
918 int DRIVE_SetSerialNumber( int drive
, DWORD serial
)
920 char buff
[DRIVE_SUPER
];
922 if (!DRIVE_IsValid( drive
)) return 0;
924 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
926 if ((DOSDrives
[drive
].type
!= DRIVE_REMOVABLE
) &&
927 (DOSDrives
[drive
].type
!= DRIVE_FIXED
)) return 0;
928 /* check, if the drive has a FAT filesystem */
929 if (DRIVE_ReadSuperblock(drive
, buff
)) return 0;
930 if (DRIVE_WriteSuperblockEntry(drive
, 0x27, 4, (char *) &serial
)) return 0;
934 if (DOSDrives
[drive
].type
== DRIVE_CDROM
) return 0;
935 DOSDrives
[drive
].serial_conf
= serial
;
940 /***********************************************************************
943 static UINT
DRIVE_GetType( int drive
)
945 if (!DRIVE_IsValid( drive
)) return DRIVE_UNKNOWN
;
946 return DOSDrives
[drive
].type
;
950 /***********************************************************************
953 UINT
DRIVE_GetFlags( int drive
)
955 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
956 return DOSDrives
[drive
].flags
;
960 /***********************************************************************
963 int DRIVE_Chdir( int drive
, const char *path
)
965 DOS_FULL_NAME full_name
;
966 char buffer
[MAX_PATHNAME_LEN
];
968 BY_HANDLE_FILE_INFORMATION info
;
969 TDB
*pTask
= TASK_GetCurrent();
971 strcpy( buffer
, "A:" );
973 TRACE("(%s,%s)\n", buffer
, path
);
974 lstrcpynA( buffer
+ 2, path
, sizeof(buffer
) - 2 );
976 if (!DOSFS_GetFullName( buffer
, TRUE
, &full_name
)) return 0;
977 if (!FILE_Stat( full_name
.long_name
, &info
)) return 0;
978 if (!(info
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
980 SetLastError( ERROR_FILE_NOT_FOUND
);
983 unix_cwd
= full_name
.long_name
+ strlen( DOSDrives
[drive
].root
);
984 while (*unix_cwd
== '/') unix_cwd
++;
986 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
987 'A' + drive
, unix_cwd
, full_name
.short_name
+ 3 );
989 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].dos_cwd
);
990 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].unix_cwd
);
991 DOSDrives
[drive
].dos_cwd
= heap_strdup( full_name
.short_name
+ 3 );
992 DOSDrives
[drive
].unix_cwd
= heap_strdup( unix_cwd
);
994 if (pTask
&& (pTask
->curdrive
& 0x80) &&
995 ((pTask
->curdrive
& ~0x80) == drive
))
997 lstrcpynA( pTask
->curdir
, full_name
.short_name
+ 2,
998 sizeof(pTask
->curdir
) );
999 DRIVE_LastTask
= GetCurrentTask();
1000 chdir(unix_cwd
); /* Only change if on current drive */
1006 /***********************************************************************
1009 int DRIVE_Disable( int drive
)
1011 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
1013 SetLastError( ERROR_INVALID_DRIVE
);
1016 DOSDrives
[drive
].flags
|= DRIVE_DISABLED
;
1021 /***********************************************************************
1024 int DRIVE_Enable( int drive
)
1026 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
1028 SetLastError( ERROR_INVALID_DRIVE
);
1031 DOSDrives
[drive
].flags
&= ~DRIVE_DISABLED
;
1036 /***********************************************************************
1037 * DRIVE_SetLogicalMapping
1039 int DRIVE_SetLogicalMapping ( int existing_drive
, int new_drive
)
1041 /* If new_drive is already valid, do nothing and return 0
1042 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1044 DOSDRIVE
*old
, *new;
1046 old
= DOSDrives
+ existing_drive
;
1047 new = DOSDrives
+ new_drive
;
1049 if ((existing_drive
< 0) || (existing_drive
>= MAX_DOS_DRIVES
) ||
1051 (new_drive
< 0) || (new_drive
>= MAX_DOS_DRIVES
))
1053 SetLastError( ERROR_INVALID_DRIVE
);
1059 TRACE("Can't map drive %c: to already existing drive %c:\n",
1060 'A' + existing_drive
, 'A' + new_drive
);
1061 /* it is already mapped there, so return success */
1062 if (!strcmp(old
->root
,new->root
))
1067 new->root
= heap_strdup( old
->root
);
1068 new->dos_cwd
= heap_strdup( old
->dos_cwd
);
1069 new->unix_cwd
= heap_strdup( old
->unix_cwd
);
1070 new->device
= heap_strdup( old
->device
);
1071 memcpy ( new->label_conf
, old
->label_conf
, 12 );
1072 memcpy ( new->label_read
, old
->label_read
, 12 );
1073 new->serial_conf
= old
->serial_conf
;
1074 new->type
= old
->type
;
1075 new->flags
= old
->flags
;
1076 new->dev
= old
->dev
;
1077 new->ino
= old
->ino
;
1079 TRACE("Drive %c: is now equal to drive %c:\n",
1080 'A' + new_drive
, 'A' + existing_drive
);
1086 /***********************************************************************
1089 * Open the drive raw device and return a Unix fd (or -1 on error).
1091 int DRIVE_OpenDevice( int drive
, int flags
)
1093 if (!DRIVE_IsValid( drive
)) return -1;
1094 return open( DOSDrives
[drive
].device
, flags
);
1098 /***********************************************************************
1101 * Read raw sectors from a device
1103 int DRIVE_RawRead(BYTE drive
, DWORD begin
, DWORD nr_sect
, BYTE
*dataptr
, BOOL fake_success
)
1107 if ((fd
= DRIVE_OpenDevice( drive
, O_RDONLY
)) != -1)
1109 lseek( fd
, begin
* 512, SEEK_SET
);
1110 /* FIXME: check errors */
1111 read( fd
, dataptr
, nr_sect
* 512 );
1116 memset(dataptr
, 0, nr_sect
* 512);
1119 if (begin
== 0 && nr_sect
> 1) *(dataptr
+ 512) = 0xf8;
1120 if (begin
== 1) *dataptr
= 0xf8;
1129 /***********************************************************************
1132 * Write raw sectors to a device
1134 int DRIVE_RawWrite(BYTE drive
, DWORD begin
, DWORD nr_sect
, BYTE
*dataptr
, BOOL fake_success
)
1138 if ((fd
= DRIVE_OpenDevice( drive
, O_RDONLY
)) != -1)
1140 lseek( fd
, begin
* 512, SEEK_SET
);
1141 /* FIXME: check errors */
1142 write( fd
, dataptr
, nr_sect
* 512 );
1146 if (!(fake_success
))
1153 /***********************************************************************
1154 * DRIVE_GetFreeSpace
1156 static int DRIVE_GetFreeSpace( int drive
, PULARGE_INTEGER size
,
1157 PULARGE_INTEGER available
)
1161 if (!DRIVE_IsValid(drive
))
1163 SetLastError( ERROR_INVALID_DRIVE
);
1167 /* FIXME: add autoconf check for this */
1168 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1169 if (statfs( DOSDrives
[drive
].root
, &info
, 0, 0) < 0)
1171 if (statfs( DOSDrives
[drive
].root
, &info
) < 0)
1175 WARN("cannot do statfs(%s)\n", DOSDrives
[drive
].root
);
1179 size
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bsize
, info
.f_blocks
);
1180 #ifdef STATFS_HAS_BAVAIL
1181 available
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bavail
, info
.f_bsize
);
1183 # ifdef STATFS_HAS_BFREE
1184 available
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bfree
, info
.f_bsize
);
1186 # error "statfs has no bfree/bavail member!"
1189 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
1190 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1191 available
->QuadPart
= 0;
1196 /***********************************************************************
1197 * DRIVE_GetCurrentDirectory
1198 * Returns "X:\\path\\etc\\".
1200 * Despite the API description, return required length including the
1201 * terminating null when buffer too small. This is the real behaviour.
1204 static UINT
DRIVE_GetCurrentDirectory( UINT buflen
, LPSTR buf
)
1207 const char *s
= DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1210 ret
= strlen(s
) + 3; /* length of WHOLE current directory */
1211 if (ret
>= buflen
) return ret
+ 1;
1212 lstrcpynA( buf
, "A:\\", min( 4u, buflen
) );
1213 if (buflen
) buf
[0] += DRIVE_GetCurrentDrive();
1214 if (buflen
> 3) lstrcpynA( buf
+ 3, s
, buflen
- 3 );
1219 /***********************************************************************
1222 * Build the environment array containing the drives' current directories.
1223 * Resulting pointer must be freed with HeapFree.
1225 char *DRIVE_BuildEnv(void)
1228 const char *cwd
[MAX_DOS_DRIVES
];
1231 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++)
1233 if ((cwd
[i
] = DRIVE_GetDosCwd(i
)) && cwd
[i
][0]) length
+= strlen(cwd
[i
]) + 8;
1235 if (!(env
= HeapAlloc( GetProcessHeap(), 0, length
+1 ))) return NULL
;
1236 for (i
= 0, p
= env
; i
< MAX_DOS_DRIVES
; i
++)
1238 if (cwd
[i
] && cwd
[i
][0])
1239 p
+= sprintf( p
, "=%c:=%c:\\%s", 'A'+i
, 'A'+i
, cwd
[i
] ) + 1;
1246 /***********************************************************************
1247 * GetDiskFreeSpace (KERNEL.422)
1249 BOOL16 WINAPI
GetDiskFreeSpace16( LPCSTR root
, LPDWORD cluster_sectors
,
1250 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1251 LPDWORD total_clusters
)
1253 return GetDiskFreeSpaceA( root
, cluster_sectors
, sector_bytes
,
1254 free_clusters
, total_clusters
);
1258 /***********************************************************************
1259 * GetDiskFreeSpaceA (KERNEL32.@)
1261 * Fails if expression resulting from current drive's dir and "root"
1262 * is not a root dir of the target drive.
1264 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1265 * if the corresponding info is unneeded.
1267 * FIXME: needs to support UNC names from Win95 OSR2 on.
1269 * Behaviour under Win95a:
1270 * CurrDir root result
1271 * "E:\\TEST" "E:" FALSE
1275 * "E:\\TEST" "\\" TRUE
1276 * "E:\\TEST" ":\\" FALSE
1277 * "E:\\TEST" "E:\\" TRUE
1278 * "E:\\TEST" "" FALSE
1279 * "E:\\" "" FALSE (!)
1281 * "E:\\TEST" 0x0 TRUE (!)
1282 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1283 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1285 BOOL WINAPI
GetDiskFreeSpaceA( LPCSTR root
, LPDWORD cluster_sectors
,
1286 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1287 LPDWORD total_clusters
)
1289 int drive
, sec_size
;
1290 ULARGE_INTEGER size
,available
;
1294 if ((!root
) || (strcmp(root
,"\\") == 0))
1295 drive
= DRIVE_GetCurrentDrive();
1297 if ( (strlen(root
) >= 2) && (root
[1] == ':')) /* root contains drive tag */
1299 drive
= toupper(root
[0]) - 'A';
1301 if (path
[0] == '\0')
1302 path
= DRIVE_GetDosCwd(drive
);
1304 if (path
[0] == '\\')
1306 if (path
[0]) /* oops, we are in a subdir */
1308 SetLastError(ERROR_INVALID_NAME
);
1314 SetLastError(ERROR_INVALID_NAME
);
1318 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1320 /* Cap the size and available at 2GB as per specs. */
1321 if ((size
.s
.HighPart
) ||(size
.s
.LowPart
> 0x7fffffff))
1323 size
.s
.HighPart
= 0;
1324 size
.s
.LowPart
= 0x7fffffff;
1326 if ((available
.s
.HighPart
) ||(available
.s
.LowPart
> 0x7fffffff))
1328 available
.s
.HighPart
=0;
1329 available
.s
.LowPart
= 0x7fffffff;
1331 sec_size
= (DRIVE_GetType(drive
)==DRIVE_CDROM
) ? 2048 : 512;
1332 size
.s
.LowPart
/= sec_size
;
1333 available
.s
.LowPart
/= sec_size
;
1334 /* FIXME: probably have to adjust those variables too for CDFS */
1336 while (cluster_sec
* 65536 < size
.s
.LowPart
) cluster_sec
*= 2;
1338 if (cluster_sectors
)
1339 *cluster_sectors
= cluster_sec
;
1341 *sector_bytes
= sec_size
;
1343 *free_clusters
= available
.s
.LowPart
/ cluster_sec
;
1345 *total_clusters
= size
.s
.LowPart
/ cluster_sec
;
1350 /***********************************************************************
1351 * GetDiskFreeSpaceW (KERNEL32.@)
1353 BOOL WINAPI
GetDiskFreeSpaceW( LPCWSTR root
, LPDWORD cluster_sectors
,
1354 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1355 LPDWORD total_clusters
)
1360 xroot
= HEAP_strdupWtoA( GetProcessHeap(), 0, root
);
1361 ret
= GetDiskFreeSpaceA( xroot
,cluster_sectors
, sector_bytes
,
1362 free_clusters
, total_clusters
);
1363 HeapFree( GetProcessHeap(), 0, xroot
);
1368 /***********************************************************************
1369 * GetDiskFreeSpaceExA (KERNEL32.@)
1371 * This function is used to acquire the size of the available and
1372 * total space on a logical volume.
1376 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1377 * detailed error information.
1380 BOOL WINAPI
GetDiskFreeSpaceExA( LPCSTR root
,
1381 PULARGE_INTEGER avail
,
1382 PULARGE_INTEGER total
,
1383 PULARGE_INTEGER totalfree
)
1386 ULARGE_INTEGER size
,available
;
1388 if (!root
) drive
= DRIVE_GetCurrentDrive();
1390 { /* C: always works for GetDiskFreeSpaceEx */
1391 if ((root
[1]) && ((root
[1] != ':') || (root
[2] && root
[2] != '\\')))
1393 FIXME("there are valid root names which are not supported yet\n");
1394 /* ..like UNC names, for instance. */
1396 WARN("invalid root '%s'\n", root
);
1399 drive
= toupper(root
[0]) - 'A';
1402 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1406 total
->s
.HighPart
= size
.s
.HighPart
;
1407 total
->s
.LowPart
= size
.s
.LowPart
;
1412 totalfree
->s
.HighPart
= available
.s
.HighPart
;
1413 totalfree
->s
.LowPart
= available
.s
.LowPart
;
1418 if (FIXME_ON(dosfs
))
1420 /* On Windows2000, we need to check the disk quota
1421 allocated for the user owning the calling process. We
1422 don't want to be more obtrusive than necessary with the
1423 FIXME messages, so don't print the FIXME unless Wine is
1424 actually masquerading as Windows2000. */
1427 ovi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOA
);
1428 if (GetVersionExA(&ovi
))
1430 if (ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
&& ovi
.dwMajorVersion
> 4)
1431 FIXME("no per-user quota support yet\n");
1435 /* Quick hack, should eventually be fixed to work 100% with
1436 Windows2000 (see comment above). */
1437 avail
->s
.HighPart
= available
.s
.HighPart
;
1438 avail
->s
.LowPart
= available
.s
.LowPart
;
1444 /***********************************************************************
1445 * GetDiskFreeSpaceExW (KERNEL32.@)
1447 BOOL WINAPI
GetDiskFreeSpaceExW( LPCWSTR root
, PULARGE_INTEGER avail
,
1448 PULARGE_INTEGER total
,
1449 PULARGE_INTEGER totalfree
)
1454 xroot
= HEAP_strdupWtoA( GetProcessHeap(), 0, root
);
1455 ret
= GetDiskFreeSpaceExA( xroot
, avail
, total
, totalfree
);
1456 HeapFree( GetProcessHeap(), 0, xroot
);
1460 /***********************************************************************
1461 * GetDriveType (KERNEL.136)
1462 * This function returns the type of a drive in Win16.
1463 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1464 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1465 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1466 * do any pseudo-clever changes.
1469 * drivetype DRIVE_xxx
1471 UINT16 WINAPI
GetDriveType16( UINT16 drive
) /* [in] number (NOT letter) of drive */
1473 UINT type
= DRIVE_GetType(drive
);
1474 TRACE("(%c:)\n", 'A' + drive
);
1475 if (type
== DRIVE_CDROM
) type
= DRIVE_REMOTE
;
1480 /***********************************************************************
1481 * GetDriveTypeA (KERNEL32.@)
1483 * Returns the type of the disk drive specified. If root is NULL the
1484 * root of the current directory is used.
1488 * Type of drive (from Win32 SDK):
1490 * DRIVE_UNKNOWN unable to find out anything about the drive
1491 * DRIVE_NO_ROOT_DIR nonexistent root dir
1492 * DRIVE_REMOVABLE the disk can be removed from the machine
1493 * DRIVE_FIXED the disk can not be removed from the machine
1494 * DRIVE_REMOTE network disk
1495 * DRIVE_CDROM CDROM drive
1496 * DRIVE_RAMDISK virtual disk in RAM
1498 UINT WINAPI
GetDriveTypeA(LPCSTR root
) /* [in] String describing drive */
1501 TRACE("(%s)\n", debugstr_a(root
));
1503 if (NULL
== root
) drive
= DRIVE_GetCurrentDrive();
1506 if ((root
[1]) && (root
[1] != ':'))
1508 WARN("invalid root %s\n", debugstr_a(root
));
1509 return DRIVE_NO_ROOT_DIR
;
1511 drive
= toupper(root
[0]) - 'A';
1513 return DRIVE_GetType(drive
);
1517 /***********************************************************************
1518 * GetDriveTypeW (KERNEL32.@)
1520 UINT WINAPI
GetDriveTypeW( LPCWSTR root
)
1522 LPSTR xpath
= HEAP_strdupWtoA( GetProcessHeap(), 0, root
);
1523 UINT ret
= GetDriveTypeA( xpath
);
1524 HeapFree( GetProcessHeap(), 0, xpath
);
1529 /***********************************************************************
1530 * GetCurrentDirectory (KERNEL.411)
1532 UINT16 WINAPI
GetCurrentDirectory16( UINT16 buflen
, LPSTR buf
)
1534 return (UINT16
)DRIVE_GetCurrentDirectory(buflen
, buf
);
1538 /***********************************************************************
1539 * GetCurrentDirectoryA (KERNEL32.@)
1541 UINT WINAPI
GetCurrentDirectoryA( UINT buflen
, LPSTR buf
)
1544 char longname
[MAX_PATHNAME_LEN
];
1545 char shortname
[MAX_PATHNAME_LEN
];
1546 ret
= DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN
, shortname
);
1547 if ( ret
> MAX_PATHNAME_LEN
) {
1548 ERR_(file
)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret
);
1551 GetLongPathNameA(shortname
, longname
, MAX_PATHNAME_LEN
);
1552 ret
= strlen( longname
) + 1;
1553 if (ret
> buflen
) return ret
;
1554 strcpy(buf
, longname
);
1558 /***********************************************************************
1559 * GetCurrentDirectoryW (KERNEL32.@)
1561 UINT WINAPI
GetCurrentDirectoryW( UINT buflen
, LPWSTR buf
)
1563 LPSTR xpath
= HeapAlloc( GetProcessHeap(), 0, buflen
+1 );
1564 UINT ret
= GetCurrentDirectoryA( buflen
, xpath
);
1565 if (ret
< buflen
) ret
= MultiByteToWideChar( CP_ACP
, 0, xpath
, -1, buf
, buflen
) - 1;
1566 HeapFree( GetProcessHeap(), 0, xpath
);
1571 /***********************************************************************
1572 * SetCurrentDirectory (KERNEL.412)
1574 BOOL16 WINAPI
SetCurrentDirectory16( LPCSTR dir
)
1576 return SetCurrentDirectoryA( dir
);
1580 /***********************************************************************
1581 * SetCurrentDirectoryA (KERNEL32.@)
1583 BOOL WINAPI
SetCurrentDirectoryA( LPCSTR dir
)
1585 int drive
, olddrive
= DRIVE_GetCurrentDrive();
1588 ERR_(file
)("(NULL)!\n");
1591 if (dir
[0] && (dir
[1]==':'))
1593 drive
= toupper( *dir
) - 'A';
1599 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1600 sets pTask->curdir only if pTask->curdrive is drive */
1601 if (!(DRIVE_SetCurrentDrive( drive
)))
1603 /* FIXME: what about empty strings? Add a \\ ? */
1604 if (!DRIVE_Chdir( drive
, dir
)) {
1605 DRIVE_SetCurrentDrive(olddrive
);
1612 /***********************************************************************
1613 * SetCurrentDirectoryW (KERNEL32.@)
1615 BOOL WINAPI
SetCurrentDirectoryW( LPCWSTR dirW
)
1617 LPSTR dir
= HEAP_strdupWtoA( GetProcessHeap(), 0, dirW
);
1618 BOOL res
= SetCurrentDirectoryA( dir
);
1619 HeapFree( GetProcessHeap(), 0, dir
);
1624 /***********************************************************************
1625 * GetLogicalDriveStringsA (KERNEL32.@)
1627 UINT WINAPI
GetLogicalDriveStringsA( UINT len
, LPSTR buffer
)
1631 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1632 if (DRIVE_IsValid(drive
)) count
++;
1633 if ((count
* 4) + 1 <= len
)
1636 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1637 if (DRIVE_IsValid(drive
))
1648 return (count
* 4) + 1; /* account for terminating null */
1649 /* The API tells about these different return values */
1653 /***********************************************************************
1654 * GetLogicalDriveStringsW (KERNEL32.@)
1656 UINT WINAPI
GetLogicalDriveStringsW( UINT len
, LPWSTR buffer
)
1660 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1661 if (DRIVE_IsValid(drive
)) count
++;
1662 if (count
* 4 * sizeof(WCHAR
) <= len
)
1665 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1666 if (DRIVE_IsValid(drive
))
1668 *p
++ = (WCHAR
)('a' + drive
);
1675 return count
* 4 * sizeof(WCHAR
);
1679 /***********************************************************************
1680 * GetLogicalDrives (KERNEL32.@)
1682 DWORD WINAPI
GetLogicalDrives(void)
1687 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1689 if ( (DRIVE_IsValid(drive
)) ||
1690 (DOSDrives
[drive
].type
== DRIVE_CDROM
)) /* audio CD is also valid */
1691 ret
|= (1 << drive
);
1697 /***********************************************************************
1698 * GetVolumeInformationA (KERNEL32.@)
1700 BOOL WINAPI
GetVolumeInformationA( LPCSTR root
, LPSTR label
,
1701 DWORD label_len
, DWORD
*serial
,
1702 DWORD
*filename_len
, DWORD
*flags
,
1703 LPSTR fsname
, DWORD fsname_len
)
1708 /* FIXME, SetLastError()s missing */
1710 if (!root
) drive
= DRIVE_GetCurrentDrive();
1713 if ((root
[1]) && (root
[1] != ':'))
1715 WARN("invalid root '%s'\n",root
);
1718 drive
= toupper(root
[0]) - 'A';
1720 if (!DRIVE_IsValid( drive
)) return FALSE
;
1723 lstrcpynA( label
, DRIVE_GetLabel(drive
), label_len
);
1724 cp
= label
+ strlen(label
);
1725 while (cp
!= label
&& *(cp
-1) == ' ') cp
--;
1728 if (serial
) *serial
= DRIVE_GetSerialNumber(drive
);
1730 /* Set the filesystem information */
1731 /* Note: we only emulate a FAT fs at present */
1734 if (DOSDrives
[drive
].flags
& DRIVE_SHORT_NAMES
)
1737 *filename_len
= 255;
1742 if (DOSDrives
[drive
].flags
& DRIVE_CASE_SENSITIVE
)
1743 *flags
|=FS_CASE_SENSITIVE
;
1744 if (DOSDrives
[drive
].flags
& DRIVE_CASE_PRESERVING
)
1745 *flags
|=FS_CASE_IS_PRESERVED
;
1748 /* Diablo checks that return code ... */
1749 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
1750 lstrcpynA( fsname
, "CDFS", fsname_len
);
1752 lstrcpynA( fsname
, "FAT", fsname_len
);
1758 /***********************************************************************
1759 * GetVolumeInformationW (KERNEL32.@)
1761 BOOL WINAPI
GetVolumeInformationW( LPCWSTR root
, LPWSTR label
,
1762 DWORD label_len
, DWORD
*serial
,
1763 DWORD
*filename_len
, DWORD
*flags
,
1764 LPWSTR fsname
, DWORD fsname_len
)
1766 LPSTR xroot
= HEAP_strdupWtoA( GetProcessHeap(), 0, root
);
1767 LPSTR xvolname
= label
? HeapAlloc(GetProcessHeap(),0,label_len
) : NULL
;
1768 LPSTR xfsname
= fsname
? HeapAlloc(GetProcessHeap(),0,fsname_len
) : NULL
;
1769 BOOL ret
= GetVolumeInformationA( xroot
, xvolname
, label_len
, serial
,
1770 filename_len
, flags
, xfsname
,
1774 if (label
) MultiByteToWideChar( CP_ACP
, 0, xvolname
, -1, label
, label_len
);
1775 if (fsname
) MultiByteToWideChar( CP_ACP
, 0, xfsname
, -1, fsname
, fsname_len
);
1777 HeapFree( GetProcessHeap(), 0, xroot
);
1778 HeapFree( GetProcessHeap(), 0, xvolname
);
1779 HeapFree( GetProcessHeap(), 0, xfsname
);
1783 /***********************************************************************
1784 * SetVolumeLabelA (KERNEL32.@)
1786 BOOL WINAPI
SetVolumeLabelA( LPCSTR root
, LPCSTR volname
)
1790 /* FIXME, SetLastErrors missing */
1792 if (!root
) drive
= DRIVE_GetCurrentDrive();
1795 if ((root
[1]) && (root
[1] != ':'))
1797 WARN("invalid root '%s'\n",root
);
1800 drive
= toupper(root
[0]) - 'A';
1802 if (!DRIVE_IsValid( drive
)) return FALSE
;
1804 /* some copy protection stuff check this */
1805 if (DOSDrives
[drive
].type
== DRIVE_CDROM
) return FALSE
;
1807 FIXME("(%s,%s),stub!\n", root
, volname
);
1811 /***********************************************************************
1812 * SetVolumeLabelW (KERNEL32.@)
1814 BOOL WINAPI
SetVolumeLabelW(LPCWSTR rootpath
,LPCWSTR volname
)
1819 xroot
= HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath
);
1820 xvol
= HEAP_strdupWtoA( GetProcessHeap(), 0, volname
);
1821 ret
= SetVolumeLabelA( xroot
, xvol
);
1822 HeapFree( GetProcessHeap(), 0, xroot
);
1823 HeapFree( GetProcessHeap(), 0, xvol
);