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 */
70 #include "wine/unicode.h"
71 #include "wine/library.h"
72 #include "wine/server.h"
73 #include "wine/debug.h"
75 WINE_DEFAULT_DEBUG_CHANNEL(dosfs
);
76 WINE_DECLARE_DEBUG_CHANNEL(file
);
80 char *root
; /* root dir in Unix format without trailing / */
81 LPWSTR dos_cwd
; /* cwd in DOS format without leading or trailing \ */
82 char *unix_cwd
; /* cwd in Unix format without leading or trailing / */
83 char *device
; /* raw device path */
84 WCHAR label_conf
[12]; /* drive label as cfg'd in wine config */
85 WCHAR label_read
[12]; /* drive label as read from device */
86 DWORD serial_conf
; /* drive serial number as cfg'd in wine config */
87 UINT type
; /* drive type */
88 UINT flags
; /* drive flags */
89 UINT codepage
; /* drive code page */
90 dev_t dev
; /* unix device number */
91 ino_t ino
; /* unix inode number */
95 static const WCHAR DRIVE_Types
[][8] =
97 { 0 }, /* DRIVE_UNKNOWN */
98 { 0 }, /* DRIVE_NO_ROOT_DIR */
99 {'f','l','o','p','p','y',0}, /* DRIVE_REMOVABLE */
100 {'h','d',0}, /* DRIVE_FIXED */
101 {'n','e','t','w','o','r','k',0}, /* DRIVE_REMOTE */
102 {'c','d','r','o','m',0}, /* DRIVE_CDROM */
103 {'r','a','m','d','i','s','k',0} /* DRIVE_RAMDISK */
107 /* Known filesystem types */
115 static const FS_DESCR DRIVE_Filesystems
[] =
117 { {'u','n','i','x',0}, DRIVE_CASE_SENSITIVE
| DRIVE_CASE_PRESERVING
},
118 { {'m','s','d','o','s',0}, DRIVE_SHORT_NAMES
},
119 { {'d','o','s',0}, DRIVE_SHORT_NAMES
},
120 { {'f','a','t',0}, DRIVE_SHORT_NAMES
},
121 { {'v','f','a','t',0}, DRIVE_CASE_PRESERVING
},
122 { {'w','i','n','9','5',0}, DRIVE_CASE_PRESERVING
},
127 static DOSDRIVE DOSDrives
[MAX_DOS_DRIVES
];
128 static int DRIVE_CurDrive
= -1;
130 static HTASK16 DRIVE_LastTask
= 0;
132 /* strdup on the process heap */
133 inline static char *heap_strdup( const char *str
)
135 INT len
= strlen(str
) + 1;
136 LPSTR p
= HeapAlloc( GetProcessHeap(), 0, len
);
137 if (p
) memcpy( p
, str
, len
);
141 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
143 extern void CDROM_InitRegistry(int dev
);
145 /***********************************************************************
148 static inline UINT
DRIVE_GetDriveType( INT drive
, LPCWSTR value
)
152 for (i
= 0; i
< sizeof(DRIVE_Types
)/sizeof(DRIVE_Types
[0]); i
++)
154 if (!strcmpiW( value
, DRIVE_Types
[i
] )) return i
;
156 MESSAGE("Drive %c: unknown drive type %s, defaulting to 'hd'.\n",
157 'A' + drive
, debugstr_w(value
) );
162 /***********************************************************************
165 static UINT
DRIVE_GetFSFlags( INT drive
, LPCWSTR value
)
167 const FS_DESCR
*descr
;
169 for (descr
= DRIVE_Filesystems
; *descr
->name
; descr
++)
170 if (!strcmpiW( value
, descr
->name
)) return descr
->flags
;
171 MESSAGE("Drive %c: unknown filesystem type %s, defaulting to 'win95'.\n",
172 'A' + drive
, debugstr_w(value
) );
173 return DRIVE_CASE_PRESERVING
;
177 /***********************************************************************
182 int i
, len
, count
= 0;
183 WCHAR driveW
[] = {'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
184 'W','i','n','e','\\','W','i','n','e','\\',
185 'C','o','n','f','i','g','\\','D','r','i','v','e',' ','A',0};
186 WCHAR drive_env
[] = {'=','A',':',0};
187 WCHAR path
[MAX_PATHNAME_LEN
];
188 char tmp
[MAX_PATHNAME_LEN
*sizeof(WCHAR
) + sizeof(KEY_VALUE_PARTIAL_INFORMATION
)];
189 struct stat drive_stat_buffer
;
194 OBJECT_ATTRIBUTES attr
;
195 UNICODE_STRING nameW
;
197 static const WCHAR PathW
[] = {'P','a','t','h',0};
198 static const WCHAR CodepageW
[] = {'C','o','d','e','p','a','g','e',0};
199 static const WCHAR LabelW
[] = {'L','a','b','e','l',0};
200 static const WCHAR SerialW
[] = {'S','e','r','i','a','l',0};
201 static const WCHAR TypeW
[] = {'T','y','p','e',0};
202 static const WCHAR FilesystemW
[] = {'F','i','l','e','s','y','s','t','e','m',0};
203 static const WCHAR DeviceW
[] = {'D','e','v','i','c','e',0};
204 static const WCHAR ReadVolInfoW
[] = {'R','e','a','d','V','o','l','I','n','f','o',0};
205 static const WCHAR FailReadOnlyW
[] = {'F','a','i','l','R','e','a','d','O','n','l','y',0};
206 static const WCHAR driveC_labelW
[] = {'D','r','i','v','e',' ','C',' ',' ',' ',' ',0};
209 attr
.Length
= sizeof(attr
);
210 attr
.RootDirectory
= 0;
211 attr
.ObjectName
= &nameW
;
213 attr
.SecurityDescriptor
= NULL
;
214 attr
.SecurityQualityOfService
= NULL
;
216 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
218 RtlInitUnicodeString( &nameW
, driveW
);
219 nameW
.Buffer
[(nameW
.Length
/ sizeof(WCHAR
)) - 1] = 'A' + i
;
220 if (NtOpenKey( &hkey
, KEY_ALL_ACCESS
, &attr
) != STATUS_SUCCESS
) continue;
222 /* Get the code page number */
223 RtlInitUnicodeString( &nameW
, CodepageW
);
224 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
226 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
227 drive
->codepage
= strtolW( data
, NULL
, 10 );
230 /* Get the root path */
231 RtlInitUnicodeString( &nameW
, PathW
);
232 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
234 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
235 ExpandEnvironmentStringsW( data
, path
, sizeof(path
)/sizeof(WCHAR
) );
237 p
= path
+ strlenW(path
) - 1;
238 while ((p
> path
) && (*p
== '/')) *p
-- = '\0';
242 len
= WideCharToMultiByte(drive
->codepage
, 0, path
, -1, NULL
, 0, NULL
, NULL
);
243 drive
->root
= HeapAlloc(GetProcessHeap(), 0, len
);
244 WideCharToMultiByte(drive
->codepage
, 0, path
, -1, drive
->root
, len
, NULL
, NULL
);
248 /* relative paths are relative to config dir */
249 const char *config
= wine_get_config_dir();
250 len
= strlen(config
);
251 len
+= WideCharToMultiByte(drive
->codepage
, 0, path
, -1, NULL
, 0, NULL
, NULL
) + 2;
252 drive
->root
= HeapAlloc( GetProcessHeap(), 0, len
);
253 len
-= sprintf( drive
->root
, "%s/", config
);
254 WideCharToMultiByte(drive
->codepage
, 0, path
, -1,
255 drive
->root
+ strlen(drive
->root
), len
, NULL
, NULL
);
258 if (stat( drive
->root
, &drive_stat_buffer
))
260 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
261 drive
->root
, strerror(errno
), 'A' + i
);
262 HeapFree( GetProcessHeap(), 0, drive
->root
);
266 if (!S_ISDIR(drive_stat_buffer
.st_mode
))
268 MESSAGE("%s is not a directory, ignoring drive %c:\n",
269 drive
->root
, 'A' + i
);
270 HeapFree( GetProcessHeap(), 0, drive
->root
);
275 drive
->dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(drive
->dos_cwd
[0]));
276 drive
->unix_cwd
= heap_strdup( "" );
277 drive
->device
= NULL
;
279 drive
->dev
= drive_stat_buffer
.st_dev
;
280 drive
->ino
= drive_stat_buffer
.st_ino
;
282 /* Get the drive type */
283 RtlInitUnicodeString( &nameW
, TypeW
);
284 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
286 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
287 drive
->type
= DRIVE_GetDriveType( i
, data
);
289 else drive
->type
= DRIVE_FIXED
;
291 /* Get the drive label */
292 RtlInitUnicodeString( &nameW
, LabelW
);
293 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
295 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
296 lstrcpynW( drive
->label_conf
, data
, 12 );
298 if ((len
= strlenW(drive
->label_conf
)) < 11)
300 /* Pad label with spaces */
301 while(len
< 11) drive
->label_conf
[len
++] = ' ';
302 drive
->label_conf
[11] = '\0';
305 /* Get the serial number */
306 RtlInitUnicodeString( &nameW
, SerialW
);
307 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
309 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
310 drive
->serial_conf
= strtoulW( data
, NULL
, 16 );
312 else drive
->serial_conf
= 12345678;
314 /* Get the filesystem type */
315 RtlInitUnicodeString( &nameW
, FilesystemW
);
316 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
318 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
319 drive
->flags
= DRIVE_GetFSFlags( i
, data
);
321 else drive
->flags
= DRIVE_CASE_PRESERVING
;
324 RtlInitUnicodeString( &nameW
, DeviceW
);
325 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
327 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
328 len
= WideCharToMultiByte(CP_ACP
, 0, data
, -1, NULL
, 0, NULL
, NULL
);
329 drive
->device
= HeapAlloc(GetProcessHeap(), 0, len
);
330 WideCharToMultiByte(drive
->codepage
, 0, data
, -1, drive
->device
, len
, NULL
, NULL
);
332 RtlInitUnicodeString( &nameW
, ReadVolInfoW
);
333 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
335 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
336 if (IS_OPTION_TRUE(data
[0])) drive
->flags
|= DRIVE_READ_VOL_INFO
;
338 else drive
->flags
|= DRIVE_READ_VOL_INFO
;
340 if (drive
->type
== DRIVE_CDROM
)
343 if ((cd_fd
= open(drive
->device
, O_RDONLY
|O_NONBLOCK
)) != -1)
345 CDROM_InitRegistry(cd_fd
);
351 /* Get the FailReadOnly flag */
352 RtlInitUnicodeString( &nameW
, FailReadOnlyW
);
353 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
355 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
356 if (IS_OPTION_TRUE(data
[0])) drive
->flags
|= DRIVE_FAIL_READ_ONLY
;
359 /* Make the first hard disk the current drive */
360 if ((DRIVE_CurDrive
== -1) && (drive
->type
== DRIVE_FIXED
))
364 TRACE("Drive %c: path=%s type=%s label=%s serial=%08lx "
365 "flags=%08x codepage=%u dev=%x ino=%x\n",
366 'A' + i
, drive
->root
, debugstr_w(DRIVE_Types
[drive
->type
]),
367 debugstr_w(drive
->label_conf
), drive
->serial_conf
, drive
->flags
,
368 drive
->codepage
, (int)drive
->dev
, (int)drive
->ino
);
377 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
378 /* Create a C drive pointing to Unix root dir */
379 DOSDrives
[2].root
= heap_strdup( "/" );
380 DOSDrives
[2].dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(DOSDrives
[2].dos_cwd
[0]));
381 DOSDrives
[2].unix_cwd
= heap_strdup( "" );
382 strcpyW( DOSDrives
[2].label_conf
, driveC_labelW
);
383 DOSDrives
[2].serial_conf
= 12345678;
384 DOSDrives
[2].type
= DRIVE_FIXED
;
385 DOSDrives
[2].device
= NULL
;
386 DOSDrives
[2].flags
= 0;
390 /* Make sure the current drive is valid */
391 if (DRIVE_CurDrive
== -1)
393 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
395 if (drive
->root
&& !(drive
->flags
& DRIVE_DISABLED
))
403 /* get current working directory info for all drives */
404 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++, drive_env
[1]++)
406 if (!GetEnvironmentVariableW(drive_env
, path
, MAX_PATHNAME_LEN
)) continue;
408 if (toupperW(path
[0]) != drive_env
[1] || path
[1] != ':') continue;
409 DRIVE_Chdir( i
, path
+ 2 );
415 /***********************************************************************
418 int DRIVE_IsValid( int drive
)
420 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
421 return (DOSDrives
[drive
].root
&&
422 !(DOSDrives
[drive
].flags
& DRIVE_DISABLED
));
426 /***********************************************************************
427 * DRIVE_GetCurrentDrive
429 int DRIVE_GetCurrentDrive(void)
431 TDB
*pTask
= GlobalLock16(GetCurrentTask());
432 if (pTask
&& (pTask
->curdrive
& 0x80)) return pTask
->curdrive
& ~0x80;
433 return DRIVE_CurDrive
;
437 /***********************************************************************
438 * DRIVE_SetCurrentDrive
440 int DRIVE_SetCurrentDrive( int drive
)
442 TDB
*pTask
= GlobalLock16(GetCurrentTask());
443 if (!DRIVE_IsValid( drive
))
445 SetLastError( ERROR_INVALID_DRIVE
);
448 TRACE("%c:\n", 'A' + drive
);
449 DRIVE_CurDrive
= drive
;
450 if (pTask
) pTask
->curdrive
= drive
| 0x80;
455 /***********************************************************************
456 * DRIVE_FindDriveRoot
458 * Find a drive for which the root matches the beginning of the given path.
459 * This can be used to translate a Unix path into a drive + DOS path.
460 * Return value is the drive, or -1 on error. On success, path is modified
461 * to point to the beginning of the DOS path.
463 * Note: path must be in the encoding of the underlying Unix file system.
465 int DRIVE_FindDriveRoot( const char **path
)
467 /* Starting with the full path, check if the device and inode match any of
468 * the wine 'drives'. If not then remove the last path component and try
469 * again. If the last component was a '..' then skip a normal component
470 * since it's a directory that's ascended back out of.
472 int drive
, level
, len
;
473 char buffer
[MAX_PATHNAME_LEN
];
477 strcpy( buffer
, *path
);
478 while ((p
= strchr( buffer
, '\\' )) != NULL
)
480 len
= strlen(buffer
);
482 /* strip off trailing slashes */
483 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
488 if (stat( buffer
, &st
) == 0 && S_ISDIR( st
.st_mode
))
490 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
492 if (!DOSDrives
[drive
].root
||
493 (DOSDrives
[drive
].flags
& DRIVE_DISABLED
))
496 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
497 (DOSDrives
[drive
].ino
== st
.st_ino
))
499 if (len
== 1) len
= 0; /* preserve root slash in returned path */
500 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
501 *path
, 'A' + drive
, buffer
, *path
+ len
);
503 if (!**path
) *path
= "\\";
508 if (len
<= 1) return -1; /* reached root */
513 /* find start of the last path component */
514 while (len
> 1 && buffer
[len
- 1] != '/') len
--;
515 if (!buffer
[len
]) break; /* empty component -> reached root */
516 /* does removing it take us up a level? */
517 if (strcmp( buffer
+ len
, "." ) != 0)
518 level
+= strcmp( buffer
+ len
, ".." ) ? 1 : -1;
520 /* strip off trailing slashes */
521 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
527 /***********************************************************************
528 * DRIVE_FindDriveRootW
530 * Unicode version of DRIVE_FindDriveRoot.
532 int DRIVE_FindDriveRootW( LPCWSTR
*path
)
534 int drive
, level
, len
;
535 WCHAR buffer
[MAX_PATHNAME_LEN
];
539 strcpyW( buffer
, *path
);
540 while ((p
= strchrW( buffer
, '\\' )) != NULL
)
542 len
= strlenW(buffer
);
544 /* strip off trailing slashes */
545 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
552 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
554 char buffA
[MAX_PATHNAME_LEN
];
556 if (!DOSDrives
[drive
].root
||
557 (DOSDrives
[drive
].flags
& DRIVE_DISABLED
))
560 if (codepage
!= DOSDrives
[drive
].codepage
)
562 WideCharToMultiByte( DOSDrives
[drive
].codepage
, 0, buffer
, -1,
563 buffA
, sizeof(buffA
), NULL
, NULL
);
564 if (stat( buffA
, &st
) == -1 || !S_ISDIR( st
.st_mode
))
569 codepage
= DOSDrives
[drive
].codepage
;
572 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
573 (DOSDrives
[drive
].ino
== st
.st_ino
))
575 static const WCHAR rootW
[] = {'\\',0};
577 if (len
== 1) len
= 0; /* preserve root slash in returned path */
578 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
579 debugstr_w(*path
), 'A' + drive
, debugstr_w(buffer
), debugstr_w(*path
+ len
));
581 if (!**path
) *path
= rootW
;
585 if (len
<= 1) return -1; /* reached root */
590 static const WCHAR dotW
[] = {'.',0};
591 static const WCHAR dotdotW
[] = {'.','.',0};
593 /* find start of the last path component */
594 while (len
> 1 && buffer
[len
- 1] != '/') len
--;
595 if (!buffer
[len
]) break; /* empty component -> reached root */
596 /* does removing it take us up a level? */
597 if (strcmpW( buffer
+ len
, dotW
) != 0)
598 level
+= strcmpW( buffer
+ len
, dotdotW
) ? 1 : -1;
600 /* strip off trailing slashes */
601 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
607 /***********************************************************************
610 const char * DRIVE_GetRoot( int drive
)
612 if (!DRIVE_IsValid( drive
)) return NULL
;
613 return DOSDrives
[drive
].root
;
617 /***********************************************************************
620 LPCWSTR
DRIVE_GetDosCwd( int drive
)
622 TDB
*pTask
= GlobalLock16(GetCurrentTask());
623 if (!DRIVE_IsValid( drive
)) return NULL
;
625 /* Check if we need to change the directory to the new task. */
626 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
627 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
628 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
630 static const WCHAR rootW
[] = {'\\',0};
631 WCHAR curdirW
[MAX_PATH
];
632 MultiByteToWideChar(CP_ACP
, 0, pTask
->curdir
, -1, curdirW
, MAX_PATH
);
633 /* Perform the task-switch */
634 if (!DRIVE_Chdir( drive
, curdirW
)) DRIVE_Chdir( drive
, rootW
);
635 DRIVE_LastTask
= GetCurrentTask();
637 return DOSDrives
[drive
].dos_cwd
;
641 /***********************************************************************
644 const char * DRIVE_GetUnixCwd( int drive
)
646 TDB
*pTask
= GlobalLock16(GetCurrentTask());
647 if (!DRIVE_IsValid( drive
)) return NULL
;
649 /* Check if we need to change the directory to the new task. */
650 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
651 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
652 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
654 static const WCHAR rootW
[] = {'\\',0};
655 WCHAR curdirW
[MAX_PATH
];
656 MultiByteToWideChar(CP_ACP
, 0, pTask
->curdir
, -1, curdirW
, MAX_PATH
);
657 /* Perform the task-switch */
658 if (!DRIVE_Chdir( drive
, curdirW
)) DRIVE_Chdir( drive
, rootW
);
659 DRIVE_LastTask
= GetCurrentTask();
661 return DOSDrives
[drive
].unix_cwd
;
665 /***********************************************************************
668 const char * DRIVE_GetDevice( int drive
)
670 return (DRIVE_IsValid( drive
)) ? DOSDrives
[drive
].device
: NULL
;
673 /******************************************************************
674 * static WORD CDROM_Data_FindBestVoldesc
678 static WORD
CDROM_Data_FindBestVoldesc(int fd
)
680 BYTE cur_vd_type
, max_vd_type
= 0;
681 unsigned int offs
, best_offs
= 0, extra_offs
= 0;
684 for (offs
= 0x8000; offs
<= 0x9800; offs
+= 0x800)
686 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
687 * the volume label is displaced forward by 8
689 lseek(fd
, offs
+ 11, SEEK_SET
); /* check for non-ISO9660 signature */
691 if ((sig
[0] == 'R') && (sig
[1] == 'O') && (sig
[2]=='M'))
695 lseek(fd
, offs
+ extra_offs
, SEEK_SET
);
696 read(fd
, &cur_vd_type
, 1);
697 if (cur_vd_type
== 0xff) /* voldesc set terminator */
699 if (cur_vd_type
> max_vd_type
)
701 max_vd_type
= cur_vd_type
;
702 best_offs
= offs
+ extra_offs
;
708 /***********************************************************************
709 * DRIVE_ReadSuperblock
712 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
713 * to check, that they are writing on a FAT filesystem !
715 int DRIVE_ReadSuperblock (int drive
, char * buff
)
717 #define DRIVE_SUPER 96
721 struct stat stat_buf
;
723 memset(buff
, 0, DRIVE_SUPER
);
724 /* O_NONBLOCK in case we're opening FIFO; will be reset later */
725 if ((fd
= open(DOSDrives
[drive
].device
, O_RDONLY
|O_NOCTTY
|O_NONBLOCK
)) != -1) {
726 if (fstat(fd
, &stat_buf
) < 0) { /* shouldn't happen since we just opened that file */
727 ERR("fstat() failed for opened device '%s' (drive %c:) ! IT SHOULDN'T HAPPEN !!!\n",
728 DOSDrives
[drive
].device
, 'A'+drive
);
730 } else if (!S_ISBLK(stat_buf
.st_mode
)) {
731 ERR("Device '%s' (drive %c:) is not a block device - check your config\n",
732 DOSDrives
[drive
].device
, 'A'+drive
);
734 /* reset O_NONBLOCK */
735 } else if (fcntl(fd
, F_SETFL
, 0) < 0 || fcntl(fd
, F_GETFL
) & O_NONBLOCK
) {
736 ERR("fcntl() failed to reset O_NONBLOCK for device '%s' (drive %c:)\n",
737 DOSDrives
[drive
].device
, 'A'+drive
);
745 if (!DOSDrives
[drive
].device
)
746 ERR("No device configured for drive %c: !\n", 'A'+drive
);
748 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives
[drive
].device
, 'A'+drive
,
749 (stat(DOSDrives
[drive
].device
, &stat_buf
)) ?
750 "not available or symlink not valid ?" : "no permission");
753 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
757 switch(DOSDrives
[drive
].type
)
759 case DRIVE_REMOVABLE
:
764 offs
= CDROM_Data_FindBestVoldesc(fd
);
771 if ((offs
) && (lseek(fd
,offs
,SEEK_SET
)!=offs
))
776 if (read(fd
,buff
,DRIVE_SUPER
)!=DRIVE_SUPER
)
782 switch(DOSDrives
[drive
].type
)
784 case DRIVE_REMOVABLE
:
786 if ((buff
[0x26]!=0x29) || /* Check for FAT present */
787 /* FIXME: do really all FAT have their name beginning with
788 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
789 memcmp( buff
+0x36,"FAT",3))
791 ERR("The filesystem is not FAT !! (device=%s)\n",
792 DOSDrives
[drive
].device
);
798 if (strncmp(&buff
[1],"CD001",5)) /* Check for iso9660 present */
803 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
817 /***********************************************************************
818 * DRIVE_WriteSuperblockEntry
821 * We are writing as little as possible (ie. not the whole SuperBlock)
822 * not to interfere with kernel. The drive can be mounted !
824 int DRIVE_WriteSuperblockEntry (int drive
, off_t ofs
, size_t len
, char * buff
)
828 if ((fd
=open(DOSDrives
[drive
].device
,O_WRONLY
))==-1)
830 ERR("Cannot open the device %s (for writing)\n",
831 DOSDrives
[drive
].device
);
834 if (lseek(fd
,ofs
,SEEK_SET
)!=ofs
)
836 ERR("lseek failed on device %s !\n",
837 DOSDrives
[drive
].device
);
841 if (write(fd
,buff
,len
)!=len
)
844 ERR("Cannot write on %s !\n",
845 DOSDrives
[drive
].device
);
851 /******************************************************************
852 * static HANDLE CDROM_Open
856 static HANDLE
CDROM_Open(int drive
)
858 WCHAR root
[] = {'\\','\\','.','\\','A',':',0};
860 return CreateFileW(root
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, 0);
863 /**************************************************************************
864 * CDROM_Data_GetLabel [internal]
866 DWORD
CDROM_Data_GetLabel(int drive
, WCHAR
*label
)
868 #define LABEL_LEN 32+1
869 int dev
= open(DOSDrives
[drive
].device
, O_RDONLY
|O_NONBLOCK
);
870 WORD offs
= CDROM_Data_FindBestVoldesc(dev
);
871 WCHAR label_read
[LABEL_LEN
]; /* Unicode possible, too */
872 DWORD unicode_id
= 0;
876 if ((lseek(dev
, offs
+0x58, SEEK_SET
) == offs
+0x58)
877 && (read(dev
, &unicode_id
, 3) == 3))
879 int ver
= (unicode_id
& 0xff0000) >> 16;
881 if ((lseek(dev
, offs
+0x28, SEEK_SET
) != offs
+0x28)
882 || (read(dev
, &label_read
, LABEL_LEN
) != LABEL_LEN
))
886 if ((LOWORD(unicode_id
) == 0x2f25) /* Unicode ID */
887 && ((ver
== 0x40) || (ver
== 0x43) || (ver
== 0x45)))
888 { /* yippee, unicode */
891 for (i
=0; i
<LABEL_LEN
;i
++)
892 { /* Motorola -> Intel Unicode conversion :-\ */
894 label_read
[i
] = (ch
<< 8) | (ch
>> 8);
896 strncpyW(label
, label_read
, 11);
901 MultiByteToWideChar(DOSDrives
[drive
].codepage
, 0, (LPSTR
)label_read
, -1, label
, 11);
909 ERR("error reading label !\n");
913 /**************************************************************************
914 * CDROM_GetLabel [internal]
916 static DWORD
CDROM_GetLabel(int drive
, WCHAR
*label
)
923 h
= CDROM_Open(drive
);
926 r
= DeviceIoControl(h
, IOCTL_CDROM_DISK_TYPE
, NULL
,
927 0, &cdd
, sizeof(cdd
), &br
, 0);
932 switch (cdd
.DiskData
& 0x03)
934 case CDROM_DISK_DATA_TRACK
:
935 if (!CDROM_Data_GetLabel(drive
, label
))
938 case CDROM_DISK_AUDIO_TRACK
:
940 static const WCHAR audioCD
[] = {'A','u','d','i','o',' ','C','D',' ',' ',' ',0};
941 strcpyW(label
, audioCD
);
944 case CDROM_DISK_DATA_TRACK
|CDROM_DISK_AUDIO_TRACK
:
945 FIXME("Need to get the label of a mixed mode CD!\n");
946 /* This assumes that the first track is a data track! */
947 /* I guess the correct way would be to enumerate all data tracks
948 and check each for the title */
949 if (!CDROM_Data_GetLabel(drive
, label
))
956 TRACE("CD: label is %s\n", debugstr_w(label
));
960 /***********************************************************************
963 LPCWSTR
DRIVE_GetLabel( int drive
)
966 char buff
[DRIVE_SUPER
];
969 if (!DRIVE_IsValid( drive
)) return NULL
;
970 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
972 read
= CDROM_GetLabel(drive
, DOSDrives
[drive
].label_read
);
975 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
977 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
978 ERR("Invalid or unreadable superblock on %s (%c:).\n",
979 DOSDrives
[drive
].device
, (char)(drive
+'A'));
981 if (DOSDrives
[drive
].type
== DRIVE_REMOVABLE
||
982 DOSDrives
[drive
].type
== DRIVE_FIXED
)
985 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
987 MultiByteToWideChar(DOSDrives
[drive
].codepage
, 0, buff
+offs
, 11,
988 DOSDrives
[drive
].label_read
, 11);
989 DOSDrives
[drive
].label_read
[11]='\0';
995 DOSDrives
[drive
].label_read
: DOSDrives
[drive
].label_conf
;
998 #define CDFRAMES_PERSEC 75
999 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
1000 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
1001 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
1003 /**************************************************************************
1004 * CDROM_Audio_GetSerial [internal]
1006 static DWORD
CDROM_Audio_GetSerial(HANDLE h
)
1008 unsigned long serial
= 0;
1011 DWORD dwStart
, dwEnd
, br
;
1014 if (!DeviceIoControl(h
, IOCTL_CDROM_READ_TOC
, NULL
, 0, &toc
, sizeof(toc
), &br
, 0))
1018 * wMagic collects the wFrames from track 1
1019 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
1021 * There it is collected for correcting the serial when there are less than
1024 wMagic
= toc
.TrackData
[0].Address
[2];
1025 dwStart
= FRAME_OF_TOC(toc
, toc
.FirstTrack
);
1027 for (i
= 0; i
<= toc
.LastTrack
- toc
.FirstTrack
; i
++) {
1028 serial
+= (toc
.TrackData
[i
].Address
[0] << 16) |
1029 (toc
.TrackData
[i
].Address
[1] << 8) | toc
.TrackData
[i
].Address
[2];
1031 dwEnd
= FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1);
1033 if (toc
.LastTrack
- toc
.FirstTrack
+ 1 < 3)
1034 serial
+= wMagic
+ (dwEnd
- dwStart
);
1039 /**************************************************************************
1040 * CDROM_Data_GetSerial [internal]
1042 static DWORD
CDROM_Data_GetSerial(int drive
)
1044 int dev
= open(DOSDrives
[drive
].device
, O_RDONLY
|O_NONBLOCK
);
1050 BYTE b0
= 0, b1
= 1, b2
= 2, b3
= 3;
1053 if (dev
== -1) return 0;
1054 offs
= CDROM_Data_FindBestVoldesc(dev
);
1063 lseek(dev
, offs
, SEEK_SET
);
1064 read(dev
, buf
, 2048);
1066 * OK, another braindead one... argh. Just believe it.
1067 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
1068 * It's true and nobody will ever be able to change it.
1070 ovi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOA
);
1071 GetVersionExA(&ovi
);
1072 if ((ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) && (ovi
.dwMajorVersion
>= 4))
1074 b0
= 3; b1
= 2; b2
= 1; b3
= 0;
1076 for (i
= 0; i
< 2048; i
+= 4)
1078 /* DON'T optimize this into DWORD !! (breaks overflow) */
1079 serial
.p
[b0
] += buf
[i
+b0
];
1080 serial
.p
[b1
] += buf
[i
+b1
];
1081 serial
.p
[b2
] += buf
[i
+b2
];
1082 serial
.p
[b3
] += buf
[i
+b3
];
1089 /**************************************************************************
1090 * CDROM_GetSerial [internal]
1092 static DWORD
CDROM_GetSerial(int drive
)
1096 CDROM_DISK_DATA cdd
;
1100 TRACE("%d\n", drive
);
1102 h
= CDROM_Open(drive
);
1105 r
= DeviceIoControl(h
, IOCTL_CDROM_DISK_TYPE
, NULL
,
1106 0, &cdd
, sizeof(cdd
), &br
, 0);
1113 switch (cdd
.DiskData
& 0x03)
1115 case CDROM_DISK_DATA_TRACK
:
1116 /* hopefully a data CD */
1117 serial
= CDROM_Data_GetSerial(drive
);
1119 case CDROM_DISK_AUDIO_TRACK
:
1121 case CDROM_DISK_DATA_TRACK
|CDROM_DISK_AUDIO_TRACK
:
1122 serial
= CDROM_Audio_GetSerial(h
);
1129 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial
), LOWORD(serial
));
1136 /***********************************************************************
1137 * DRIVE_GetSerialNumber
1139 DWORD
DRIVE_GetSerialNumber( int drive
)
1142 char buff
[DRIVE_SUPER
];
1144 TRACE("drive %d, type = %d\n", drive
, DOSDrives
[drive
].type
);
1146 if (!DRIVE_IsValid( drive
)) return 0;
1148 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
1150 switch(DOSDrives
[drive
].type
)
1152 case DRIVE_REMOVABLE
:
1154 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
1155 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
1156 " Maybe not FAT?\n" ,
1157 DOSDrives
[drive
].device
, 'A'+drive
);
1159 serial
= *((DWORD
*)(buff
+0x27));
1162 serial
= CDROM_GetSerial(drive
);
1165 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive
+'A');
1169 return (serial
) ? serial
: DOSDrives
[drive
].serial_conf
;
1173 /***********************************************************************
1174 * DRIVE_SetSerialNumber
1176 int DRIVE_SetSerialNumber( int drive
, DWORD serial
)
1178 char buff
[DRIVE_SUPER
];
1180 if (!DRIVE_IsValid( drive
)) return 0;
1182 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
1184 if ((DOSDrives
[drive
].type
!= DRIVE_REMOVABLE
) &&
1185 (DOSDrives
[drive
].type
!= DRIVE_FIXED
)) return 0;
1186 /* check, if the drive has a FAT filesystem */
1187 if (DRIVE_ReadSuperblock(drive
, buff
)) return 0;
1188 if (DRIVE_WriteSuperblockEntry(drive
, 0x27, 4, (char *) &serial
)) return 0;
1192 if (DOSDrives
[drive
].type
== DRIVE_CDROM
) return 0;
1193 DOSDrives
[drive
].serial_conf
= serial
;
1198 /***********************************************************************
1201 static UINT
DRIVE_GetType( int drive
)
1203 if (!DRIVE_IsValid( drive
)) return DRIVE_NO_ROOT_DIR
;
1204 return DOSDrives
[drive
].type
;
1208 /***********************************************************************
1211 UINT
DRIVE_GetFlags( int drive
)
1213 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
1214 return DOSDrives
[drive
].flags
;
1217 /***********************************************************************
1220 UINT
DRIVE_GetCodepage( int drive
)
1222 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
1223 return DOSDrives
[drive
].codepage
;
1227 /***********************************************************************
1230 int DRIVE_Chdir( int drive
, LPCWSTR path
)
1232 DOS_FULL_NAME full_name
;
1233 WCHAR buffer
[MAX_PATHNAME_LEN
];
1235 BY_HANDLE_FILE_INFORMATION info
;
1236 TDB
*pTask
= GlobalLock16(GetCurrentTask());
1238 buffer
[0] = 'A' + drive
;
1241 TRACE("(%s,%s)\n", debugstr_w(buffer
), debugstr_w(path
) );
1242 strncpyW( buffer
+ 2, path
, MAX_PATHNAME_LEN
- 2 );
1243 buffer
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
1245 if (!DOSFS_GetFullName( buffer
, TRUE
, &full_name
)) return 0;
1246 if (!FILE_Stat( full_name
.long_name
, &info
, NULL
)) return 0;
1247 if (!(info
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1249 SetLastError( ERROR_FILE_NOT_FOUND
);
1252 unix_cwd
= full_name
.long_name
+ strlen( DOSDrives
[drive
].root
);
1253 while (*unix_cwd
== '/') unix_cwd
++;
1255 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
1256 'A' + drive
, unix_cwd
, debugstr_w(full_name
.short_name
+ 3) );
1258 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].dos_cwd
);
1259 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].unix_cwd
);
1260 DOSDrives
[drive
].dos_cwd
= HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name
.short_name
) - 2) * sizeof(WCHAR
));
1261 strcpyW(DOSDrives
[drive
].dos_cwd
, full_name
.short_name
+ 3);
1262 DOSDrives
[drive
].unix_cwd
= heap_strdup( unix_cwd
);
1264 if (pTask
&& (pTask
->curdrive
& 0x80) &&
1265 ((pTask
->curdrive
& ~0x80) == drive
))
1267 WideCharToMultiByte(CP_ACP
, 0, full_name
.short_name
+ 2, -1,
1268 pTask
->curdir
, sizeof(pTask
->curdir
), NULL
, NULL
);
1269 DRIVE_LastTask
= GetCurrentTask();
1275 /***********************************************************************
1278 int DRIVE_Disable( int drive
)
1280 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
1282 SetLastError( ERROR_INVALID_DRIVE
);
1285 DOSDrives
[drive
].flags
|= DRIVE_DISABLED
;
1290 /***********************************************************************
1293 int DRIVE_Enable( int drive
)
1295 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
1297 SetLastError( ERROR_INVALID_DRIVE
);
1300 DOSDrives
[drive
].flags
&= ~DRIVE_DISABLED
;
1305 /***********************************************************************
1306 * DRIVE_SetLogicalMapping
1308 int DRIVE_SetLogicalMapping ( int existing_drive
, int new_drive
)
1310 /* If new_drive is already valid, do nothing and return 0
1311 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1313 DOSDRIVE
*old
, *new;
1315 old
= DOSDrives
+ existing_drive
;
1316 new = DOSDrives
+ new_drive
;
1318 if ((existing_drive
< 0) || (existing_drive
>= MAX_DOS_DRIVES
) ||
1320 (new_drive
< 0) || (new_drive
>= MAX_DOS_DRIVES
))
1322 SetLastError( ERROR_INVALID_DRIVE
);
1328 TRACE("Can't map drive %c: to already existing drive %c:\n",
1329 'A' + existing_drive
, 'A' + new_drive
);
1330 /* it is already mapped there, so return success */
1331 if (!strcmp(old
->root
,new->root
))
1336 new->root
= heap_strdup( old
->root
);
1337 new->dos_cwd
= HeapAlloc(GetProcessHeap(), 0, (strlenW(old
->dos_cwd
) + 1) * sizeof(WCHAR
));
1338 strcpyW(new->dos_cwd
, old
->dos_cwd
);
1339 new->unix_cwd
= heap_strdup( old
->unix_cwd
);
1340 new->device
= heap_strdup( old
->device
);
1341 memcpy ( new->label_conf
, old
->label_conf
, 12 );
1342 memcpy ( new->label_read
, old
->label_read
, 12 );
1343 new->serial_conf
= old
->serial_conf
;
1344 new->type
= old
->type
;
1345 new->flags
= old
->flags
;
1346 new->dev
= old
->dev
;
1347 new->ino
= old
->ino
;
1349 TRACE("Drive %c: is now equal to drive %c:\n",
1350 'A' + new_drive
, 'A' + existing_drive
);
1356 /***********************************************************************
1359 * Open the drive raw device and return a Unix fd (or -1 on error).
1361 int DRIVE_OpenDevice( int drive
, int flags
)
1363 if (!DRIVE_IsValid( drive
)) return -1;
1364 return open( DOSDrives
[drive
].device
, flags
);
1368 /***********************************************************************
1369 * DRIVE_GetFreeSpace
1371 static int DRIVE_GetFreeSpace( int drive
, PULARGE_INTEGER size
,
1372 PULARGE_INTEGER available
)
1376 if (!DRIVE_IsValid(drive
))
1378 SetLastError( ERROR_PATH_NOT_FOUND
);
1382 /* FIXME: add autoconf check for this */
1383 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1384 if (statfs( DOSDrives
[drive
].root
, &info
, 0, 0) < 0)
1386 if (statfs( DOSDrives
[drive
].root
, &info
) < 0)
1390 WARN("cannot do statfs(%s)\n", DOSDrives
[drive
].root
);
1394 size
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bsize
, info
.f_blocks
);
1395 #ifdef HAVE_STRUCT_STATFS_F_BAVAIL
1396 available
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bavail
, info
.f_bsize
);
1398 # ifdef HAVE_STRUCT_STATFS_F_BFREE
1399 available
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bfree
, info
.f_bsize
);
1401 # error "statfs has no bfree/bavail member!"
1404 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
1405 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1406 available
->QuadPart
= 0;
1411 /***********************************************************************
1412 * DRIVE_GetCurrentDirectory
1413 * Returns "X:\\path\\etc\\".
1415 * Despite the API description, return required length including the
1416 * terminating null when buffer too small. This is the real behaviour.
1418 static UINT
DRIVE_GetCurrentDirectory( UINT buflen
, LPWSTR buf
)
1421 LPCWSTR dos_cwd
= DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1422 static const WCHAR driveA_rootW
[] = {'A',':','\\',0};
1424 ret
= strlenW(dos_cwd
) + 3; /* length of WHOLE current directory */
1425 if (ret
>= buflen
) return ret
+ 1;
1427 strcpyW( buf
, driveA_rootW
);
1428 buf
[0] += DRIVE_GetCurrentDrive();
1429 strcatW( buf
, dos_cwd
);
1434 /***********************************************************************
1437 * Build the environment array containing the drives' current directories.
1438 * Resulting pointer must be freed with HeapFree.
1440 char *DRIVE_BuildEnv(void)
1443 LPCWSTR cwd
[MAX_DOS_DRIVES
];
1446 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++)
1448 if ((cwd
[i
] = DRIVE_GetDosCwd(i
)) && cwd
[i
][0])
1449 length
+= WideCharToMultiByte(DRIVE_GetCodepage(i
), 0,
1450 cwd
[i
], -1, NULL
, 0, NULL
, NULL
) + 7;
1452 if (!(env
= HeapAlloc( GetProcessHeap(), 0, length
+1 ))) return NULL
;
1453 for (i
= 0, p
= env
; i
< MAX_DOS_DRIVES
; i
++)
1455 if (cwd
[i
] && cwd
[i
][0])
1457 *p
++ = '='; *p
++ = 'A' + i
; *p
++ = ':';
1458 *p
++ = '='; *p
++ = 'A' + i
; *p
++ = ':'; *p
++ = '\\';
1459 WideCharToMultiByte(DRIVE_GetCodepage(i
), 0, cwd
[i
], -1, p
, 0x7fffffff, NULL
, NULL
);
1468 /***********************************************************************
1469 * GetDiskFreeSpace (KERNEL.422)
1471 BOOL16 WINAPI
GetDiskFreeSpace16( LPCSTR root
, LPDWORD cluster_sectors
,
1472 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1473 LPDWORD total_clusters
)
1475 return GetDiskFreeSpaceA( root
, cluster_sectors
, sector_bytes
,
1476 free_clusters
, total_clusters
);
1480 /***********************************************************************
1481 * GetDiskFreeSpaceW (KERNEL32.@)
1483 * Fails if expression resulting from current drive's dir and "root"
1484 * is not a root dir of the target drive.
1486 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1487 * if the corresponding info is unneeded.
1489 * FIXME: needs to support UNC names from Win95 OSR2 on.
1491 * Behaviour under Win95a:
1492 * CurrDir root result
1493 * "E:\\TEST" "E:" FALSE
1497 * "E:\\TEST" "\\" TRUE
1498 * "E:\\TEST" ":\\" FALSE
1499 * "E:\\TEST" "E:\\" TRUE
1500 * "E:\\TEST" "" FALSE
1501 * "E:\\" "" FALSE (!)
1503 * "E:\\TEST" 0x0 TRUE (!)
1504 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1505 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1507 BOOL WINAPI
GetDiskFreeSpaceW( LPCWSTR root
, LPDWORD cluster_sectors
,
1508 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1509 LPDWORD total_clusters
)
1511 int drive
, sec_size
;
1512 ULARGE_INTEGER size
,available
;
1516 TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root
), cluster_sectors
, sector_bytes
,
1517 free_clusters
, total_clusters
);
1519 if (!root
|| root
[0] == '\\' || root
[0] == '/')
1520 drive
= DRIVE_GetCurrentDrive();
1522 if (root
[0] && root
[1] == ':') /* root contains drive tag */
1524 drive
= toupperW(root
[0]) - 'A';
1526 if (path
[0] == '\0')
1528 path
= DRIVE_GetDosCwd(drive
);
1531 SetLastError(ERROR_PATH_NOT_FOUND
);
1536 if (path
[0] == '\\')
1539 if (path
[0]) /* oops, we are in a subdir */
1541 SetLastError(ERROR_INVALID_NAME
);
1548 SetLastError(ERROR_PATH_NOT_FOUND
);
1550 SetLastError(ERROR_INVALID_NAME
);
1554 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1556 /* Cap the size and available at 2GB as per specs. */
1557 if ((size
.s
.HighPart
) ||(size
.s
.LowPart
> 0x7fffffff))
1559 size
.s
.HighPart
= 0;
1560 size
.s
.LowPart
= 0x7fffffff;
1562 if ((available
.s
.HighPart
) ||(available
.s
.LowPart
> 0x7fffffff))
1564 available
.s
.HighPart
=0;
1565 available
.s
.LowPart
= 0x7fffffff;
1567 sec_size
= (DRIVE_GetType(drive
)==DRIVE_CDROM
) ? 2048 : 512;
1568 size
.s
.LowPart
/= sec_size
;
1569 available
.s
.LowPart
/= sec_size
;
1570 /* FIXME: probably have to adjust those variables too for CDFS */
1572 while (cluster_sec
* 65536 < size
.s
.LowPart
) cluster_sec
*= 2;
1574 if (cluster_sectors
)
1575 *cluster_sectors
= cluster_sec
;
1577 *sector_bytes
= sec_size
;
1579 *free_clusters
= available
.s
.LowPart
/ cluster_sec
;
1581 *total_clusters
= size
.s
.LowPart
/ cluster_sec
;
1586 /***********************************************************************
1587 * GetDiskFreeSpaceA (KERNEL32.@)
1589 BOOL WINAPI
GetDiskFreeSpaceA( LPCSTR root
, LPDWORD cluster_sectors
,
1590 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1591 LPDWORD total_clusters
)
1593 UNICODE_STRING rootW
;
1598 if(!RtlCreateUnicodeStringFromAsciiz(&rootW
, root
))
1600 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1605 rootW
.Buffer
= NULL
;
1607 ret
= GetDiskFreeSpaceW(rootW
.Buffer
, cluster_sectors
, sector_bytes
,
1608 free_clusters
, total_clusters
);
1609 RtlFreeUnicodeString(&rootW
);
1615 /***********************************************************************
1616 * GetDiskFreeSpaceExW (KERNEL32.@)
1618 * This function is used to acquire the size of the available and
1619 * total space on a logical volume.
1623 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1624 * detailed error information.
1627 BOOL WINAPI
GetDiskFreeSpaceExW( LPCWSTR root
,
1628 PULARGE_INTEGER avail
,
1629 PULARGE_INTEGER total
,
1630 PULARGE_INTEGER totalfree
)
1633 ULARGE_INTEGER size
,available
;
1635 if (!root
) drive
= DRIVE_GetCurrentDrive();
1637 { /* C: always works for GetDiskFreeSpaceEx */
1638 if ((root
[1]) && ((root
[1] != ':') || (root
[2] && root
[2] != '\\')))
1640 FIXME("there are valid root names which are not supported yet\n");
1641 /* ..like UNC names, for instance. */
1643 WARN("invalid root '%s'\n", debugstr_w(root
));
1646 drive
= toupperW(root
[0]) - 'A';
1649 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1653 total
->s
.HighPart
= size
.s
.HighPart
;
1654 total
->s
.LowPart
= size
.s
.LowPart
;
1659 totalfree
->s
.HighPart
= available
.s
.HighPart
;
1660 totalfree
->s
.LowPart
= available
.s
.LowPart
;
1665 if (FIXME_ON(dosfs
))
1667 /* On Windows2000, we need to check the disk quota
1668 allocated for the user owning the calling process. We
1669 don't want to be more obtrusive than necessary with the
1670 FIXME messages, so don't print the FIXME unless Wine is
1671 actually masquerading as Windows2000. */
1674 ovi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOA
);
1675 if (GetVersionExA(&ovi
))
1677 if (ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
&& ovi
.dwMajorVersion
> 4)
1678 FIXME("no per-user quota support yet\n");
1682 /* Quick hack, should eventually be fixed to work 100% with
1683 Windows2000 (see comment above). */
1684 avail
->s
.HighPart
= available
.s
.HighPart
;
1685 avail
->s
.LowPart
= available
.s
.LowPart
;
1691 /***********************************************************************
1692 * GetDiskFreeSpaceExA (KERNEL32.@)
1694 BOOL WINAPI
GetDiskFreeSpaceExA( LPCSTR root
, PULARGE_INTEGER avail
,
1695 PULARGE_INTEGER total
,
1696 PULARGE_INTEGER totalfree
)
1698 UNICODE_STRING rootW
;
1701 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
1702 else rootW
.Buffer
= NULL
;
1704 ret
= GetDiskFreeSpaceExW( rootW
.Buffer
, avail
, total
, totalfree
);
1706 RtlFreeUnicodeString(&rootW
);
1710 /***********************************************************************
1711 * GetDriveType (KERNEL.136)
1712 * This function returns the type of a drive in Win16.
1713 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1714 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1715 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1716 * do any pseudo-clever changes.
1719 * drivetype DRIVE_xxx
1721 UINT16 WINAPI
GetDriveType16( UINT16 drive
) /* [in] number (NOT letter) of drive */
1723 UINT type
= DRIVE_GetType(drive
);
1724 TRACE("(%c:)\n", 'A' + drive
);
1725 if (type
== DRIVE_CDROM
) type
= DRIVE_REMOTE
;
1730 /***********************************************************************
1731 * GetDriveTypeW (KERNEL32.@)
1733 * Returns the type of the disk drive specified. If root is NULL the
1734 * root of the current directory is used.
1738 * Type of drive (from Win32 SDK):
1740 * DRIVE_UNKNOWN unable to find out anything about the drive
1741 * DRIVE_NO_ROOT_DIR nonexistent root dir
1742 * DRIVE_REMOVABLE the disk can be removed from the machine
1743 * DRIVE_FIXED the disk can not be removed from the machine
1744 * DRIVE_REMOTE network disk
1745 * DRIVE_CDROM CDROM drive
1746 * DRIVE_RAMDISK virtual disk in RAM
1748 UINT WINAPI
GetDriveTypeW(LPCWSTR root
) /* [in] String describing drive */
1751 TRACE("(%s)\n", debugstr_w(root
));
1753 if (NULL
== root
) drive
= DRIVE_GetCurrentDrive();
1756 if ((root
[1]) && (root
[1] != ':'))
1758 WARN("invalid root %s\n", debugstr_w(root
));
1759 return DRIVE_NO_ROOT_DIR
;
1761 drive
= toupperW(root
[0]) - 'A';
1763 return DRIVE_GetType(drive
);
1767 /***********************************************************************
1768 * GetDriveTypeA (KERNEL32.@)
1770 UINT WINAPI
GetDriveTypeA( LPCSTR root
)
1772 UNICODE_STRING rootW
;
1777 if( !RtlCreateUnicodeStringFromAsciiz(&rootW
, root
))
1779 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1784 rootW
.Buffer
= NULL
;
1786 ret
= GetDriveTypeW(rootW
.Buffer
);
1788 RtlFreeUnicodeString(&rootW
);
1794 /***********************************************************************
1795 * GetCurrentDirectory (KERNEL.411)
1797 UINT16 WINAPI
GetCurrentDirectory16( UINT16 buflen
, LPSTR buf
)
1799 WCHAR cur_dirW
[MAX_PATH
];
1801 DRIVE_GetCurrentDirectory(MAX_PATH
, cur_dirW
);
1802 return (UINT16
)WideCharToMultiByte(CP_ACP
, 0, cur_dirW
, -1, buf
, buflen
, NULL
, NULL
);
1806 /***********************************************************************
1807 * GetCurrentDirectoryW (KERNEL32.@)
1809 UINT WINAPI
GetCurrentDirectoryW( UINT buflen
, LPWSTR buf
)
1812 WCHAR longname
[MAX_PATHNAME_LEN
];
1813 WCHAR shortname
[MAX_PATHNAME_LEN
];
1815 ret
= DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN
, shortname
);
1816 if ( ret
> MAX_PATHNAME_LEN
) {
1817 ERR_(file
)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret
);
1820 GetLongPathNameW(shortname
, longname
, MAX_PATHNAME_LEN
);
1821 ret
= strlenW( longname
) + 1;
1822 if (ret
> buflen
) return ret
;
1823 strcpyW(buf
, longname
);
1827 /***********************************************************************
1828 * GetCurrentDirectoryA (KERNEL32.@)
1830 UINT WINAPI
GetCurrentDirectoryA( UINT buflen
, LPSTR buf
)
1832 WCHAR bufferW
[MAX_PATH
];
1835 retW
= GetCurrentDirectoryW(MAX_PATH
, bufferW
);
1839 else if (retW
> MAX_PATH
)
1841 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1846 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, NULL
, 0, NULL
, NULL
);
1849 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buf
, buflen
, NULL
, NULL
);
1850 ret
--; /* length without 0 */
1857 /***********************************************************************
1858 * SetCurrentDirectory (KERNEL.412)
1860 BOOL16 WINAPI
SetCurrentDirectory16( LPCSTR dir
)
1862 return SetCurrentDirectoryA( dir
);
1866 /***********************************************************************
1867 * SetCurrentDirectoryW (KERNEL32.@)
1869 BOOL WINAPI
SetCurrentDirectoryW( LPCWSTR dir
)
1871 int drive
, olddrive
= DRIVE_GetCurrentDrive();
1875 SetLastError(ERROR_INVALID_PARAMETER
);
1878 if (dir
[0] && (dir
[1]==':'))
1880 drive
= toupperW( *dir
) - 'A';
1886 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1887 sets pTask->curdir only if pTask->curdrive is drive */
1888 if (!(DRIVE_SetCurrentDrive( drive
)))
1891 /* FIXME: what about empty strings? Add a \\ ? */
1892 if (!DRIVE_Chdir( drive
, dir
)) {
1893 DRIVE_SetCurrentDrive(olddrive
);
1900 /***********************************************************************
1901 * SetCurrentDirectoryA (KERNEL32.@)
1903 BOOL WINAPI
SetCurrentDirectoryA( LPCSTR dir
)
1905 UNICODE_STRING dirW
;
1910 SetLastError(ERROR_INVALID_PARAMETER
);
1914 if (RtlCreateUnicodeStringFromAsciiz(&dirW
, dir
))
1916 ret
= SetCurrentDirectoryW(dirW
.Buffer
);
1917 RtlFreeUnicodeString(&dirW
);
1920 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1925 /***********************************************************************
1926 * GetLogicalDriveStringsA (KERNEL32.@)
1928 UINT WINAPI
GetLogicalDriveStringsA( UINT len
, LPSTR buffer
)
1932 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1933 if (DRIVE_IsValid(drive
)) count
++;
1934 if ((count
* 4) + 1 <= len
)
1937 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1938 if (DRIVE_IsValid(drive
))
1949 return (count
* 4) + 1; /* account for terminating null */
1950 /* The API tells about these different return values */
1954 /***********************************************************************
1955 * GetLogicalDriveStringsW (KERNEL32.@)
1957 UINT WINAPI
GetLogicalDriveStringsW( UINT len
, LPWSTR buffer
)
1961 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1962 if (DRIVE_IsValid(drive
)) count
++;
1963 if (count
* 4 * sizeof(WCHAR
) <= len
)
1966 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1967 if (DRIVE_IsValid(drive
))
1969 *p
++ = (WCHAR
)('a' + drive
);
1976 return count
* 4 * sizeof(WCHAR
);
1980 /***********************************************************************
1981 * GetLogicalDrives (KERNEL32.@)
1983 DWORD WINAPI
GetLogicalDrives(void)
1988 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1990 if ( (DRIVE_IsValid(drive
)) ||
1991 (DOSDrives
[drive
].type
== DRIVE_CDROM
)) /* audio CD is also valid */
1992 ret
|= (1 << drive
);
1998 /***********************************************************************
1999 * GetVolumeInformationW (KERNEL32.@)
2001 BOOL WINAPI
GetVolumeInformationW( LPCWSTR root
, LPWSTR label
,
2002 DWORD label_len
, DWORD
*serial
,
2003 DWORD
*filename_len
, DWORD
*flags
,
2004 LPWSTR fsname
, DWORD fsname_len
)
2009 /* FIXME, SetLastError()s missing */
2011 if (!root
) drive
= DRIVE_GetCurrentDrive();
2014 if (root
[0] && root
[1] != ':')
2016 WARN("invalid root %s\n", debugstr_w(root
));
2019 drive
= toupperW(root
[0]) - 'A';
2021 if (!DRIVE_IsValid( drive
)) return FALSE
;
2022 if (label
&& label_len
)
2024 strncpyW( label
, DRIVE_GetLabel(drive
), label_len
);
2025 label
[label_len
- 1] = 0; /* ensure 0 termination */
2026 cp
= label
+ strlenW(label
);
2027 while (cp
!= label
&& *(cp
-1) == ' ') cp
--;
2030 if (serial
) *serial
= DRIVE_GetSerialNumber(drive
);
2032 /* Set the filesystem information */
2033 /* Note: we only emulate a FAT fs at present */
2036 if (DOSDrives
[drive
].flags
& DRIVE_SHORT_NAMES
)
2039 *filename_len
= 255;
2044 if (DOSDrives
[drive
].flags
& DRIVE_CASE_SENSITIVE
)
2045 *flags
|=FS_CASE_SENSITIVE
;
2046 if (DOSDrives
[drive
].flags
& DRIVE_CASE_PRESERVING
)
2047 *flags
|=FS_CASE_IS_PRESERVED
;
2049 if (fsname
&& fsname_len
)
2051 /* Diablo checks that return code ... */
2052 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
2054 static const WCHAR cdfsW
[] = {'C','D','F','S',0};
2055 strncpyW( fsname
, cdfsW
, fsname_len
);
2059 static const WCHAR fatW
[] = {'F','A','T',0};
2060 strncpyW( fsname
, fatW
, fsname_len
);
2062 fsname
[fsname_len
- 1] = 0; /* ensure 0 termination */
2068 /***********************************************************************
2069 * GetVolumeInformationA (KERNEL32.@)
2071 BOOL WINAPI
GetVolumeInformationA( LPCSTR root
, LPSTR label
,
2072 DWORD label_len
, DWORD
*serial
,
2073 DWORD
*filename_len
, DWORD
*flags
,
2074 LPSTR fsname
, DWORD fsname_len
)
2076 UNICODE_STRING rootW
;
2077 LPWSTR labelW
, fsnameW
;
2080 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
2081 else rootW
.Buffer
= NULL
;
2082 labelW
= label
? HeapAlloc(GetProcessHeap(), 0, label_len
* sizeof(WCHAR
)) : NULL
;
2083 fsnameW
= fsname
? HeapAlloc(GetProcessHeap(), 0, fsname_len
* sizeof(WCHAR
)) : NULL
;
2085 if ((ret
= GetVolumeInformationW(rootW
.Buffer
, labelW
, label_len
, serial
,
2086 filename_len
, flags
, fsnameW
, fsname_len
)))
2088 if (label
) WideCharToMultiByte(CP_ACP
, 0, labelW
, -1, label
, label_len
, NULL
, NULL
);
2089 if (fsname
) WideCharToMultiByte(CP_ACP
, 0, fsnameW
, -1, fsname
, fsname_len
, NULL
, NULL
);
2092 RtlFreeUnicodeString(&rootW
);
2093 if (labelW
) HeapFree( GetProcessHeap(), 0, labelW
);
2094 if (fsnameW
) HeapFree( GetProcessHeap(), 0, fsnameW
);
2098 /***********************************************************************
2099 * SetVolumeLabelW (KERNEL32.@)
2101 BOOL WINAPI
SetVolumeLabelW( LPCWSTR root
, LPCWSTR volname
)
2105 /* FIXME, SetLastErrors missing */
2107 if (!root
) drive
= DRIVE_GetCurrentDrive();
2110 if ((root
[1]) && (root
[1] != ':'))
2112 WARN("invalid root %s\n", debugstr_w(root
));
2115 drive
= toupperW(root
[0]) - 'A';
2117 if (!DRIVE_IsValid( drive
)) return FALSE
;
2119 /* some copy protection stuff check this */
2120 if (DOSDrives
[drive
].type
== DRIVE_CDROM
) return FALSE
;
2122 strncpyW(DOSDrives
[drive
].label_conf
, volname
, 12);
2123 DOSDrives
[drive
].label_conf
[12 - 1] = 0; /* ensure 0 termination */
2127 /***********************************************************************
2128 * SetVolumeLabelA (KERNEL32.@)
2130 BOOL WINAPI
SetVolumeLabelA(LPCSTR root
, LPCSTR volname
)
2132 UNICODE_STRING rootW
, volnameW
;
2135 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
2136 else rootW
.Buffer
= NULL
;
2137 if (volname
) RtlCreateUnicodeStringFromAsciiz(&volnameW
, volname
);
2138 else volnameW
.Buffer
= NULL
;
2140 ret
= SetVolumeLabelW( rootW
.Buffer
, volnameW
.Buffer
);
2142 RtlFreeUnicodeString(&rootW
);
2143 RtlFreeUnicodeString(&volnameW
);
2147 /***********************************************************************
2148 * GetVolumeNameForVolumeMountPointW (KERNEL32.@)
2150 DWORD WINAPI
GetVolumeNameForVolumeMountPointW(LPWSTR str
, DWORD a
, DWORD b
)
2152 FIXME("(%s, %lx, %lx): stub\n", debugstr_w(str
), a
, b
);