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>
42 #ifdef HAVE_SYS_PARAM_H
43 # include <sys/param.h>
45 #ifdef STATFS_DEFINED_BY_SYS_VFS
48 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
49 # include <sys/mount.h>
51 # ifdef STATFS_DEFINED_BY_SYS_STATFS
52 # include <sys/statfs.h>
57 #define NONAMELESSUNION
58 #define NONAMELESSSTRUCT
61 #include "wine/winbase16.h" /* for GetCurrentTask */
71 #include "wine/unicode.h"
72 #include "wine/library.h"
73 #include "wine/server.h"
74 #include "wine/debug.h"
76 WINE_DEFAULT_DEBUG_CHANNEL(dosfs
);
77 WINE_DECLARE_DEBUG_CHANNEL(file
);
81 char *root
; /* root dir in Unix format without trailing / */
82 LPWSTR dos_cwd
; /* cwd in DOS format without leading or trailing \ */
83 char *unix_cwd
; /* cwd in Unix format without leading or trailing / */
84 char *device
; /* raw device path */
85 WCHAR label_conf
[12]; /* drive label as cfg'd in wine config */
86 WCHAR label_read
[12]; /* drive label as read from device */
87 DWORD serial_conf
; /* drive serial number as cfg'd in wine config */
88 UINT type
; /* drive type */
89 UINT flags
; /* drive flags */
90 UINT codepage
; /* drive code page */
91 dev_t dev
; /* unix device number */
92 ino_t ino
; /* unix inode number */
96 static const WCHAR DRIVE_Types
[][8] =
98 { 0 }, /* DRIVE_UNKNOWN */
99 { 0 }, /* DRIVE_NO_ROOT_DIR */
100 {'f','l','o','p','p','y',0}, /* DRIVE_REMOVABLE */
101 {'h','d',0}, /* DRIVE_FIXED */
102 {'n','e','t','w','o','r','k',0}, /* DRIVE_REMOTE */
103 {'c','d','r','o','m',0}, /* DRIVE_CDROM */
104 {'r','a','m','d','i','s','k',0} /* DRIVE_RAMDISK */
108 /* Known filesystem types */
116 static const FS_DESCR DRIVE_Filesystems
[] =
118 { {'u','n','i','x',0}, DRIVE_CASE_SENSITIVE
| DRIVE_CASE_PRESERVING
},
119 { {'m','s','d','o','s',0}, DRIVE_SHORT_NAMES
},
120 { {'d','o','s',0}, DRIVE_SHORT_NAMES
},
121 { {'f','a','t',0}, DRIVE_SHORT_NAMES
},
122 { {'v','f','a','t',0}, DRIVE_CASE_PRESERVING
},
123 { {'w','i','n','9','5',0}, DRIVE_CASE_PRESERVING
},
128 static DOSDRIVE DOSDrives
[MAX_DOS_DRIVES
];
129 static int DRIVE_CurDrive
= -1;
131 static HTASK16 DRIVE_LastTask
= 0;
133 /* strdup on the process heap */
134 inline static char *heap_strdup( const char *str
)
136 INT len
= strlen(str
) + 1;
137 LPSTR p
= HeapAlloc( GetProcessHeap(), 0, len
);
138 if (p
) memcpy( p
, str
, len
);
142 extern void CDROM_InitRegistry(int dev
);
144 /***********************************************************************
147 static UINT
DRIVE_GetDriveType( LPCWSTR name
)
151 static const WCHAR TypeW
[] = {'T','y','p','e',0};
152 static const WCHAR hdW
[] = {'h','d',0};
154 PROFILE_GetWineIniString( name
, TypeW
, hdW
, buffer
, 20 );
157 for (i
= 0; i
< sizeof(DRIVE_Types
)/sizeof(DRIVE_Types
[0]); i
++)
159 if (!strcmpiW( buffer
, DRIVE_Types
[i
] )) return i
;
161 MESSAGE("%s: unknown drive type %s, defaulting to 'hd'.\n",
162 debugstr_w(name
), debugstr_w(buffer
) );
167 /***********************************************************************
170 static UINT
DRIVE_GetFSFlags( LPCWSTR name
, LPCWSTR value
)
172 const FS_DESCR
*descr
;
174 for (descr
= DRIVE_Filesystems
; *descr
->name
; descr
++)
175 if (!strcmpiW( value
, descr
->name
)) return descr
->flags
;
176 MESSAGE("%s: unknown filesystem type %s, defaulting to 'win95'.\n",
177 debugstr_w(name
), debugstr_w(value
) );
178 return DRIVE_CASE_PRESERVING
;
182 /***********************************************************************
187 int i
, len
, count
= 0;
188 WCHAR name
[] = {'D','r','i','v','e',' ','A',0};
189 WCHAR drive_env
[] = {'=','A',':',0};
190 WCHAR path
[MAX_PATHNAME_LEN
];
192 struct stat drive_stat_buffer
;
195 static const WCHAR PathW
[] = {'P','a','t','h',0};
196 static const WCHAR empty_strW
[] = { 0 };
197 static const WCHAR CodepageW
[] = {'C','o','d','e','p','a','g','e',0};
198 static const WCHAR LabelW
[] = {'L','a','b','e','l',0};
199 static const WCHAR SerialW
[] = {'S','e','r','i','a','l',0};
200 static const WCHAR zeroW
[] = {'0',0};
201 static const WCHAR def_serialW
[] = {'1','2','3','4','5','6','7','8',0};
202 static const WCHAR FilesystemW
[] = {'F','i','l','e','s','y','s','t','e','m',0};
203 static const WCHAR win95W
[] = {'w','i','n','9','5',0};
204 static const WCHAR DeviceW
[] = {'D','e','v','i','c','e',0};
205 static const WCHAR ReadVolInfoW
[] = {'R','e','a','d','V','o','l','I','n','f','o',0};
206 static const WCHAR FailReadOnlyW
[] = {'F','a','i','l','R','e','a','d','O','n','l','y',0};
207 static const WCHAR driveC_labelW
[] = {'D','r','i','v','e',' ','C',' ',' ',' ',' ',0};
209 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, name
[6]++, drive
++)
211 PROFILE_GetWineIniString( name
, PathW
, empty_strW
, path
, MAX_PATHNAME_LEN
);
214 /* Get the code page number */
215 PROFILE_GetWineIniString( name
, CodepageW
, zeroW
, /* 0 == CP_ACP */
217 drive
->codepage
= strtolW( buffer
, NULL
, 10 );
219 p
= path
+ strlenW(path
) - 1;
220 while ((p
> path
) && (*p
== '/')) *p
-- = '\0';
224 len
= WideCharToMultiByte(drive
->codepage
, 0, path
, -1, NULL
, 0, NULL
, NULL
);
225 drive
->root
= HeapAlloc(GetProcessHeap(), 0, len
);
226 WideCharToMultiByte(drive
->codepage
, 0, path
, -1, drive
->root
, len
, NULL
, NULL
);
230 /* relative paths are relative to config dir */
231 const char *config
= wine_get_config_dir();
232 len
= strlen(config
);
233 len
+= WideCharToMultiByte(drive
->codepage
, 0, path
, -1, NULL
, 0, NULL
, NULL
) + 2;
234 drive
->root
= HeapAlloc( GetProcessHeap(), 0, len
);
235 len
-= sprintf( drive
->root
, "%s/", config
);
236 WideCharToMultiByte(drive
->codepage
, 0, path
, -1, drive
->root
+ strlen(drive
->root
), len
, NULL
, NULL
);
239 if (stat( drive
->root
, &drive_stat_buffer
))
241 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
242 drive
->root
, strerror(errno
), 'A' + i
);
243 HeapFree( GetProcessHeap(), 0, drive
->root
);
247 if (!S_ISDIR(drive_stat_buffer
.st_mode
))
249 MESSAGE("%s is not a directory, ignoring drive %c:\n",
250 drive
->root
, 'A' + i
);
251 HeapFree( GetProcessHeap(), 0, drive
->root
);
256 drive
->dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(drive
->dos_cwd
[0]));
257 drive
->unix_cwd
= heap_strdup( "" );
258 drive
->type
= DRIVE_GetDriveType( name
);
259 drive
->device
= NULL
;
261 drive
->dev
= drive_stat_buffer
.st_dev
;
262 drive
->ino
= drive_stat_buffer
.st_ino
;
264 /* Get the drive label */
265 PROFILE_GetWineIniString( name
, LabelW
, empty_strW
, drive
->label_conf
, 12 );
266 if ((len
= strlenW(drive
->label_conf
)) < 11)
268 /* Pad label with spaces */
269 while(len
< 11) drive
->label_conf
[len
++] = ' ';
270 drive
->label_conf
[11] = '\0';
273 /* Get the serial number */
274 PROFILE_GetWineIniString( name
, SerialW
, def_serialW
, buffer
, 80 );
275 drive
->serial_conf
= strtoulW( buffer
, NULL
, 16 );
277 /* Get the filesystem type */
278 PROFILE_GetWineIniString( name
, FilesystemW
, win95W
, buffer
, 80 );
279 drive
->flags
= DRIVE_GetFSFlags( name
, buffer
);
282 PROFILE_GetWineIniString( name
, DeviceW
, empty_strW
, buffer
, 80 );
286 len
= WideCharToMultiByte(CP_ACP
, 0, buffer
, -1, NULL
, 0, NULL
, NULL
);
287 drive
->device
= HeapAlloc(GetProcessHeap(), 0, len
);
288 WideCharToMultiByte(drive
->codepage
, 0, buffer
, -1, drive
->device
, len
, NULL
, NULL
);
290 if (PROFILE_GetWineIniBool( name
, ReadVolInfoW
, 1))
291 drive
->flags
|= DRIVE_READ_VOL_INFO
;
293 if (drive
->type
== DRIVE_CDROM
)
295 if ((cd_fd
= open(drive
->device
, O_RDONLY
|O_NONBLOCK
)) != -1)
297 CDROM_InitRegistry(cd_fd
);
303 /* Get the FailReadOnly flag */
304 if (PROFILE_GetWineIniBool( name
, FailReadOnlyW
, 0 ))
305 drive
->flags
|= DRIVE_FAIL_READ_ONLY
;
307 /* Make the first hard disk the current drive */
308 if ((DRIVE_CurDrive
== -1) && (drive
->type
== DRIVE_FIXED
))
312 TRACE("%s: path=%s type=%s label=%s serial=%08lx "
313 "flags=%08x codepage=%u dev=%x ino=%x\n",
314 debugstr_w(name
), drive
->root
, debugstr_w(DRIVE_Types
[drive
->type
]),
315 debugstr_w(drive
->label_conf
), drive
->serial_conf
, drive
->flags
,
316 drive
->codepage
, (int)drive
->dev
, (int)drive
->ino
);
318 else WARN("%s: not defined\n", debugstr_w(name
) );
323 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
324 /* Create a C drive pointing to Unix root dir */
325 DOSDrives
[2].root
= heap_strdup( "/" );
326 DOSDrives
[2].dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(DOSDrives
[2].dos_cwd
[0]));
327 DOSDrives
[2].unix_cwd
= heap_strdup( "" );
328 strcpyW( DOSDrives
[2].label_conf
, driveC_labelW
);
329 DOSDrives
[2].serial_conf
= 12345678;
330 DOSDrives
[2].type
= DRIVE_FIXED
;
331 DOSDrives
[2].device
= NULL
;
332 DOSDrives
[2].flags
= 0;
336 /* Make sure the current drive is valid */
337 if (DRIVE_CurDrive
== -1)
339 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
341 if (drive
->root
&& !(drive
->flags
& DRIVE_DISABLED
))
349 /* get current working directory info for all drives */
350 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++, drive_env
[1]++)
352 if (!GetEnvironmentVariableW(drive_env
, path
, MAX_PATHNAME_LEN
)) continue;
354 if (toupperW(path
[0]) != drive_env
[1] || path
[1] != ':') continue;
355 DRIVE_Chdir( i
, path
+ 2 );
361 /***********************************************************************
364 int DRIVE_IsValid( int drive
)
366 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
367 return (DOSDrives
[drive
].root
&&
368 !(DOSDrives
[drive
].flags
& DRIVE_DISABLED
));
372 /***********************************************************************
373 * DRIVE_GetCurrentDrive
375 int DRIVE_GetCurrentDrive(void)
377 TDB
*pTask
= TASK_GetCurrent();
378 if (pTask
&& (pTask
->curdrive
& 0x80)) return pTask
->curdrive
& ~0x80;
379 return DRIVE_CurDrive
;
383 /***********************************************************************
384 * DRIVE_SetCurrentDrive
386 int DRIVE_SetCurrentDrive( int drive
)
388 TDB
*pTask
= TASK_GetCurrent();
389 if (!DRIVE_IsValid( drive
))
391 SetLastError( ERROR_INVALID_DRIVE
);
394 TRACE("%c:\n", 'A' + drive
);
395 DRIVE_CurDrive
= drive
;
396 if (pTask
) pTask
->curdrive
= drive
| 0x80;
401 /***********************************************************************
402 * DRIVE_FindDriveRoot
404 * Find a drive for which the root matches the beginning of the given path.
405 * This can be used to translate a Unix path into a drive + DOS path.
406 * Return value is the drive, or -1 on error. On success, path is modified
407 * to point to the beginning of the DOS path.
409 * Note: path must be in the encoding of the underlying Unix file system.
411 int DRIVE_FindDriveRoot( const char **path
)
413 /* Starting with the full path, check if the device and inode match any of
414 * the wine 'drives'. If not then remove the last path component and try
415 * again. If the last component was a '..' then skip a normal component
416 * since it's a directory that's ascended back out of.
418 int drive
, level
, len
;
419 char buffer
[MAX_PATHNAME_LEN
];
423 strcpy( buffer
, *path
);
424 while ((p
= strchr( buffer
, '\\' )) != NULL
)
426 len
= strlen(buffer
);
428 /* strip off trailing slashes */
429 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
434 if (stat( buffer
, &st
) == 0 && S_ISDIR( st
.st_mode
))
436 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
438 if (!DOSDrives
[drive
].root
||
439 (DOSDrives
[drive
].flags
& DRIVE_DISABLED
))
442 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
443 (DOSDrives
[drive
].ino
== st
.st_ino
))
445 if (len
== 1) len
= 0; /* preserve root slash in returned path */
446 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
447 *path
, 'A' + drive
, buffer
, *path
+ len
);
449 if (!**path
) *path
= "\\";
454 if (len
<= 1) return -1; /* reached root */
459 /* find start of the last path component */
460 while (len
> 1 && buffer
[len
- 1] != '/') len
--;
461 if (!buffer
[len
]) break; /* empty component -> reached root */
462 /* does removing it take us up a level? */
463 if (strcmp( buffer
+ len
, "." ) != 0)
464 level
+= strcmp( buffer
+ len
, ".." ) ? 1 : -1;
466 /* strip off trailing slashes */
467 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
473 /***********************************************************************
474 * DRIVE_FindDriveRootW
476 * Unicode version of DRIVE_FindDriveRoot.
478 int DRIVE_FindDriveRootW( LPCWSTR
*path
)
480 int drive
, rootdrive
= -1;
481 char buffer
[MAX_PATHNAME_LEN
];
483 int len
, match_len
= -1;
485 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
487 if (!DOSDrives
[drive
].root
||
488 (DOSDrives
[drive
].flags
& DRIVE_DISABLED
)) continue;
490 WideCharToMultiByte(DOSDrives
[drive
].codepage
, 0, *path
, -1,
491 buffer
, MAX_PATHNAME_LEN
, NULL
, NULL
);
493 len
= strlen(DOSDrives
[drive
].root
);
494 if(strncmp(DOSDrives
[drive
].root
, buffer
, len
))
496 if(len
<= match_len
) continue;
505 TRACE("%s -> drive %c:, root='%s', name=%s\n",
506 buffer
, 'A' + rootdrive
, DOSDrives
[rootdrive
].root
, debugstr_w(*path
) );
512 /***********************************************************************
515 const char * DRIVE_GetRoot( int drive
)
517 if (!DRIVE_IsValid( drive
)) return NULL
;
518 return DOSDrives
[drive
].root
;
522 /***********************************************************************
525 LPCWSTR
DRIVE_GetDosCwd( int drive
)
527 TDB
*pTask
= TASK_GetCurrent();
528 if (!DRIVE_IsValid( drive
)) return NULL
;
530 /* Check if we need to change the directory to the new task. */
531 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
532 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
533 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
535 static const WCHAR rootW
[] = {'\\',0};
536 WCHAR curdirW
[MAX_PATH
];
537 MultiByteToWideChar(CP_ACP
, 0, pTask
->curdir
, -1, curdirW
, MAX_PATH
);
538 /* Perform the task-switch */
539 if (!DRIVE_Chdir( drive
, curdirW
)) DRIVE_Chdir( drive
, rootW
);
540 DRIVE_LastTask
= GetCurrentTask();
542 return DOSDrives
[drive
].dos_cwd
;
546 /***********************************************************************
549 const char * DRIVE_GetUnixCwd( int drive
)
551 TDB
*pTask
= TASK_GetCurrent();
552 if (!DRIVE_IsValid( drive
)) return NULL
;
554 /* Check if we need to change the directory to the new task. */
555 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
556 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
557 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
559 static const WCHAR rootW
[] = {'\\',0};
560 WCHAR curdirW
[MAX_PATH
];
561 MultiByteToWideChar(CP_ACP
, 0, pTask
->curdir
, -1, curdirW
, MAX_PATH
);
562 /* Perform the task-switch */
563 if (!DRIVE_Chdir( drive
, curdirW
)) DRIVE_Chdir( drive
, rootW
);
564 DRIVE_LastTask
= GetCurrentTask();
566 return DOSDrives
[drive
].unix_cwd
;
570 /***********************************************************************
573 const char * DRIVE_GetDevice( int drive
)
575 return (DRIVE_IsValid( drive
)) ? DOSDrives
[drive
].device
: NULL
;
578 /******************************************************************
579 * static WORD CDROM_Data_FindBestVoldesc
583 static WORD
CDROM_Data_FindBestVoldesc(int fd
)
585 BYTE cur_vd_type
, max_vd_type
= 0;
586 unsigned int offs
, best_offs
= 0, extra_offs
= 0;
589 for (offs
= 0x8000; offs
<= 0x9800; offs
+= 0x800)
591 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
592 * the volume label is displaced forward by 8
594 lseek(fd
, offs
+ 11, SEEK_SET
); /* check for non-ISO9660 signature */
596 if ((sig
[0] == 'R') && (sig
[1] == 'O') && (sig
[2]=='M'))
600 lseek(fd
, offs
+ extra_offs
, SEEK_SET
);
601 read(fd
, &cur_vd_type
, 1);
602 if (cur_vd_type
== 0xff) /* voldesc set terminator */
604 if (cur_vd_type
> max_vd_type
)
606 max_vd_type
= cur_vd_type
;
607 best_offs
= offs
+ extra_offs
;
613 /***********************************************************************
614 * DRIVE_ReadSuperblock
617 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
618 * to check, that they are writing on a FAT filesystem !
620 int DRIVE_ReadSuperblock (int drive
, char * buff
)
622 #define DRIVE_SUPER 96
627 if (memset(buff
,0,DRIVE_SUPER
)!=buff
) return -1;
628 if ((fd
=open(DOSDrives
[drive
].device
,O_RDONLY
)) == -1)
631 if (!DOSDrives
[drive
].device
)
632 ERR("No device configured for drive %c: !\n", 'A'+drive
);
634 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives
[drive
].device
, 'A'+drive
,
635 (stat(DOSDrives
[drive
].device
, &st
)) ?
636 "not available or symlink not valid ?" : "no permission");
637 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
638 PROFILE_UsageWineIni();
642 switch(DOSDrives
[drive
].type
)
644 case DRIVE_REMOVABLE
:
649 offs
= CDROM_Data_FindBestVoldesc(fd
);
656 if ((offs
) && (lseek(fd
,offs
,SEEK_SET
)!=offs
))
661 if (read(fd
,buff
,DRIVE_SUPER
)!=DRIVE_SUPER
)
667 switch(DOSDrives
[drive
].type
)
669 case DRIVE_REMOVABLE
:
671 if ((buff
[0x26]!=0x29) || /* Check for FAT present */
672 /* FIXME: do really all FAT have their name beginning with
673 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
674 memcmp( buff
+0x36,"FAT",3))
676 ERR("The filesystem is not FAT !! (device=%s)\n",
677 DOSDrives
[drive
].device
);
683 if (strncmp(&buff
[1],"CD001",5)) /* Check for iso9660 present */
688 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
702 /***********************************************************************
703 * DRIVE_WriteSuperblockEntry
706 * We are writing as little as possible (ie. not the whole SuperBlock)
707 * not to interfere with kernel. The drive can be mounted !
709 int DRIVE_WriteSuperblockEntry (int drive
, off_t ofs
, size_t len
, char * buff
)
713 if ((fd
=open(DOSDrives
[drive
].device
,O_WRONLY
))==-1)
715 ERR("Cannot open the device %s (for writing)\n",
716 DOSDrives
[drive
].device
);
719 if (lseek(fd
,ofs
,SEEK_SET
)!=ofs
)
721 ERR("lseek failed on device %s !\n",
722 DOSDrives
[drive
].device
);
726 if (write(fd
,buff
,len
)!=len
)
729 ERR("Cannot write on %s !\n",
730 DOSDrives
[drive
].device
);
736 /******************************************************************
737 * static HANDLE CDROM_Open
741 static HANDLE
CDROM_Open(int drive
)
743 WCHAR root
[] = {'\\','\\','.','\\','A',':',0};
745 return CreateFileW(root
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, 0);
748 /**************************************************************************
749 * CDROM_Data_GetLabel [internal]
751 DWORD
CDROM_Data_GetLabel(int drive
, WCHAR
*label
)
753 #define LABEL_LEN 32+1
754 int dev
= open(DOSDrives
[drive
].device
, O_RDONLY
|O_NONBLOCK
);
755 WORD offs
= CDROM_Data_FindBestVoldesc(dev
);
756 WCHAR label_read
[LABEL_LEN
]; /* Unicode possible, too */
757 DWORD unicode_id
= 0;
761 if ((lseek(dev
, offs
+0x58, SEEK_SET
) == offs
+0x58)
762 && (read(dev
, &unicode_id
, 3) == 3))
764 int ver
= (unicode_id
& 0xff0000) >> 16;
766 if ((lseek(dev
, offs
+0x28, SEEK_SET
) != offs
+0x28)
767 || (read(dev
, &label_read
, LABEL_LEN
) != LABEL_LEN
))
771 if ((LOWORD(unicode_id
) == 0x2f25) /* Unicode ID */
772 && ((ver
== 0x40) || (ver
== 0x43) || (ver
== 0x45)))
773 { /* yippee, unicode */
776 for (i
=0; i
<LABEL_LEN
;i
++)
777 { /* Motorola -> Intel Unicode conversion :-\ */
779 label_read
[i
] = (ch
<< 8) | (ch
>> 8);
781 strncpyW(label
, label_read
, 11);
786 MultiByteToWideChar(DOSDrives
[drive
].codepage
, 0, (LPSTR
)label_read
, -1, label
, 11);
794 ERR("error reading label !\n");
798 /**************************************************************************
799 * CDROM_GetLabel [internal]
801 static DWORD
CDROM_GetLabel(int drive
, WCHAR
*label
)
803 HANDLE h
= CDROM_Open(drive
);
808 if (!h
|| !DeviceIoControl(h
, IOCTL_CDROM_DISK_TYPE
, NULL
, 0, &cdd
, sizeof(cdd
), &br
, 0))
811 switch (cdd
.DiskData
& 0x03)
813 case CDROM_DISK_DATA_TRACK
:
814 if (!CDROM_Data_GetLabel(drive
, label
))
817 case CDROM_DISK_AUDIO_TRACK
:
819 static const WCHAR audioCD
[] = {'A','u','d','i','o',' ','C','D',' ',' ',' ',0};
820 strcpyW(label
, audioCD
);
823 case CDROM_DISK_DATA_TRACK
|CDROM_DISK_AUDIO_TRACK
:
824 FIXME("Need to get the label of a mixed mode CD: not implemented yet !\n");
830 TRACE("CD: label is %s\n", debugstr_w(label
));
834 /***********************************************************************
837 LPCWSTR
DRIVE_GetLabel( int drive
)
840 char buff
[DRIVE_SUPER
];
843 if (!DRIVE_IsValid( drive
)) return NULL
;
844 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
846 read
= CDROM_GetLabel(drive
, DOSDrives
[drive
].label_read
);
849 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
851 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
852 ERR("Invalid or unreadable superblock on %s (%c:).\n",
853 DOSDrives
[drive
].device
, (char)(drive
+'A'));
855 if (DOSDrives
[drive
].type
== DRIVE_REMOVABLE
||
856 DOSDrives
[drive
].type
== DRIVE_FIXED
)
859 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
861 MultiByteToWideChar(DOSDrives
[drive
].codepage
, 0, buff
+offs
, 11,
862 DOSDrives
[drive
].label_read
, 11);
863 DOSDrives
[drive
].label_read
[11]='\0';
869 DOSDrives
[drive
].label_read
: DOSDrives
[drive
].label_conf
;
872 #define CDFRAMES_PERSEC 75
873 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
874 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
875 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
877 /**************************************************************************
878 * CDROM_Audio_GetSerial [internal]
880 static DWORD
CDROM_Audio_GetSerial(HANDLE h
)
882 unsigned long serial
= 0;
885 DWORD dwStart
, dwEnd
, br
;
888 if (!DeviceIoControl(h
, IOCTL_CDROM_READ_TOC
, NULL
, 0, &toc
, sizeof(toc
), &br
, 0))
892 * wMagic collects the wFrames from track 1
893 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
895 * There it is collected for correcting the serial when there are less than
898 wMagic
= toc
.TrackData
[0].Address
[2];
899 dwStart
= FRAME_OF_TOC(toc
, toc
.FirstTrack
);
901 for (i
= 0; i
<= toc
.LastTrack
- toc
.FirstTrack
; i
++) {
902 serial
+= (toc
.TrackData
[i
].Address
[0] << 16) |
903 (toc
.TrackData
[i
].Address
[1] << 8) | toc
.TrackData
[i
].Address
[2];
905 dwEnd
= FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1);
907 if (toc
.LastTrack
- toc
.FirstTrack
+ 1 < 3)
908 serial
+= wMagic
+ (dwEnd
- dwStart
);
913 /**************************************************************************
914 * CDROM_Data_GetSerial [internal]
916 static DWORD
CDROM_Data_GetSerial(int drive
)
918 int dev
= open(DOSDrives
[drive
].device
, O_RDONLY
|O_NONBLOCK
);
924 BYTE b0
= 0, b1
= 1, b2
= 2, b3
= 3;
927 if (dev
== -1) return 0;
928 offs
= CDROM_Data_FindBestVoldesc(dev
);
937 lseek(dev
, offs
, SEEK_SET
);
938 read(dev
, buf
, 2048);
940 * OK, another braindead one... argh. Just believe it.
941 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
942 * It's true and nobody will ever be able to change it.
944 ovi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOA
);
946 if ((ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) && (ovi
.dwMajorVersion
>= 4))
948 b0
= 3; b1
= 2; b2
= 1; b3
= 0;
950 for (i
= 0; i
< 2048; i
+= 4)
952 /* DON'T optimize this into DWORD !! (breaks overflow) */
953 serial
.p
[b0
] += buf
[i
+b0
];
954 serial
.p
[b1
] += buf
[i
+b1
];
955 serial
.p
[b2
] += buf
[i
+b2
];
956 serial
.p
[b3
] += buf
[i
+b3
];
963 /**************************************************************************
964 * CDROM_GetSerial [internal]
966 static DWORD
CDROM_GetSerial(int drive
)
969 HANDLE h
= CDROM_Open(drive
);
973 if (!h
|| ! !DeviceIoControl(h
, IOCTL_CDROM_DISK_TYPE
, NULL
, 0, &cdd
, sizeof(cdd
), &br
, 0))
976 switch (cdd
.DiskData
& 0x03)
978 case CDROM_DISK_DATA_TRACK
:
979 /* hopefully a data CD */
980 serial
= CDROM_Data_GetSerial(drive
);
982 case CDROM_DISK_AUDIO_TRACK
:
984 case CDROM_DISK_DATA_TRACK
|CDROM_DISK_AUDIO_TRACK
:
985 serial
= CDROM_Audio_GetSerial(h
);
992 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial
), LOWORD(serial
));
999 /***********************************************************************
1000 * DRIVE_GetSerialNumber
1002 DWORD
DRIVE_GetSerialNumber( int drive
)
1005 char buff
[DRIVE_SUPER
];
1007 TRACE("drive %d, type = %d\n", drive
, DOSDrives
[drive
].type
);
1009 if (!DRIVE_IsValid( drive
)) return 0;
1011 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
1013 switch(DOSDrives
[drive
].type
)
1015 case DRIVE_REMOVABLE
:
1017 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
1018 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
1019 " Maybe not FAT?\n" ,
1020 DOSDrives
[drive
].device
, 'A'+drive
);
1022 serial
= *((DWORD
*)(buff
+0x27));
1025 serial
= CDROM_GetSerial(drive
);
1028 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive
+'A');
1032 return (serial
) ? serial
: DOSDrives
[drive
].serial_conf
;
1036 /***********************************************************************
1037 * DRIVE_SetSerialNumber
1039 int DRIVE_SetSerialNumber( int drive
, DWORD serial
)
1041 char buff
[DRIVE_SUPER
];
1043 if (!DRIVE_IsValid( drive
)) return 0;
1045 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
1047 if ((DOSDrives
[drive
].type
!= DRIVE_REMOVABLE
) &&
1048 (DOSDrives
[drive
].type
!= DRIVE_FIXED
)) return 0;
1049 /* check, if the drive has a FAT filesystem */
1050 if (DRIVE_ReadSuperblock(drive
, buff
)) return 0;
1051 if (DRIVE_WriteSuperblockEntry(drive
, 0x27, 4, (char *) &serial
)) return 0;
1055 if (DOSDrives
[drive
].type
== DRIVE_CDROM
) return 0;
1056 DOSDrives
[drive
].serial_conf
= serial
;
1061 /***********************************************************************
1064 static UINT
DRIVE_GetType( int drive
)
1066 if (!DRIVE_IsValid( drive
)) return DRIVE_NO_ROOT_DIR
;
1067 return DOSDrives
[drive
].type
;
1071 /***********************************************************************
1074 UINT
DRIVE_GetFlags( int drive
)
1076 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
1077 return DOSDrives
[drive
].flags
;
1080 /***********************************************************************
1083 UINT
DRIVE_GetCodepage( int drive
)
1085 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
1086 return DOSDrives
[drive
].codepage
;
1090 /***********************************************************************
1093 int DRIVE_Chdir( int drive
, LPCWSTR path
)
1095 DOS_FULL_NAME full_name
;
1096 WCHAR buffer
[MAX_PATHNAME_LEN
];
1098 BY_HANDLE_FILE_INFORMATION info
;
1099 TDB
*pTask
= TASK_GetCurrent();
1101 buffer
[0] = 'A' + drive
;
1104 TRACE("(%s,%s)\n", debugstr_w(buffer
), debugstr_w(path
) );
1105 strncpyW( buffer
+ 2, path
, MAX_PATHNAME_LEN
- 2 );
1106 buffer
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
1108 if (!DOSFS_GetFullName( buffer
, TRUE
, &full_name
)) return 0;
1109 if (!FILE_Stat( full_name
.long_name
, &info
, NULL
)) return 0;
1110 if (!(info
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1112 SetLastError( ERROR_FILE_NOT_FOUND
);
1115 unix_cwd
= full_name
.long_name
+ strlen( DOSDrives
[drive
].root
);
1116 while (*unix_cwd
== '/') unix_cwd
++;
1118 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
1119 'A' + drive
, unix_cwd
, debugstr_w(full_name
.short_name
+ 3) );
1121 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].dos_cwd
);
1122 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].unix_cwd
);
1123 DOSDrives
[drive
].dos_cwd
= HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name
.short_name
) - 2) * sizeof(WCHAR
));
1124 strcpyW(DOSDrives
[drive
].dos_cwd
, full_name
.short_name
+ 3);
1125 DOSDrives
[drive
].unix_cwd
= heap_strdup( unix_cwd
);
1127 if (pTask
&& (pTask
->curdrive
& 0x80) &&
1128 ((pTask
->curdrive
& ~0x80) == drive
))
1130 WideCharToMultiByte(CP_ACP
, 0, full_name
.short_name
+ 2, -1,
1131 pTask
->curdir
, sizeof(pTask
->curdir
), NULL
, NULL
);
1132 DRIVE_LastTask
= GetCurrentTask();
1138 /***********************************************************************
1141 int DRIVE_Disable( int drive
)
1143 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
1145 SetLastError( ERROR_INVALID_DRIVE
);
1148 DOSDrives
[drive
].flags
|= DRIVE_DISABLED
;
1153 /***********************************************************************
1156 int DRIVE_Enable( int drive
)
1158 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
1160 SetLastError( ERROR_INVALID_DRIVE
);
1163 DOSDrives
[drive
].flags
&= ~DRIVE_DISABLED
;
1168 /***********************************************************************
1169 * DRIVE_SetLogicalMapping
1171 int DRIVE_SetLogicalMapping ( int existing_drive
, int new_drive
)
1173 /* If new_drive is already valid, do nothing and return 0
1174 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1176 DOSDRIVE
*old
, *new;
1178 old
= DOSDrives
+ existing_drive
;
1179 new = DOSDrives
+ new_drive
;
1181 if ((existing_drive
< 0) || (existing_drive
>= MAX_DOS_DRIVES
) ||
1183 (new_drive
< 0) || (new_drive
>= MAX_DOS_DRIVES
))
1185 SetLastError( ERROR_INVALID_DRIVE
);
1191 TRACE("Can't map drive %c: to already existing drive %c:\n",
1192 'A' + existing_drive
, 'A' + new_drive
);
1193 /* it is already mapped there, so return success */
1194 if (!strcmp(old
->root
,new->root
))
1199 new->root
= heap_strdup( old
->root
);
1200 new->dos_cwd
= HeapAlloc(GetProcessHeap(), 0, (strlenW(old
->dos_cwd
) + 1) * sizeof(WCHAR
));
1201 strcpyW(new->dos_cwd
, old
->dos_cwd
);
1202 new->unix_cwd
= heap_strdup( old
->unix_cwd
);
1203 new->device
= heap_strdup( old
->device
);
1204 memcpy ( new->label_conf
, old
->label_conf
, 12 );
1205 memcpy ( new->label_read
, old
->label_read
, 12 );
1206 new->serial_conf
= old
->serial_conf
;
1207 new->type
= old
->type
;
1208 new->flags
= old
->flags
;
1209 new->dev
= old
->dev
;
1210 new->ino
= old
->ino
;
1212 TRACE("Drive %c: is now equal to drive %c:\n",
1213 'A' + new_drive
, 'A' + existing_drive
);
1219 /***********************************************************************
1222 * Open the drive raw device and return a Unix fd (or -1 on error).
1224 int DRIVE_OpenDevice( int drive
, int flags
)
1226 if (!DRIVE_IsValid( drive
)) return -1;
1227 return open( DOSDrives
[drive
].device
, flags
);
1231 /***********************************************************************
1234 * Read raw sectors from a device
1236 int DRIVE_RawRead(BYTE drive
, DWORD begin
, DWORD nr_sect
, BYTE
*dataptr
, BOOL fake_success
)
1240 if ((fd
= DRIVE_OpenDevice( drive
, O_RDONLY
)) != -1)
1242 lseek( fd
, begin
* 512, SEEK_SET
);
1243 /* FIXME: check errors */
1244 read( fd
, dataptr
, nr_sect
* 512 );
1249 memset(dataptr
, 0, nr_sect
* 512);
1252 if (begin
== 0 && nr_sect
> 1) *(dataptr
+ 512) = 0xf8;
1253 if (begin
== 1) *dataptr
= 0xf8;
1262 /***********************************************************************
1265 * Write raw sectors to a device
1267 int DRIVE_RawWrite(BYTE drive
, DWORD begin
, DWORD nr_sect
, BYTE
*dataptr
, BOOL fake_success
)
1271 if ((fd
= DRIVE_OpenDevice( drive
, O_RDONLY
)) != -1)
1273 lseek( fd
, begin
* 512, SEEK_SET
);
1274 /* FIXME: check errors */
1275 write( fd
, dataptr
, nr_sect
* 512 );
1279 if (!(fake_success
))
1286 /***********************************************************************
1287 * DRIVE_GetFreeSpace
1289 static int DRIVE_GetFreeSpace( int drive
, PULARGE_INTEGER size
,
1290 PULARGE_INTEGER available
)
1294 if (!DRIVE_IsValid(drive
))
1296 SetLastError( ERROR_PATH_NOT_FOUND
);
1300 /* FIXME: add autoconf check for this */
1301 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1302 if (statfs( DOSDrives
[drive
].root
, &info
, 0, 0) < 0)
1304 if (statfs( DOSDrives
[drive
].root
, &info
) < 0)
1308 WARN("cannot do statfs(%s)\n", DOSDrives
[drive
].root
);
1312 size
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bsize
, info
.f_blocks
);
1313 #ifdef STATFS_HAS_BAVAIL
1314 available
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bavail
, info
.f_bsize
);
1316 # ifdef STATFS_HAS_BFREE
1317 available
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bfree
, info
.f_bsize
);
1319 # error "statfs has no bfree/bavail member!"
1322 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
1323 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1324 available
->QuadPart
= 0;
1329 /***********************************************************************
1330 * DRIVE_GetCurrentDirectory
1331 * Returns "X:\\path\\etc\\".
1333 * Despite the API description, return required length including the
1334 * terminating null when buffer too small. This is the real behaviour.
1336 static UINT
DRIVE_GetCurrentDirectory( UINT buflen
, LPWSTR buf
)
1339 LPCWSTR dos_cwd
= DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1340 static const WCHAR driveA_rootW
[] = {'A',':','\\',0};
1342 ret
= strlenW(dos_cwd
) + 3; /* length of WHOLE current directory */
1343 if (ret
>= buflen
) return ret
+ 1;
1345 strcpyW( buf
, driveA_rootW
);
1346 buf
[0] += DRIVE_GetCurrentDrive();
1347 strcatW( buf
, dos_cwd
);
1352 /***********************************************************************
1355 * Build the environment array containing the drives' current directories.
1356 * Resulting pointer must be freed with HeapFree.
1358 char *DRIVE_BuildEnv(void)
1361 LPCWSTR cwd
[MAX_DOS_DRIVES
];
1364 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++)
1366 if ((cwd
[i
] = DRIVE_GetDosCwd(i
)) && cwd
[i
][0])
1367 length
+= WideCharToMultiByte(DRIVE_GetCodepage(i
), 0,
1368 cwd
[i
], -1, NULL
, 0, NULL
, NULL
) + 7;
1370 if (!(env
= HeapAlloc( GetProcessHeap(), 0, length
+1 ))) return NULL
;
1371 for (i
= 0, p
= env
; i
< MAX_DOS_DRIVES
; i
++)
1373 if (cwd
[i
] && cwd
[i
][0])
1375 *p
++ = '='; *p
++ = 'A' + i
; *p
++ = ':';
1376 *p
++ = '='; *p
++ = 'A' + i
; *p
++ = ':'; *p
++ = '\\';
1377 WideCharToMultiByte(DRIVE_GetCodepage(i
), 0, cwd
[i
], -1, p
, 0x7fffffff, NULL
, NULL
);
1386 /***********************************************************************
1387 * GetDiskFreeSpace (KERNEL.422)
1389 BOOL16 WINAPI
GetDiskFreeSpace16( LPCSTR root
, LPDWORD cluster_sectors
,
1390 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1391 LPDWORD total_clusters
)
1393 return GetDiskFreeSpaceA( root
, cluster_sectors
, sector_bytes
,
1394 free_clusters
, total_clusters
);
1398 /***********************************************************************
1399 * GetDiskFreeSpaceW (KERNEL32.@)
1401 * Fails if expression resulting from current drive's dir and "root"
1402 * is not a root dir of the target drive.
1404 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1405 * if the corresponding info is unneeded.
1407 * FIXME: needs to support UNC names from Win95 OSR2 on.
1409 * Behaviour under Win95a:
1410 * CurrDir root result
1411 * "E:\\TEST" "E:" FALSE
1415 * "E:\\TEST" "\\" TRUE
1416 * "E:\\TEST" ":\\" FALSE
1417 * "E:\\TEST" "E:\\" TRUE
1418 * "E:\\TEST" "" FALSE
1419 * "E:\\" "" FALSE (!)
1421 * "E:\\TEST" 0x0 TRUE (!)
1422 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1423 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1425 BOOL WINAPI
GetDiskFreeSpaceW( LPCWSTR root
, LPDWORD cluster_sectors
,
1426 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1427 LPDWORD total_clusters
)
1429 int drive
, sec_size
;
1430 ULARGE_INTEGER size
,available
;
1434 TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root
), cluster_sectors
, sector_bytes
,
1435 free_clusters
, total_clusters
);
1437 if (!root
|| root
[0] == '\\' || root
[0] == '/')
1438 drive
= DRIVE_GetCurrentDrive();
1440 if (root
[0] && root
[1] == ':') /* root contains drive tag */
1442 drive
= toupperW(root
[0]) - 'A';
1444 if (path
[0] == '\0')
1446 path
= DRIVE_GetDosCwd(drive
);
1449 SetLastError(ERROR_PATH_NOT_FOUND
);
1454 if (path
[0] == '\\')
1457 if (path
[0]) /* oops, we are in a subdir */
1459 SetLastError(ERROR_INVALID_NAME
);
1466 SetLastError(ERROR_PATH_NOT_FOUND
);
1468 SetLastError(ERROR_INVALID_NAME
);
1472 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1474 /* Cap the size and available at 2GB as per specs. */
1475 if ((size
.s
.HighPart
) ||(size
.s
.LowPart
> 0x7fffffff))
1477 size
.s
.HighPart
= 0;
1478 size
.s
.LowPart
= 0x7fffffff;
1480 if ((available
.s
.HighPart
) ||(available
.s
.LowPart
> 0x7fffffff))
1482 available
.s
.HighPart
=0;
1483 available
.s
.LowPart
= 0x7fffffff;
1485 sec_size
= (DRIVE_GetType(drive
)==DRIVE_CDROM
) ? 2048 : 512;
1486 size
.s
.LowPart
/= sec_size
;
1487 available
.s
.LowPart
/= sec_size
;
1488 /* FIXME: probably have to adjust those variables too for CDFS */
1490 while (cluster_sec
* 65536 < size
.s
.LowPart
) cluster_sec
*= 2;
1492 if (cluster_sectors
)
1493 *cluster_sectors
= cluster_sec
;
1495 *sector_bytes
= sec_size
;
1497 *free_clusters
= available
.s
.LowPart
/ cluster_sec
;
1499 *total_clusters
= size
.s
.LowPart
/ cluster_sec
;
1504 /***********************************************************************
1505 * GetDiskFreeSpaceA (KERNEL32.@)
1507 BOOL WINAPI
GetDiskFreeSpaceA( LPCSTR root
, LPDWORD cluster_sectors
,
1508 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1509 LPDWORD total_clusters
)
1511 UNICODE_STRING rootW
;
1516 if(!RtlCreateUnicodeStringFromAsciiz(&rootW
, root
))
1518 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1523 rootW
.Buffer
= NULL
;
1525 ret
= GetDiskFreeSpaceW(rootW
.Buffer
, cluster_sectors
, sector_bytes
,
1526 free_clusters
, total_clusters
);
1527 RtlFreeUnicodeString(&rootW
);
1533 /***********************************************************************
1534 * GetDiskFreeSpaceExW (KERNEL32.@)
1536 * This function is used to acquire the size of the available and
1537 * total space on a logical volume.
1541 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1542 * detailed error information.
1545 BOOL WINAPI
GetDiskFreeSpaceExW( LPCWSTR root
,
1546 PULARGE_INTEGER avail
,
1547 PULARGE_INTEGER total
,
1548 PULARGE_INTEGER totalfree
)
1551 ULARGE_INTEGER size
,available
;
1553 if (!root
) drive
= DRIVE_GetCurrentDrive();
1555 { /* C: always works for GetDiskFreeSpaceEx */
1556 if ((root
[1]) && ((root
[1] != ':') || (root
[2] && root
[2] != '\\')))
1558 FIXME("there are valid root names which are not supported yet\n");
1559 /* ..like UNC names, for instance. */
1561 WARN("invalid root '%s'\n", debugstr_w(root
));
1564 drive
= toupperW(root
[0]) - 'A';
1567 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1571 total
->s
.HighPart
= size
.s
.HighPart
;
1572 total
->s
.LowPart
= size
.s
.LowPart
;
1577 totalfree
->s
.HighPart
= available
.s
.HighPart
;
1578 totalfree
->s
.LowPart
= available
.s
.LowPart
;
1583 if (FIXME_ON(dosfs
))
1585 /* On Windows2000, we need to check the disk quota
1586 allocated for the user owning the calling process. We
1587 don't want to be more obtrusive than necessary with the
1588 FIXME messages, so don't print the FIXME unless Wine is
1589 actually masquerading as Windows2000. */
1592 ovi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOA
);
1593 if (GetVersionExA(&ovi
))
1595 if (ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
&& ovi
.dwMajorVersion
> 4)
1596 FIXME("no per-user quota support yet\n");
1600 /* Quick hack, should eventually be fixed to work 100% with
1601 Windows2000 (see comment above). */
1602 avail
->s
.HighPart
= available
.s
.HighPart
;
1603 avail
->s
.LowPart
= available
.s
.LowPart
;
1609 /***********************************************************************
1610 * GetDiskFreeSpaceExA (KERNEL32.@)
1612 BOOL WINAPI
GetDiskFreeSpaceExA( LPCSTR root
, PULARGE_INTEGER avail
,
1613 PULARGE_INTEGER total
,
1614 PULARGE_INTEGER totalfree
)
1616 UNICODE_STRING rootW
;
1619 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
1620 else rootW
.Buffer
= NULL
;
1622 ret
= GetDiskFreeSpaceExW( rootW
.Buffer
, avail
, total
, totalfree
);
1624 RtlFreeUnicodeString(&rootW
);
1628 /***********************************************************************
1629 * GetDriveType (KERNEL.136)
1630 * This function returns the type of a drive in Win16.
1631 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1632 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1633 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1634 * do any pseudo-clever changes.
1637 * drivetype DRIVE_xxx
1639 UINT16 WINAPI
GetDriveType16( UINT16 drive
) /* [in] number (NOT letter) of drive */
1641 UINT type
= DRIVE_GetType(drive
);
1642 TRACE("(%c:)\n", 'A' + drive
);
1643 if (type
== DRIVE_CDROM
) type
= DRIVE_REMOTE
;
1648 /***********************************************************************
1649 * GetDriveTypeW (KERNEL32.@)
1651 * Returns the type of the disk drive specified. If root is NULL the
1652 * root of the current directory is used.
1656 * Type of drive (from Win32 SDK):
1658 * DRIVE_UNKNOWN unable to find out anything about the drive
1659 * DRIVE_NO_ROOT_DIR nonexistent root dir
1660 * DRIVE_REMOVABLE the disk can be removed from the machine
1661 * DRIVE_FIXED the disk can not be removed from the machine
1662 * DRIVE_REMOTE network disk
1663 * DRIVE_CDROM CDROM drive
1664 * DRIVE_RAMDISK virtual disk in RAM
1666 UINT WINAPI
GetDriveTypeW(LPCWSTR root
) /* [in] String describing drive */
1669 TRACE("(%s)\n", debugstr_w(root
));
1671 if (NULL
== root
) drive
= DRIVE_GetCurrentDrive();
1674 if ((root
[1]) && (root
[1] != ':'))
1676 WARN("invalid root %s\n", debugstr_w(root
));
1677 return DRIVE_NO_ROOT_DIR
;
1679 drive
= toupperW(root
[0]) - 'A';
1681 return DRIVE_GetType(drive
);
1685 /***********************************************************************
1686 * GetDriveTypeA (KERNEL32.@)
1688 UINT WINAPI
GetDriveTypeA( LPCSTR root
)
1690 UNICODE_STRING rootW
;
1695 if( !RtlCreateUnicodeStringFromAsciiz(&rootW
, root
))
1697 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1702 rootW
.Buffer
= NULL
;
1704 ret
= GetDriveTypeW(rootW
.Buffer
);
1706 RtlFreeUnicodeString(&rootW
);
1712 /***********************************************************************
1713 * GetCurrentDirectory (KERNEL.411)
1715 UINT16 WINAPI
GetCurrentDirectory16( UINT16 buflen
, LPSTR buf
)
1717 WCHAR cur_dirW
[MAX_PATH
];
1719 DRIVE_GetCurrentDirectory(MAX_PATH
, cur_dirW
);
1720 return (UINT16
)WideCharToMultiByte(CP_ACP
, 0, cur_dirW
, -1, buf
, buflen
, NULL
, NULL
);
1724 /***********************************************************************
1725 * GetCurrentDirectoryW (KERNEL32.@)
1727 UINT WINAPI
GetCurrentDirectoryW( UINT buflen
, LPWSTR buf
)
1730 WCHAR longname
[MAX_PATHNAME_LEN
];
1731 WCHAR shortname
[MAX_PATHNAME_LEN
];
1733 ret
= DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN
, shortname
);
1734 if ( ret
> MAX_PATHNAME_LEN
) {
1735 ERR_(file
)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret
);
1738 GetLongPathNameW(shortname
, longname
, MAX_PATHNAME_LEN
);
1739 ret
= strlenW( longname
) + 1;
1740 if (ret
> buflen
) return ret
;
1741 strcpyW(buf
, longname
);
1745 /***********************************************************************
1746 * GetCurrentDirectoryA (KERNEL32.@)
1748 UINT WINAPI
GetCurrentDirectoryA( UINT buflen
, LPSTR buf
)
1750 WCHAR bufferW
[MAX_PATH
];
1753 retW
= GetCurrentDirectoryW(MAX_PATH
, bufferW
);
1757 else if (retW
> MAX_PATH
)
1759 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1764 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, NULL
, 0, NULL
, NULL
);
1767 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buf
, buflen
, NULL
, NULL
);
1768 ret
--; /* length without 0 */
1775 /***********************************************************************
1776 * SetCurrentDirectory (KERNEL.412)
1778 BOOL16 WINAPI
SetCurrentDirectory16( LPCSTR dir
)
1780 return SetCurrentDirectoryA( dir
);
1784 /***********************************************************************
1785 * SetCurrentDirectoryW (KERNEL32.@)
1787 BOOL WINAPI
SetCurrentDirectoryW( LPCWSTR dir
)
1789 int drive
, olddrive
= DRIVE_GetCurrentDrive();
1793 SetLastError(ERROR_INVALID_PARAMETER
);
1796 if (dir
[0] && (dir
[1]==':'))
1798 drive
= toupperW( *dir
) - 'A';
1804 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1805 sets pTask->curdir only if pTask->curdrive is drive */
1806 if (!(DRIVE_SetCurrentDrive( drive
)))
1809 /* FIXME: what about empty strings? Add a \\ ? */
1810 if (!DRIVE_Chdir( drive
, dir
)) {
1811 DRIVE_SetCurrentDrive(olddrive
);
1818 /***********************************************************************
1819 * SetCurrentDirectoryA (KERNEL32.@)
1821 BOOL WINAPI
SetCurrentDirectoryA( LPCSTR dir
)
1823 UNICODE_STRING dirW
;
1828 SetLastError(ERROR_INVALID_PARAMETER
);
1832 if (RtlCreateUnicodeStringFromAsciiz(&dirW
, dir
))
1834 ret
= SetCurrentDirectoryW(dirW
.Buffer
);
1835 RtlFreeUnicodeString(&dirW
);
1838 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1843 /***********************************************************************
1844 * GetLogicalDriveStringsA (KERNEL32.@)
1846 UINT WINAPI
GetLogicalDriveStringsA( UINT len
, LPSTR buffer
)
1850 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1851 if (DRIVE_IsValid(drive
)) count
++;
1852 if ((count
* 4) + 1 <= len
)
1855 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1856 if (DRIVE_IsValid(drive
))
1867 return (count
* 4) + 1; /* account for terminating null */
1868 /* The API tells about these different return values */
1872 /***********************************************************************
1873 * GetLogicalDriveStringsW (KERNEL32.@)
1875 UINT WINAPI
GetLogicalDriveStringsW( UINT len
, LPWSTR buffer
)
1879 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1880 if (DRIVE_IsValid(drive
)) count
++;
1881 if (count
* 4 * sizeof(WCHAR
) <= len
)
1884 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1885 if (DRIVE_IsValid(drive
))
1887 *p
++ = (WCHAR
)('a' + drive
);
1894 return count
* 4 * sizeof(WCHAR
);
1898 /***********************************************************************
1899 * GetLogicalDrives (KERNEL32.@)
1901 DWORD WINAPI
GetLogicalDrives(void)
1906 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1908 if ( (DRIVE_IsValid(drive
)) ||
1909 (DOSDrives
[drive
].type
== DRIVE_CDROM
)) /* audio CD is also valid */
1910 ret
|= (1 << drive
);
1916 /***********************************************************************
1917 * GetVolumeInformationW (KERNEL32.@)
1919 BOOL WINAPI
GetVolumeInformationW( LPCWSTR root
, LPWSTR label
,
1920 DWORD label_len
, DWORD
*serial
,
1921 DWORD
*filename_len
, DWORD
*flags
,
1922 LPWSTR fsname
, DWORD fsname_len
)
1927 /* FIXME, SetLastError()s missing */
1929 if (!root
) drive
= DRIVE_GetCurrentDrive();
1932 if (root
[0] && root
[1] != ':')
1934 WARN("invalid root %s\n", debugstr_w(root
));
1937 drive
= toupperW(root
[0]) - 'A';
1939 if (!DRIVE_IsValid( drive
)) return FALSE
;
1940 if (label
&& label_len
)
1942 strncpyW( label
, DRIVE_GetLabel(drive
), label_len
);
1943 label
[label_len
- 1] = 0; /* ensure 0 termination */
1944 cp
= label
+ strlenW(label
);
1945 while (cp
!= label
&& *(cp
-1) == ' ') cp
--;
1948 if (serial
) *serial
= DRIVE_GetSerialNumber(drive
);
1950 /* Set the filesystem information */
1951 /* Note: we only emulate a FAT fs at present */
1954 if (DOSDrives
[drive
].flags
& DRIVE_SHORT_NAMES
)
1957 *filename_len
= 255;
1962 if (DOSDrives
[drive
].flags
& DRIVE_CASE_SENSITIVE
)
1963 *flags
|=FS_CASE_SENSITIVE
;
1964 if (DOSDrives
[drive
].flags
& DRIVE_CASE_PRESERVING
)
1965 *flags
|=FS_CASE_IS_PRESERVED
;
1967 if (fsname
&& fsname_len
)
1969 /* Diablo checks that return code ... */
1970 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
1972 static const WCHAR cdfsW
[] = {'C','D','F','S',0};
1973 strncpyW( fsname
, cdfsW
, fsname_len
);
1977 static const WCHAR fatW
[] = {'F','A','T',0};
1978 strncpyW( fsname
, fatW
, fsname_len
);
1980 fsname
[fsname_len
- 1] = 0; /* ensure 0 termination */
1986 /***********************************************************************
1987 * GetVolumeInformationA (KERNEL32.@)
1989 BOOL WINAPI
GetVolumeInformationA( LPCSTR root
, LPSTR label
,
1990 DWORD label_len
, DWORD
*serial
,
1991 DWORD
*filename_len
, DWORD
*flags
,
1992 LPSTR fsname
, DWORD fsname_len
)
1994 UNICODE_STRING rootW
;
1995 LPWSTR labelW
, fsnameW
;
1998 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
1999 else rootW
.Buffer
= NULL
;
2000 labelW
= label
? HeapAlloc(GetProcessHeap(), 0, label_len
* sizeof(WCHAR
)) : NULL
;
2001 fsnameW
= fsname
? HeapAlloc(GetProcessHeap(), 0, fsname_len
* sizeof(WCHAR
)) : NULL
;
2003 if ((ret
= GetVolumeInformationW(rootW
.Buffer
, labelW
, label_len
, serial
,
2004 filename_len
, flags
, fsnameW
, fsname_len
)))
2006 if (label
) WideCharToMultiByte(CP_ACP
, 0, labelW
, -1, label
, label_len
, NULL
, NULL
);
2007 if (fsname
) WideCharToMultiByte(CP_ACP
, 0, fsnameW
, -1, fsname
, fsname_len
, NULL
, NULL
);
2010 RtlFreeUnicodeString(&rootW
);
2011 if (labelW
) HeapFree( GetProcessHeap(), 0, labelW
);
2012 if (fsnameW
) HeapFree( GetProcessHeap(), 0, fsnameW
);
2016 /***********************************************************************
2017 * SetVolumeLabelW (KERNEL32.@)
2019 BOOL WINAPI
SetVolumeLabelW( LPCWSTR root
, LPCWSTR volname
)
2023 /* FIXME, SetLastErrors missing */
2025 if (!root
) drive
= DRIVE_GetCurrentDrive();
2028 if ((root
[1]) && (root
[1] != ':'))
2030 WARN("invalid root %s\n", debugstr_w(root
));
2033 drive
= toupperW(root
[0]) - 'A';
2035 if (!DRIVE_IsValid( drive
)) return FALSE
;
2037 /* some copy protection stuff check this */
2038 if (DOSDrives
[drive
].type
== DRIVE_CDROM
) return FALSE
;
2040 strncpyW(DOSDrives
[drive
].label_conf
, volname
, 12);
2041 DOSDrives
[drive
].label_conf
[12 - 1] = 0; /* ensure 0 termination */
2045 /***********************************************************************
2046 * SetVolumeLabelA (KERNEL32.@)
2048 BOOL WINAPI
SetVolumeLabelA(LPCSTR root
, LPCSTR volname
)
2050 UNICODE_STRING rootW
, volnameW
;
2053 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
2054 else rootW
.Buffer
= NULL
;
2055 if (volname
) RtlCreateUnicodeStringFromAsciiz(&volnameW
, volname
);
2056 else volnameW
.Buffer
= NULL
;
2058 ret
= SetVolumeLabelW( rootW
.Buffer
, volnameW
.Buffer
);
2060 RtlFreeUnicodeString(&rootW
);
2061 RtlFreeUnicodeString(&volnameW
);
2065 /***********************************************************************
2066 * GetVolumeNameForVolumeMountPointW (KERNEL32.@)
2068 DWORD WINAPI
GetVolumeNameForVolumeMountPointW(LPWSTR str
, DWORD a
, DWORD b
)
2070 FIXME("(%s, %lx, %lx): stub\n", debugstr_w(str
), a
, b
);