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 */
67 #include "wine/library.h"
68 #include "wine/server.h"
69 #include "wine/debug.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 extern void CDROM_InitRegistry(int dev
);
138 /***********************************************************************
141 static UINT
DRIVE_GetDriveType( const char *name
)
146 PROFILE_GetWineIniString( name
, "Type", "hd", buffer
, sizeof(buffer
) );
147 for (i
= 0; i
< sizeof(DRIVE_Types
)/sizeof(DRIVE_Types
[0]); i
++)
149 if (!strcasecmp( buffer
, DRIVE_Types
[i
] )) return i
;
151 MESSAGE("%s: unknown drive type '%s', defaulting to 'hd'.\n",
157 /***********************************************************************
160 static UINT
DRIVE_GetFSFlags( const char *name
, const char *value
)
162 const FS_DESCR
*descr
;
164 for (descr
= DRIVE_Filesystems
; descr
->name
; descr
++)
165 if (!strcasecmp( value
, descr
->name
)) return descr
->flags
;
166 MESSAGE("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
168 return DRIVE_CASE_PRESERVING
;
172 /***********************************************************************
177 int i
, len
, count
= 0;
178 char name
[] = "Drive A";
179 char drive_env
[] = "=A:";
180 char path
[MAX_PATHNAME_LEN
];
182 struct stat drive_stat_buffer
;
186 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, name
[6]++, drive
++)
188 PROFILE_GetWineIniString( name
, "Path", "", path
, sizeof(path
)-1 );
191 p
= path
+ strlen(path
) - 1;
192 while ((p
> path
) && (*p
== '/')) *p
-- = '\0';
196 drive
->root
= heap_strdup( path
);
200 /* relative paths are relative to config dir */
201 const char *config
= wine_get_config_dir();
202 drive
->root
= HeapAlloc( GetProcessHeap(), 0, strlen(config
) + strlen(path
) + 2 );
203 sprintf( drive
->root
, "%s/%s", config
, path
);
206 if (stat( drive
->root
, &drive_stat_buffer
))
208 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
209 drive
->root
, strerror(errno
), 'A' + i
);
210 HeapFree( GetProcessHeap(), 0, drive
->root
);
214 if (!S_ISDIR(drive_stat_buffer
.st_mode
))
216 MESSAGE("%s is not a directory, ignoring drive %c:\n",
217 drive
->root
, 'A' + i
);
218 HeapFree( GetProcessHeap(), 0, drive
->root
);
223 drive
->dos_cwd
= heap_strdup( "" );
224 drive
->unix_cwd
= heap_strdup( "" );
225 drive
->type
= DRIVE_GetDriveType( name
);
226 drive
->device
= NULL
;
228 drive
->dev
= drive_stat_buffer
.st_dev
;
229 drive
->ino
= drive_stat_buffer
.st_ino
;
231 /* Get the drive label */
232 PROFILE_GetWineIniString( name
, "Label", "", drive
->label_conf
, 12 );
233 if ((len
= strlen(drive
->label_conf
)) < 11)
235 /* Pad label with spaces */
236 memset( drive
->label_conf
+ len
, ' ', 11 - len
);
237 drive
->label_conf
[11] = '\0';
240 /* Get the serial number */
241 PROFILE_GetWineIniString( name
, "Serial", "12345678",
242 buffer
, sizeof(buffer
) );
243 drive
->serial_conf
= strtoul( buffer
, NULL
, 16 );
245 /* Get the filesystem type */
246 PROFILE_GetWineIniString( name
, "Filesystem", "win95",
247 buffer
, sizeof(buffer
) );
248 drive
->flags
= DRIVE_GetFSFlags( name
, buffer
);
251 PROFILE_GetWineIniString( name
, "Device", "",
252 buffer
, sizeof(buffer
) );
256 drive
->device
= heap_strdup( buffer
);
257 if (PROFILE_GetWineIniBool( name
, "ReadVolInfo", 1))
258 drive
->flags
|= DRIVE_READ_VOL_INFO
;
259 if (drive
->type
== DRIVE_CDROM
)
261 if ((cd_fd
= open(buffer
,O_RDONLY
|O_NONBLOCK
)) != -1) {
262 CDROM_InitRegistry(cd_fd
);
268 /* Get the FailReadOnly flag */
269 if (PROFILE_GetWineIniBool( name
, "FailReadOnly", 0 ))
270 drive
->flags
|= DRIVE_FAIL_READ_ONLY
;
272 /* Make the first hard disk the current drive */
273 if ((DRIVE_CurDrive
== -1) && (drive
->type
== DRIVE_FIXED
))
277 TRACE("%s: path=%s type=%s label='%s' serial=%08lx "
278 "flags=%08x dev=%x ino=%x\n",
279 name
, drive
->root
, DRIVE_Types
[drive
->type
],
280 drive
->label_conf
, drive
->serial_conf
, drive
->flags
,
281 (int)drive
->dev
, (int)drive
->ino
);
283 else WARN("%s: not defined\n", name
);
288 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
289 /* Create a C drive pointing to Unix root dir */
290 DOSDrives
[2].root
= heap_strdup( "/" );
291 DOSDrives
[2].dos_cwd
= heap_strdup( "" );
292 DOSDrives
[2].unix_cwd
= heap_strdup( "" );
293 strcpy( DOSDrives
[2].label_conf
, "Drive C " );
294 DOSDrives
[2].serial_conf
= 12345678;
295 DOSDrives
[2].type
= DRIVE_FIXED
;
296 DOSDrives
[2].device
= NULL
;
297 DOSDrives
[2].flags
= 0;
301 /* Make sure the current drive is valid */
302 if (DRIVE_CurDrive
== -1)
304 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
306 if (drive
->root
&& !(drive
->flags
& DRIVE_DISABLED
))
314 /* get current working directory info for all drives */
315 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++, drive_env
[1]++)
317 if (!GetEnvironmentVariableA(drive_env
, path
, sizeof(path
))) continue;
319 if (toupper(path
[0]) != drive_env
[1] || path
[1] != ':') continue;
320 DRIVE_Chdir( i
, path
+ 2 );
326 /***********************************************************************
329 int DRIVE_IsValid( int drive
)
331 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
332 return (DOSDrives
[drive
].root
&&
333 !(DOSDrives
[drive
].flags
& DRIVE_DISABLED
));
337 /***********************************************************************
338 * DRIVE_GetCurrentDrive
340 int DRIVE_GetCurrentDrive(void)
342 TDB
*pTask
= TASK_GetCurrent();
343 if (pTask
&& (pTask
->curdrive
& 0x80)) return pTask
->curdrive
& ~0x80;
344 return DRIVE_CurDrive
;
348 /***********************************************************************
349 * DRIVE_SetCurrentDrive
351 int DRIVE_SetCurrentDrive( int drive
)
353 TDB
*pTask
= TASK_GetCurrent();
354 if (!DRIVE_IsValid( drive
))
356 SetLastError( ERROR_INVALID_DRIVE
);
359 TRACE("%c:\n", 'A' + drive
);
360 DRIVE_CurDrive
= drive
;
361 if (pTask
) pTask
->curdrive
= drive
| 0x80;
362 chdir(DRIVE_GetUnixCwd(drive
));
367 /***********************************************************************
368 * DRIVE_FindDriveRoot
370 * Find a drive for which the root matches the beginning of the given path.
371 * This can be used to translate a Unix path into a drive + DOS path.
372 * Return value is the drive, or -1 on error. On success, path is modified
373 * to point to the beginning of the DOS path.
375 int DRIVE_FindDriveRoot( const char **path
)
377 /* Starting with the full path, check if the device and inode match any of
378 * the wine 'drives'. If not then remove the last path component and try
379 * again. If the last component was a '..' then skip a normal component
380 * since it's a directory that's ascended back out of.
382 int drive
, level
, len
;
383 char buffer
[MAX_PATHNAME_LEN
];
387 strcpy( buffer
, *path
);
388 while ((p
= strchr( buffer
, '\\' )) != NULL
)
390 len
= strlen(buffer
);
392 /* strip off trailing slashes */
393 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
398 if (stat( buffer
, &st
) == 0 && S_ISDIR( st
.st_mode
))
400 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
402 if (!DOSDrives
[drive
].root
||
403 (DOSDrives
[drive
].flags
& DRIVE_DISABLED
))
406 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
407 (DOSDrives
[drive
].ino
== st
.st_ino
))
409 if (len
== 1) len
= 0; /* preserve root slash in returned path */
410 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
411 *path
, 'A' + drive
, buffer
, *path
+ len
);
413 if (!**path
) *path
= "\\";
418 if (len
<= 1) return -1; /* reached root */
423 /* find start of the last path component */
424 while (len
> 1 && buffer
[len
- 1] != '/') len
--;
425 if (!buffer
[len
]) break; /* empty component -> reached root */
426 /* does removing it take us up a level? */
427 if (strcmp( buffer
+ len
, "." ) != 0)
428 level
+= strcmp( buffer
+ len
, ".." ) ? 1 : -1;
430 /* strip off trailing slashes */
431 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
437 /***********************************************************************
440 const char * DRIVE_GetRoot( int drive
)
442 if (!DRIVE_IsValid( drive
)) return NULL
;
443 return DOSDrives
[drive
].root
;
447 /***********************************************************************
450 const char * DRIVE_GetDosCwd( int drive
)
452 TDB
*pTask
= TASK_GetCurrent();
453 if (!DRIVE_IsValid( drive
)) return NULL
;
455 /* Check if we need to change the directory to the new task. */
456 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
457 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
458 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
460 /* Perform the task-switch */
461 if (!DRIVE_Chdir( drive
, pTask
->curdir
)) DRIVE_Chdir( drive
, "\\" );
462 DRIVE_LastTask
= GetCurrentTask();
464 return DOSDrives
[drive
].dos_cwd
;
468 /***********************************************************************
471 const char * DRIVE_GetUnixCwd( int drive
)
473 TDB
*pTask
= TASK_GetCurrent();
474 if (!DRIVE_IsValid( drive
)) return NULL
;
476 /* Check if we need to change the directory to the new task. */
477 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
478 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
479 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
481 /* Perform the task-switch */
482 if (!DRIVE_Chdir( drive
, pTask
->curdir
)) DRIVE_Chdir( drive
, "\\" );
483 DRIVE_LastTask
= GetCurrentTask();
485 return DOSDrives
[drive
].unix_cwd
;
489 /***********************************************************************
492 const char * DRIVE_GetDevice( int drive
)
494 return (DRIVE_IsValid( drive
)) ? DOSDrives
[drive
].device
: NULL
;
497 /******************************************************************
498 * static WORD CDROM_Data_FindBestVoldesc
502 static WORD
CDROM_Data_FindBestVoldesc(int fd
)
504 BYTE cur_vd_type
, max_vd_type
= 0;
505 unsigned int offs
, best_offs
= 0, extra_offs
= 0;
508 for (offs
= 0x8000; offs
<= 0x9800; offs
+= 0x800)
510 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
511 * the volume label is displaced forward by 8
513 lseek(fd
, offs
+ 11, SEEK_SET
); /* check for non-ISO9660 signature */
515 if ((sig
[0] == 'R') && (sig
[1] == 'O') && (sig
[2]=='M'))
519 lseek(fd
, offs
+ extra_offs
, SEEK_SET
);
520 read(fd
, &cur_vd_type
, 1);
521 if (cur_vd_type
== 0xff) /* voldesc set terminator */
523 if (cur_vd_type
> max_vd_type
)
525 max_vd_type
= cur_vd_type
;
526 best_offs
= offs
+ extra_offs
;
532 /***********************************************************************
533 * DRIVE_ReadSuperblock
536 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
537 * to check, that they are writing on a FAT filesystem !
539 int DRIVE_ReadSuperblock (int drive
, char * buff
)
541 #define DRIVE_SUPER 96
546 if (memset(buff
,0,DRIVE_SUPER
)!=buff
) return -1;
547 if ((fd
=open(DOSDrives
[drive
].device
,O_RDONLY
)) == -1)
550 if (!DOSDrives
[drive
].device
)
551 ERR("No device configured for drive %c: !\n", 'A'+drive
);
553 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives
[drive
].device
, 'A'+drive
,
554 (stat(DOSDrives
[drive
].device
, &st
)) ?
555 "not available or symlink not valid ?" : "no permission");
556 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
557 PROFILE_UsageWineIni();
561 switch(DOSDrives
[drive
].type
)
563 case DRIVE_REMOVABLE
:
568 offs
= CDROM_Data_FindBestVoldesc(fd
);
575 if ((offs
) && (lseek(fd
,offs
,SEEK_SET
)!=offs
))
580 if (read(fd
,buff
,DRIVE_SUPER
)!=DRIVE_SUPER
)
586 switch(DOSDrives
[drive
].type
)
588 case DRIVE_REMOVABLE
:
590 if ((buff
[0x26]!=0x29) || /* Check for FAT present */
591 /* FIXME: do really all FAT have their name beginning with
592 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
593 memcmp( buff
+0x36,"FAT",3))
595 ERR("The filesystem is not FAT !! (device=%s)\n",
596 DOSDrives
[drive
].device
);
602 if (strncmp(&buff
[1],"CD001",5)) /* Check for iso9660 present */
607 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
621 /***********************************************************************
622 * DRIVE_WriteSuperblockEntry
625 * We are writing as little as possible (ie. not the whole SuperBlock)
626 * not to interfere with kernel. The drive can be mounted !
628 int DRIVE_WriteSuperblockEntry (int drive
, off_t ofs
, size_t len
, char * buff
)
632 if ((fd
=open(DOSDrives
[drive
].device
,O_WRONLY
))==-1)
634 ERR("Cannot open the device %s (for writing)\n",
635 DOSDrives
[drive
].device
);
638 if (lseek(fd
,ofs
,SEEK_SET
)!=ofs
)
640 ERR("lseek failed on device %s !\n",
641 DOSDrives
[drive
].device
);
645 if (write(fd
,buff
,len
)!=len
)
648 ERR("Cannot write on %s !\n",
649 DOSDrives
[drive
].device
);
655 /******************************************************************
656 * static HANDLE CDROM_Open
660 static HANDLE
CDROM_Open(int drive
)
664 strcpy(root
, "\\\\.\\A:");
667 return CreateFileA(root
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, 0);
670 /**************************************************************************
671 * CDROM_Data_GetLabel [internal]
673 DWORD
CDROM_Data_GetLabel(int drive
, char *label
)
675 #define LABEL_LEN 32+1
676 int dev
= open(DOSDrives
[drive
].device
, O_RDONLY
|O_NONBLOCK
);
677 WORD offs
= CDROM_Data_FindBestVoldesc(dev
);
678 WCHAR label_read
[LABEL_LEN
]; /* Unicode possible, too */
679 DWORD unicode_id
= 0;
683 if ((lseek(dev
, offs
+0x58, SEEK_SET
) == offs
+0x58)
684 && (read(dev
, &unicode_id
, 3) == 3))
686 int ver
= (unicode_id
& 0xff0000) >> 16;
688 if ((lseek(dev
, offs
+0x28, SEEK_SET
) != offs
+0x28)
689 || (read(dev
, &label_read
, LABEL_LEN
) != LABEL_LEN
))
693 if ((LOWORD(unicode_id
) == 0x2f25) /* Unicode ID */
694 && ((ver
== 0x40) || (ver
== 0x43) || (ver
== 0x45)))
695 { /* yippee, unicode */
698 for (i
=0; i
<LABEL_LEN
;i
++)
699 { /* Motorola -> Intel Unicode conversion :-\ */
701 label_read
[i
] = (ch
<< 8) | (ch
>> 8);
703 WideCharToMultiByte( CP_ACP
, 0, label_read
, -1, label
, 12, NULL
, NULL
);
708 strncpy(label
, (LPSTR
)label_read
, 11);
716 ERR("error reading label !\n");
720 /**************************************************************************
721 * CDROM_GetLabel [internal]
723 static DWORD
CDROM_GetLabel(int drive
, char *label
)
725 HANDLE h
= CDROM_Open(drive
);
730 if (!h
|| !DeviceIoControl(h
, IOCTL_CDROM_DISK_TYPE
, NULL
, 0, &cdd
, sizeof(cdd
), &br
, 0))
733 switch (cdd
.DiskData
& 0x03)
735 case CDROM_DISK_DATA_TRACK
:
736 if (!CDROM_Data_GetLabel(drive
, label
))
739 case CDROM_DISK_AUDIO_TRACK
:
740 strcpy(label
, "Audio CD ");
742 case CDROM_DISK_DATA_TRACK
|CDROM_DISK_AUDIO_TRACK
:
743 FIXME("Need to get the label of a mixed mode CD: not implemented yet !\n");
749 TRACE("CD: label is '%s'.\n", label
);
753 /***********************************************************************
756 const char * DRIVE_GetLabel( int drive
)
759 char buff
[DRIVE_SUPER
];
762 if (!DRIVE_IsValid( drive
)) return NULL
;
763 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
765 read
= CDROM_GetLabel(drive
, DOSDrives
[drive
].label_read
);
768 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
770 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
771 ERR("Invalid or unreadable superblock on %s (%c:).\n",
772 DOSDrives
[drive
].device
, (char)(drive
+'A'));
774 if (DOSDrives
[drive
].type
== DRIVE_REMOVABLE
||
775 DOSDrives
[drive
].type
== DRIVE_FIXED
)
778 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
779 if (offs
!= -1) memcpy(DOSDrives
[drive
].label_read
,buff
+offs
,11);
780 DOSDrives
[drive
].label_read
[11]='\0';
786 DOSDrives
[drive
].label_read
: DOSDrives
[drive
].label_conf
;
789 #define CDFRAMES_PERSEC 75
790 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
791 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
792 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
794 /**************************************************************************
795 * CDROM_Audio_GetSerial [internal]
797 static DWORD
CDROM_Audio_GetSerial(HANDLE h
)
799 unsigned long serial
= 0;
802 DWORD dwStart
, dwEnd
, br
;
805 if (!DeviceIoControl(h
, IOCTL_CDROM_READ_TOC
, NULL
, 0, &toc
, sizeof(toc
), &br
, 0))
809 * wMagic collects the wFrames from track 1
810 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
812 * There it is collected for correcting the serial when there are less than
815 wMagic
= toc
.TrackData
[0].Address
[2];
816 dwStart
= FRAME_OF_TOC(toc
, toc
.FirstTrack
);
818 for (i
= 0; i
<= toc
.LastTrack
- toc
.FirstTrack
; i
++) {
819 serial
+= (toc
.TrackData
[i
].Address
[0] << 16) |
820 (toc
.TrackData
[i
].Address
[1] << 8) | toc
.TrackData
[i
].Address
[2];
822 dwEnd
= FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1);
824 if (toc
.LastTrack
- toc
.FirstTrack
+ 1 < 3)
825 serial
+= wMagic
+ (dwEnd
- dwStart
);
830 /**************************************************************************
831 * CDROM_Data_GetSerial [internal]
833 static DWORD
CDROM_Data_GetSerial(int drive
)
835 int dev
= open(DOSDrives
[drive
].device
, O_RDONLY
|O_NONBLOCK
);
841 BYTE b0
= 0, b1
= 1, b2
= 2, b3
= 3;
844 if (dev
== -1) return 0;
845 offs
= CDROM_Data_FindBestVoldesc(dev
);
854 lseek(dev
, offs
, SEEK_SET
);
855 read(dev
, buf
, 2048);
857 * OK, another braindead one... argh. Just believe it.
858 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
859 * It's true and nobody will ever be able to change it.
861 ovi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOA
);
863 if ((ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) && (ovi
.dwMajorVersion
>= 4))
865 b0
= 3; b1
= 2; b2
= 1; b3
= 0;
867 for (i
= 0; i
< 2048; i
+= 4)
869 /* DON'T optimize this into DWORD !! (breaks overflow) */
870 serial
.p
[b0
] += buf
[i
+b0
];
871 serial
.p
[b1
] += buf
[i
+b1
];
872 serial
.p
[b2
] += buf
[i
+b2
];
873 serial
.p
[b3
] += buf
[i
+b3
];
880 /**************************************************************************
881 * CDROM_GetSerial [internal]
883 static DWORD
CDROM_GetSerial(int drive
)
886 HANDLE h
= CDROM_Open(drive
);
890 if (!h
|| ! !DeviceIoControl(h
, IOCTL_CDROM_DISK_TYPE
, NULL
, 0, &cdd
, sizeof(cdd
), &br
, 0))
893 switch (cdd
.DiskData
& 0x03)
895 case CDROM_DISK_DATA_TRACK
:
896 /* hopefully a data CD */
897 serial
= CDROM_Data_GetSerial(drive
);
899 case CDROM_DISK_AUDIO_TRACK
:
901 case CDROM_DISK_DATA_TRACK
|CDROM_DISK_AUDIO_TRACK
:
902 serial
= CDROM_Audio_GetSerial(h
);
909 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial
), LOWORD(serial
));
916 /***********************************************************************
917 * DRIVE_GetSerialNumber
919 DWORD
DRIVE_GetSerialNumber( int drive
)
922 char buff
[DRIVE_SUPER
];
924 if (!DRIVE_IsValid( drive
)) return 0;
926 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
928 switch(DOSDrives
[drive
].type
)
930 case DRIVE_REMOVABLE
:
932 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
933 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
934 " Maybe not FAT?\n" ,
935 DOSDrives
[drive
].device
, 'A'+drive
);
937 serial
= *((DWORD
*)(buff
+0x27));
940 serial
= CDROM_GetSerial(drive
);
943 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive
+'A');
947 return (serial
) ? serial
: DOSDrives
[drive
].serial_conf
;
951 /***********************************************************************
952 * DRIVE_SetSerialNumber
954 int DRIVE_SetSerialNumber( int drive
, DWORD serial
)
956 char buff
[DRIVE_SUPER
];
958 if (!DRIVE_IsValid( drive
)) return 0;
960 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
962 if ((DOSDrives
[drive
].type
!= DRIVE_REMOVABLE
) &&
963 (DOSDrives
[drive
].type
!= DRIVE_FIXED
)) return 0;
964 /* check, if the drive has a FAT filesystem */
965 if (DRIVE_ReadSuperblock(drive
, buff
)) return 0;
966 if (DRIVE_WriteSuperblockEntry(drive
, 0x27, 4, (char *) &serial
)) return 0;
970 if (DOSDrives
[drive
].type
== DRIVE_CDROM
) return 0;
971 DOSDrives
[drive
].serial_conf
= serial
;
976 /***********************************************************************
979 static UINT
DRIVE_GetType( int drive
)
981 if (!DRIVE_IsValid( drive
)) return DRIVE_UNKNOWN
;
982 return DOSDrives
[drive
].type
;
986 /***********************************************************************
989 UINT
DRIVE_GetFlags( int drive
)
991 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
992 return DOSDrives
[drive
].flags
;
996 /***********************************************************************
999 int DRIVE_Chdir( int drive
, const char *path
)
1001 DOS_FULL_NAME full_name
;
1002 char buffer
[MAX_PATHNAME_LEN
];
1004 BY_HANDLE_FILE_INFORMATION info
;
1005 TDB
*pTask
= TASK_GetCurrent();
1007 strcpy( buffer
, "A:" );
1009 TRACE("(%s,%s)\n", buffer
, path
);
1010 lstrcpynA( buffer
+ 2, path
, sizeof(buffer
) - 2 );
1012 if (!DOSFS_GetFullName( buffer
, TRUE
, &full_name
)) return 0;
1013 if (!FILE_Stat( full_name
.long_name
, &info
)) return 0;
1014 if (!(info
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1016 SetLastError( ERROR_FILE_NOT_FOUND
);
1019 unix_cwd
= full_name
.long_name
+ strlen( DOSDrives
[drive
].root
);
1020 while (*unix_cwd
== '/') unix_cwd
++;
1022 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
1023 'A' + drive
, unix_cwd
, full_name
.short_name
+ 3 );
1025 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].dos_cwd
);
1026 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].unix_cwd
);
1027 DOSDrives
[drive
].dos_cwd
= heap_strdup( full_name
.short_name
+ 3 );
1028 DOSDrives
[drive
].unix_cwd
= heap_strdup( unix_cwd
);
1030 if (pTask
&& (pTask
->curdrive
& 0x80) &&
1031 ((pTask
->curdrive
& ~0x80) == drive
))
1033 lstrcpynA( pTask
->curdir
, full_name
.short_name
+ 2,
1034 sizeof(pTask
->curdir
) );
1035 DRIVE_LastTask
= GetCurrentTask();
1036 chdir(unix_cwd
); /* Only change if on current drive */
1042 /***********************************************************************
1045 int DRIVE_Disable( int drive
)
1047 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
1049 SetLastError( ERROR_INVALID_DRIVE
);
1052 DOSDrives
[drive
].flags
|= DRIVE_DISABLED
;
1057 /***********************************************************************
1060 int DRIVE_Enable( int drive
)
1062 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
1064 SetLastError( ERROR_INVALID_DRIVE
);
1067 DOSDrives
[drive
].flags
&= ~DRIVE_DISABLED
;
1072 /***********************************************************************
1073 * DRIVE_SetLogicalMapping
1075 int DRIVE_SetLogicalMapping ( int existing_drive
, int new_drive
)
1077 /* If new_drive is already valid, do nothing and return 0
1078 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1080 DOSDRIVE
*old
, *new;
1082 old
= DOSDrives
+ existing_drive
;
1083 new = DOSDrives
+ new_drive
;
1085 if ((existing_drive
< 0) || (existing_drive
>= MAX_DOS_DRIVES
) ||
1087 (new_drive
< 0) || (new_drive
>= MAX_DOS_DRIVES
))
1089 SetLastError( ERROR_INVALID_DRIVE
);
1095 TRACE("Can't map drive %c: to already existing drive %c:\n",
1096 'A' + existing_drive
, 'A' + new_drive
);
1097 /* it is already mapped there, so return success */
1098 if (!strcmp(old
->root
,new->root
))
1103 new->root
= heap_strdup( old
->root
);
1104 new->dos_cwd
= heap_strdup( old
->dos_cwd
);
1105 new->unix_cwd
= heap_strdup( old
->unix_cwd
);
1106 new->device
= heap_strdup( old
->device
);
1107 memcpy ( new->label_conf
, old
->label_conf
, 12 );
1108 memcpy ( new->label_read
, old
->label_read
, 12 );
1109 new->serial_conf
= old
->serial_conf
;
1110 new->type
= old
->type
;
1111 new->flags
= old
->flags
;
1112 new->dev
= old
->dev
;
1113 new->ino
= old
->ino
;
1115 TRACE("Drive %c: is now equal to drive %c:\n",
1116 'A' + new_drive
, 'A' + existing_drive
);
1122 /***********************************************************************
1125 * Open the drive raw device and return a Unix fd (or -1 on error).
1127 int DRIVE_OpenDevice( int drive
, int flags
)
1129 if (!DRIVE_IsValid( drive
)) return -1;
1130 return open( DOSDrives
[drive
].device
, flags
);
1134 /***********************************************************************
1137 * Read raw sectors from a device
1139 int DRIVE_RawRead(BYTE drive
, DWORD begin
, DWORD nr_sect
, BYTE
*dataptr
, BOOL fake_success
)
1143 if ((fd
= DRIVE_OpenDevice( drive
, O_RDONLY
)) != -1)
1145 lseek( fd
, begin
* 512, SEEK_SET
);
1146 /* FIXME: check errors */
1147 read( fd
, dataptr
, nr_sect
* 512 );
1152 memset(dataptr
, 0, nr_sect
* 512);
1155 if (begin
== 0 && nr_sect
> 1) *(dataptr
+ 512) = 0xf8;
1156 if (begin
== 1) *dataptr
= 0xf8;
1165 /***********************************************************************
1168 * Write raw sectors to a device
1170 int DRIVE_RawWrite(BYTE drive
, DWORD begin
, DWORD nr_sect
, BYTE
*dataptr
, BOOL fake_success
)
1174 if ((fd
= DRIVE_OpenDevice( drive
, O_RDONLY
)) != -1)
1176 lseek( fd
, begin
* 512, SEEK_SET
);
1177 /* FIXME: check errors */
1178 write( fd
, dataptr
, nr_sect
* 512 );
1182 if (!(fake_success
))
1189 /***********************************************************************
1190 * DRIVE_GetFreeSpace
1192 static int DRIVE_GetFreeSpace( int drive
, PULARGE_INTEGER size
,
1193 PULARGE_INTEGER available
)
1197 if (!DRIVE_IsValid(drive
))
1199 SetLastError( ERROR_INVALID_DRIVE
);
1203 /* FIXME: add autoconf check for this */
1204 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1205 if (statfs( DOSDrives
[drive
].root
, &info
, 0, 0) < 0)
1207 if (statfs( DOSDrives
[drive
].root
, &info
) < 0)
1211 WARN("cannot do statfs(%s)\n", DOSDrives
[drive
].root
);
1215 size
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bsize
, info
.f_blocks
);
1216 #ifdef STATFS_HAS_BAVAIL
1217 available
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bavail
, info
.f_bsize
);
1219 # ifdef STATFS_HAS_BFREE
1220 available
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bfree
, info
.f_bsize
);
1222 # error "statfs has no bfree/bavail member!"
1225 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
1226 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1227 available
->QuadPart
= 0;
1232 /***********************************************************************
1233 * DRIVE_GetCurrentDirectory
1234 * Returns "X:\\path\\etc\\".
1236 * Despite the API description, return required length including the
1237 * terminating null when buffer too small. This is the real behaviour.
1240 static UINT
DRIVE_GetCurrentDirectory( UINT buflen
, LPSTR buf
)
1243 const char *s
= DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1246 ret
= strlen(s
) + 3; /* length of WHOLE current directory */
1247 if (ret
>= buflen
) return ret
+ 1;
1248 lstrcpynA( buf
, "A:\\", min( 4u, buflen
) );
1249 if (buflen
) buf
[0] += DRIVE_GetCurrentDrive();
1250 if (buflen
> 3) lstrcpynA( buf
+ 3, s
, buflen
- 3 );
1255 /***********************************************************************
1258 * Build the environment array containing the drives' current directories.
1259 * Resulting pointer must be freed with HeapFree.
1261 char *DRIVE_BuildEnv(void)
1264 const char *cwd
[MAX_DOS_DRIVES
];
1267 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++)
1269 if ((cwd
[i
] = DRIVE_GetDosCwd(i
)) && cwd
[i
][0]) length
+= strlen(cwd
[i
]) + 8;
1271 if (!(env
= HeapAlloc( GetProcessHeap(), 0, length
+1 ))) return NULL
;
1272 for (i
= 0, p
= env
; i
< MAX_DOS_DRIVES
; i
++)
1274 if (cwd
[i
] && cwd
[i
][0])
1275 p
+= sprintf( p
, "=%c:=%c:\\%s", 'A'+i
, 'A'+i
, cwd
[i
] ) + 1;
1282 /***********************************************************************
1283 * GetDiskFreeSpace (KERNEL.422)
1285 BOOL16 WINAPI
GetDiskFreeSpace16( LPCSTR root
, LPDWORD cluster_sectors
,
1286 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1287 LPDWORD total_clusters
)
1289 return GetDiskFreeSpaceA( root
, cluster_sectors
, sector_bytes
,
1290 free_clusters
, total_clusters
);
1294 /***********************************************************************
1295 * GetDiskFreeSpaceA (KERNEL32.@)
1297 * Fails if expression resulting from current drive's dir and "root"
1298 * is not a root dir of the target drive.
1300 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1301 * if the corresponding info is unneeded.
1303 * FIXME: needs to support UNC names from Win95 OSR2 on.
1305 * Behaviour under Win95a:
1306 * CurrDir root result
1307 * "E:\\TEST" "E:" FALSE
1311 * "E:\\TEST" "\\" TRUE
1312 * "E:\\TEST" ":\\" FALSE
1313 * "E:\\TEST" "E:\\" TRUE
1314 * "E:\\TEST" "" FALSE
1315 * "E:\\" "" FALSE (!)
1317 * "E:\\TEST" 0x0 TRUE (!)
1318 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1319 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1321 BOOL WINAPI
GetDiskFreeSpaceA( LPCSTR root
, LPDWORD cluster_sectors
,
1322 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1323 LPDWORD total_clusters
)
1325 int drive
, sec_size
;
1326 ULARGE_INTEGER size
,available
;
1330 if ((!root
) || (strcmp(root
,"\\") == 0))
1331 drive
= DRIVE_GetCurrentDrive();
1333 if ( (strlen(root
) >= 2) && (root
[1] == ':')) /* root contains drive tag */
1335 drive
= toupper(root
[0]) - 'A';
1337 if (path
[0] == '\0')
1338 path
= DRIVE_GetDosCwd(drive
);
1340 if (path
[0] == '\\')
1342 if (path
[0]) /* oops, we are in a subdir */
1344 SetLastError(ERROR_INVALID_NAME
);
1350 SetLastError(ERROR_INVALID_NAME
);
1354 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1356 /* Cap the size and available at 2GB as per specs. */
1357 if ((size
.s
.HighPart
) ||(size
.s
.LowPart
> 0x7fffffff))
1359 size
.s
.HighPart
= 0;
1360 size
.s
.LowPart
= 0x7fffffff;
1362 if ((available
.s
.HighPart
) ||(available
.s
.LowPart
> 0x7fffffff))
1364 available
.s
.HighPart
=0;
1365 available
.s
.LowPart
= 0x7fffffff;
1367 sec_size
= (DRIVE_GetType(drive
)==DRIVE_CDROM
) ? 2048 : 512;
1368 size
.s
.LowPart
/= sec_size
;
1369 available
.s
.LowPart
/= sec_size
;
1370 /* FIXME: probably have to adjust those variables too for CDFS */
1372 while (cluster_sec
* 65536 < size
.s
.LowPart
) cluster_sec
*= 2;
1374 if (cluster_sectors
)
1375 *cluster_sectors
= cluster_sec
;
1377 *sector_bytes
= sec_size
;
1379 *free_clusters
= available
.s
.LowPart
/ cluster_sec
;
1381 *total_clusters
= size
.s
.LowPart
/ cluster_sec
;
1386 /***********************************************************************
1387 * GetDiskFreeSpaceW (KERNEL32.@)
1389 BOOL WINAPI
GetDiskFreeSpaceW( LPCWSTR root
, LPDWORD cluster_sectors
,
1390 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1391 LPDWORD total_clusters
)
1396 xroot
= HEAP_strdupWtoA( GetProcessHeap(), 0, root
);
1397 ret
= GetDiskFreeSpaceA( xroot
,cluster_sectors
, sector_bytes
,
1398 free_clusters
, total_clusters
);
1399 HeapFree( GetProcessHeap(), 0, xroot
);
1404 /***********************************************************************
1405 * GetDiskFreeSpaceExA (KERNEL32.@)
1407 * This function is used to acquire the size of the available and
1408 * total space on a logical volume.
1412 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1413 * detailed error information.
1416 BOOL WINAPI
GetDiskFreeSpaceExA( LPCSTR root
,
1417 PULARGE_INTEGER avail
,
1418 PULARGE_INTEGER total
,
1419 PULARGE_INTEGER totalfree
)
1422 ULARGE_INTEGER size
,available
;
1424 if (!root
) drive
= DRIVE_GetCurrentDrive();
1426 { /* C: always works for GetDiskFreeSpaceEx */
1427 if ((root
[1]) && ((root
[1] != ':') || (root
[2] && root
[2] != '\\')))
1429 FIXME("there are valid root names which are not supported yet\n");
1430 /* ..like UNC names, for instance. */
1432 WARN("invalid root '%s'\n", root
);
1435 drive
= toupper(root
[0]) - 'A';
1438 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1442 total
->s
.HighPart
= size
.s
.HighPart
;
1443 total
->s
.LowPart
= size
.s
.LowPart
;
1448 totalfree
->s
.HighPart
= available
.s
.HighPart
;
1449 totalfree
->s
.LowPart
= available
.s
.LowPart
;
1454 if (FIXME_ON(dosfs
))
1456 /* On Windows2000, we need to check the disk quota
1457 allocated for the user owning the calling process. We
1458 don't want to be more obtrusive than necessary with the
1459 FIXME messages, so don't print the FIXME unless Wine is
1460 actually masquerading as Windows2000. */
1463 ovi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOA
);
1464 if (GetVersionExA(&ovi
))
1466 if (ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
&& ovi
.dwMajorVersion
> 4)
1467 FIXME("no per-user quota support yet\n");
1471 /* Quick hack, should eventually be fixed to work 100% with
1472 Windows2000 (see comment above). */
1473 avail
->s
.HighPart
= available
.s
.HighPart
;
1474 avail
->s
.LowPart
= available
.s
.LowPart
;
1480 /***********************************************************************
1481 * GetDiskFreeSpaceExW (KERNEL32.@)
1483 BOOL WINAPI
GetDiskFreeSpaceExW( LPCWSTR root
, PULARGE_INTEGER avail
,
1484 PULARGE_INTEGER total
,
1485 PULARGE_INTEGER totalfree
)
1490 xroot
= HEAP_strdupWtoA( GetProcessHeap(), 0, root
);
1491 ret
= GetDiskFreeSpaceExA( xroot
, avail
, total
, totalfree
);
1492 HeapFree( GetProcessHeap(), 0, xroot
);
1496 /***********************************************************************
1497 * GetDriveType (KERNEL.136)
1498 * This function returns the type of a drive in Win16.
1499 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1500 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1501 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1502 * do any pseudo-clever changes.
1505 * drivetype DRIVE_xxx
1507 UINT16 WINAPI
GetDriveType16( UINT16 drive
) /* [in] number (NOT letter) of drive */
1509 UINT type
= DRIVE_GetType(drive
);
1510 TRACE("(%c:)\n", 'A' + drive
);
1511 if (type
== DRIVE_CDROM
) type
= DRIVE_REMOTE
;
1516 /***********************************************************************
1517 * GetDriveTypeA (KERNEL32.@)
1519 * Returns the type of the disk drive specified. If root is NULL the
1520 * root of the current directory is used.
1524 * Type of drive (from Win32 SDK):
1526 * DRIVE_UNKNOWN unable to find out anything about the drive
1527 * DRIVE_NO_ROOT_DIR nonexistent root dir
1528 * DRIVE_REMOVABLE the disk can be removed from the machine
1529 * DRIVE_FIXED the disk can not be removed from the machine
1530 * DRIVE_REMOTE network disk
1531 * DRIVE_CDROM CDROM drive
1532 * DRIVE_RAMDISK virtual disk in RAM
1534 UINT WINAPI
GetDriveTypeA(LPCSTR root
) /* [in] String describing drive */
1537 TRACE("(%s)\n", debugstr_a(root
));
1539 if (NULL
== root
) drive
= DRIVE_GetCurrentDrive();
1542 if ((root
[1]) && (root
[1] != ':'))
1544 WARN("invalid root %s\n", debugstr_a(root
));
1545 return DRIVE_NO_ROOT_DIR
;
1547 drive
= toupper(root
[0]) - 'A';
1549 return DRIVE_GetType(drive
);
1553 /***********************************************************************
1554 * GetDriveTypeW (KERNEL32.@)
1556 UINT WINAPI
GetDriveTypeW( LPCWSTR root
)
1558 LPSTR xpath
= HEAP_strdupWtoA( GetProcessHeap(), 0, root
);
1559 UINT ret
= GetDriveTypeA( xpath
);
1560 HeapFree( GetProcessHeap(), 0, xpath
);
1565 /***********************************************************************
1566 * GetCurrentDirectory (KERNEL.411)
1568 UINT16 WINAPI
GetCurrentDirectory16( UINT16 buflen
, LPSTR buf
)
1570 return (UINT16
)DRIVE_GetCurrentDirectory(buflen
, buf
);
1574 /***********************************************************************
1575 * GetCurrentDirectoryA (KERNEL32.@)
1577 UINT WINAPI
GetCurrentDirectoryA( UINT buflen
, LPSTR buf
)
1580 char longname
[MAX_PATHNAME_LEN
];
1581 char shortname
[MAX_PATHNAME_LEN
];
1582 ret
= DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN
, shortname
);
1583 if ( ret
> MAX_PATHNAME_LEN
) {
1584 ERR_(file
)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret
);
1587 GetLongPathNameA(shortname
, longname
, MAX_PATHNAME_LEN
);
1588 ret
= strlen( longname
) + 1;
1589 if (ret
> buflen
) return ret
;
1590 strcpy(buf
, longname
);
1594 /***********************************************************************
1595 * GetCurrentDirectoryW (KERNEL32.@)
1597 UINT WINAPI
GetCurrentDirectoryW( UINT buflen
, LPWSTR buf
)
1599 LPSTR xpath
= HeapAlloc( GetProcessHeap(), 0, buflen
+1 );
1600 UINT ret
= GetCurrentDirectoryA( buflen
, xpath
);
1601 if (ret
< buflen
) ret
= MultiByteToWideChar( CP_ACP
, 0, xpath
, -1, buf
, buflen
) - 1;
1602 HeapFree( GetProcessHeap(), 0, xpath
);
1607 /***********************************************************************
1608 * SetCurrentDirectory (KERNEL.412)
1610 BOOL16 WINAPI
SetCurrentDirectory16( LPCSTR dir
)
1612 return SetCurrentDirectoryA( dir
);
1616 /***********************************************************************
1617 * SetCurrentDirectoryA (KERNEL32.@)
1619 BOOL WINAPI
SetCurrentDirectoryA( LPCSTR dir
)
1621 int drive
, olddrive
= DRIVE_GetCurrentDrive();
1624 ERR_(file
)("(NULL)!\n");
1627 if (dir
[0] && (dir
[1]==':'))
1629 drive
= toupper( *dir
) - 'A';
1635 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1636 sets pTask->curdir only if pTask->curdrive is drive */
1637 if (!(DRIVE_SetCurrentDrive( drive
)))
1639 /* FIXME: what about empty strings? Add a \\ ? */
1640 if (!DRIVE_Chdir( drive
, dir
)) {
1641 DRIVE_SetCurrentDrive(olddrive
);
1648 /***********************************************************************
1649 * SetCurrentDirectoryW (KERNEL32.@)
1651 BOOL WINAPI
SetCurrentDirectoryW( LPCWSTR dirW
)
1653 LPSTR dir
= HEAP_strdupWtoA( GetProcessHeap(), 0, dirW
);
1654 BOOL res
= SetCurrentDirectoryA( dir
);
1655 HeapFree( GetProcessHeap(), 0, dir
);
1660 /***********************************************************************
1661 * GetLogicalDriveStringsA (KERNEL32.@)
1663 UINT WINAPI
GetLogicalDriveStringsA( UINT len
, LPSTR buffer
)
1667 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1668 if (DRIVE_IsValid(drive
)) count
++;
1669 if ((count
* 4) + 1 <= len
)
1672 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1673 if (DRIVE_IsValid(drive
))
1684 return (count
* 4) + 1; /* account for terminating null */
1685 /* The API tells about these different return values */
1689 /***********************************************************************
1690 * GetLogicalDriveStringsW (KERNEL32.@)
1692 UINT WINAPI
GetLogicalDriveStringsW( UINT len
, LPWSTR buffer
)
1696 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1697 if (DRIVE_IsValid(drive
)) count
++;
1698 if (count
* 4 * sizeof(WCHAR
) <= len
)
1701 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1702 if (DRIVE_IsValid(drive
))
1704 *p
++ = (WCHAR
)('a' + drive
);
1711 return count
* 4 * sizeof(WCHAR
);
1715 /***********************************************************************
1716 * GetLogicalDrives (KERNEL32.@)
1718 DWORD WINAPI
GetLogicalDrives(void)
1723 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1725 if ( (DRIVE_IsValid(drive
)) ||
1726 (DOSDrives
[drive
].type
== DRIVE_CDROM
)) /* audio CD is also valid */
1727 ret
|= (1 << drive
);
1733 /***********************************************************************
1734 * GetVolumeInformationA (KERNEL32.@)
1736 BOOL WINAPI
GetVolumeInformationA( LPCSTR root
, LPSTR label
,
1737 DWORD label_len
, DWORD
*serial
,
1738 DWORD
*filename_len
, DWORD
*flags
,
1739 LPSTR fsname
, DWORD fsname_len
)
1744 /* FIXME, SetLastError()s missing */
1746 if (!root
) drive
= DRIVE_GetCurrentDrive();
1749 if ((root
[1]) && (root
[1] != ':'))
1751 WARN("invalid root '%s'\n",root
);
1754 drive
= toupper(root
[0]) - 'A';
1756 if (!DRIVE_IsValid( drive
)) return FALSE
;
1759 lstrcpynA( label
, DRIVE_GetLabel(drive
), label_len
);
1760 cp
= label
+ strlen(label
);
1761 while (cp
!= label
&& *(cp
-1) == ' ') cp
--;
1764 if (serial
) *serial
= DRIVE_GetSerialNumber(drive
);
1766 /* Set the filesystem information */
1767 /* Note: we only emulate a FAT fs at present */
1770 if (DOSDrives
[drive
].flags
& DRIVE_SHORT_NAMES
)
1773 *filename_len
= 255;
1778 if (DOSDrives
[drive
].flags
& DRIVE_CASE_SENSITIVE
)
1779 *flags
|=FS_CASE_SENSITIVE
;
1780 if (DOSDrives
[drive
].flags
& DRIVE_CASE_PRESERVING
)
1781 *flags
|=FS_CASE_IS_PRESERVED
;
1784 /* Diablo checks that return code ... */
1785 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
1786 lstrcpynA( fsname
, "CDFS", fsname_len
);
1788 lstrcpynA( fsname
, "FAT", fsname_len
);
1794 /***********************************************************************
1795 * GetVolumeInformationW (KERNEL32.@)
1797 BOOL WINAPI
GetVolumeInformationW( LPCWSTR root
, LPWSTR label
,
1798 DWORD label_len
, DWORD
*serial
,
1799 DWORD
*filename_len
, DWORD
*flags
,
1800 LPWSTR fsname
, DWORD fsname_len
)
1802 LPSTR xroot
= HEAP_strdupWtoA( GetProcessHeap(), 0, root
);
1803 LPSTR xvolname
= label
? HeapAlloc(GetProcessHeap(),0,label_len
) : NULL
;
1804 LPSTR xfsname
= fsname
? HeapAlloc(GetProcessHeap(),0,fsname_len
) : NULL
;
1805 BOOL ret
= GetVolumeInformationA( xroot
, xvolname
, label_len
, serial
,
1806 filename_len
, flags
, xfsname
,
1810 if (label
) MultiByteToWideChar( CP_ACP
, 0, xvolname
, -1, label
, label_len
);
1811 if (fsname
) MultiByteToWideChar( CP_ACP
, 0, xfsname
, -1, fsname
, fsname_len
);
1813 HeapFree( GetProcessHeap(), 0, xroot
);
1814 HeapFree( GetProcessHeap(), 0, xvolname
);
1815 HeapFree( GetProcessHeap(), 0, xfsname
);
1819 /***********************************************************************
1820 * SetVolumeLabelA (KERNEL32.@)
1822 BOOL WINAPI
SetVolumeLabelA( LPCSTR root
, LPCSTR volname
)
1826 /* FIXME, SetLastErrors missing */
1828 if (!root
) drive
= DRIVE_GetCurrentDrive();
1831 if ((root
[1]) && (root
[1] != ':'))
1833 WARN("invalid root '%s'\n",root
);
1836 drive
= toupper(root
[0]) - 'A';
1838 if (!DRIVE_IsValid( drive
)) return FALSE
;
1840 /* some copy protection stuff check this */
1841 if (DOSDrives
[drive
].type
== DRIVE_CDROM
) return FALSE
;
1843 FIXME("(%s,%s),stub!\n", root
, volname
);
1847 /***********************************************************************
1848 * SetVolumeLabelW (KERNEL32.@)
1850 BOOL WINAPI
SetVolumeLabelW(LPCWSTR rootpath
,LPCWSTR volname
)
1855 xroot
= HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath
);
1856 xvol
= HEAP_strdupWtoA( GetProcessHeap(), 0, volname
);
1857 ret
= SetVolumeLabelA( xroot
, xvol
);
1858 HeapFree( GetProcessHeap(), 0, xroot
);
1859 HeapFree( GetProcessHeap(), 0, xvol
);