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"
35 #include <sys/types.h>
42 #ifdef HAVE_SYS_STATVFS_H
43 # include <sys/statvfs.h>
46 #define NONAMELESSUNION
47 #define NONAMELESSSTRUCT
53 #include "wine/winbase16.h" /* for GetCurrentTask */
60 #include "wine/unicode.h"
61 #include "wine/library.h"
62 #include "wine/server.h"
63 #include "wine/debug.h"
65 WINE_DEFAULT_DEBUG_CHANNEL(dosfs
);
66 WINE_DECLARE_DEBUG_CHANNEL(file
);
70 char *root
; /* root dir in Unix format without trailing / */
71 LPWSTR dos_cwd
; /* cwd in DOS format without leading or trailing \ */
72 char *unix_cwd
; /* cwd in Unix format without leading or trailing / */
73 char *device
; /* raw device path */
74 UINT type
; /* drive type */
75 UINT flags
; /* drive flags */
76 dev_t dev
; /* unix device number */
77 ino_t ino
; /* unix inode number */
81 static const WCHAR DRIVE_Types
[][8] =
83 { 0 }, /* DRIVE_UNKNOWN */
84 { 0 }, /* DRIVE_NO_ROOT_DIR */
85 {'f','l','o','p','p','y',0}, /* DRIVE_REMOVABLE */
86 {'h','d',0}, /* DRIVE_FIXED */
87 {'n','e','t','w','o','r','k',0}, /* DRIVE_REMOTE */
88 {'c','d','r','o','m',0}, /* DRIVE_CDROM */
89 {'r','a','m','d','i','s','k',0} /* DRIVE_RAMDISK */
93 /* Known filesystem types */
101 static const FS_DESCR DRIVE_Filesystems
[] =
103 { {'u','n','i','x',0}, DRIVE_CASE_SENSITIVE
| DRIVE_CASE_PRESERVING
},
104 { {'m','s','d','o','s',0}, DRIVE_SHORT_NAMES
},
105 { {'d','o','s',0}, DRIVE_SHORT_NAMES
},
106 { {'f','a','t',0}, DRIVE_SHORT_NAMES
},
107 { {'v','f','a','t',0}, DRIVE_CASE_PRESERVING
},
108 { {'w','i','n','9','5',0}, DRIVE_CASE_PRESERVING
},
113 static DOSDRIVE DOSDrives
[MAX_DOS_DRIVES
];
114 static int DRIVE_CurDrive
= -1;
116 static HTASK16 DRIVE_LastTask
= 0;
118 /* strdup on the process heap */
119 inline static char *heap_strdup( const char *str
)
121 INT len
= strlen(str
) + 1;
122 LPSTR p
= HeapAlloc( GetProcessHeap(), 0, len
);
123 if (p
) memcpy( p
, str
, len
);
127 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
129 extern void CDROM_InitRegistry(int dev
);
131 /***********************************************************************
134 static inline UINT
DRIVE_GetDriveType( INT drive
, LPCWSTR value
)
138 for (i
= 0; i
< sizeof(DRIVE_Types
)/sizeof(DRIVE_Types
[0]); i
++)
140 if (!strcmpiW( value
, DRIVE_Types
[i
] )) return i
;
142 MESSAGE("Drive %c: unknown drive type %s, defaulting to 'hd'.\n",
143 'A' + drive
, debugstr_w(value
) );
148 /***********************************************************************
151 static UINT
DRIVE_GetFSFlags( INT drive
, LPCWSTR value
)
153 const FS_DESCR
*descr
;
155 for (descr
= DRIVE_Filesystems
; *descr
->name
; descr
++)
156 if (!strcmpiW( value
, descr
->name
)) return descr
->flags
;
157 MESSAGE("Drive %c: unknown filesystem type %s, defaulting to 'win95'.\n",
158 'A' + drive
, debugstr_w(value
) );
159 return DRIVE_CASE_PRESERVING
;
163 /***********************************************************************
168 int i
, len
, count
= 0;
169 WCHAR driveW
[] = {'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
170 'W','i','n','e','\\','W','i','n','e','\\',
171 'C','o','n','f','i','g','\\','D','r','i','v','e',' ','A',0};
172 WCHAR drive_env
[] = {'=','A',':',0};
173 WCHAR path
[MAX_PATHNAME_LEN
];
174 char tmp
[MAX_PATHNAME_LEN
*sizeof(WCHAR
) + sizeof(KEY_VALUE_PARTIAL_INFORMATION
)];
175 struct stat drive_stat_buffer
;
180 OBJECT_ATTRIBUTES attr
;
181 UNICODE_STRING nameW
;
183 static const WCHAR PathW
[] = {'P','a','t','h',0};
184 static const WCHAR TypeW
[] = {'T','y','p','e',0};
185 static const WCHAR FilesystemW
[] = {'F','i','l','e','s','y','s','t','e','m',0};
186 static const WCHAR DeviceW
[] = {'D','e','v','i','c','e',0};
187 static const WCHAR FailReadOnlyW
[] = {'F','a','i','l','R','e','a','d','O','n','l','y',0};
189 attr
.Length
= sizeof(attr
);
190 attr
.RootDirectory
= 0;
191 attr
.ObjectName
= &nameW
;
193 attr
.SecurityDescriptor
= NULL
;
194 attr
.SecurityQualityOfService
= NULL
;
196 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
198 RtlInitUnicodeString( &nameW
, driveW
);
199 nameW
.Buffer
[(nameW
.Length
/ sizeof(WCHAR
)) - 1] = 'A' + i
;
200 if (NtOpenKey( &hkey
, KEY_ALL_ACCESS
, &attr
) != STATUS_SUCCESS
) continue;
202 /* Get the root path */
203 RtlInitUnicodeString( &nameW
, PathW
);
204 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
206 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
207 ExpandEnvironmentStringsW( data
, path
, sizeof(path
)/sizeof(WCHAR
) );
209 p
= path
+ strlenW(path
) - 1;
210 while ((p
> path
) && (*p
== '/')) *p
-- = '\0';
214 len
= WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1, NULL
, 0, NULL
, NULL
);
215 drive
->root
= HeapAlloc(GetProcessHeap(), 0, len
);
216 WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1, drive
->root
, len
, NULL
, NULL
);
220 /* relative paths are relative to config dir */
221 const char *config
= wine_get_config_dir();
222 len
= strlen(config
);
223 len
+= WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1, NULL
, 0, NULL
, NULL
) + 2;
224 drive
->root
= HeapAlloc( GetProcessHeap(), 0, len
);
225 len
-= sprintf( drive
->root
, "%s/", config
);
226 WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1,
227 drive
->root
+ strlen(drive
->root
), len
, NULL
, NULL
);
230 if (stat( drive
->root
, &drive_stat_buffer
))
232 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
233 drive
->root
, strerror(errno
), 'A' + i
);
234 HeapFree( GetProcessHeap(), 0, drive
->root
);
238 if (!S_ISDIR(drive_stat_buffer
.st_mode
))
240 MESSAGE("%s is not a directory, ignoring drive %c:\n",
241 drive
->root
, 'A' + i
);
242 HeapFree( GetProcessHeap(), 0, drive
->root
);
247 drive
->dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(drive
->dos_cwd
[0]));
248 drive
->unix_cwd
= heap_strdup( "" );
249 drive
->device
= NULL
;
251 drive
->dev
= drive_stat_buffer
.st_dev
;
252 drive
->ino
= drive_stat_buffer
.st_ino
;
254 /* Get the drive type */
255 RtlInitUnicodeString( &nameW
, TypeW
);
256 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
258 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
259 drive
->type
= DRIVE_GetDriveType( i
, data
);
261 else drive
->type
= DRIVE_FIXED
;
263 /* Get the filesystem type */
264 RtlInitUnicodeString( &nameW
, FilesystemW
);
265 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
267 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
268 drive
->flags
= DRIVE_GetFSFlags( i
, data
);
270 else drive
->flags
= DRIVE_CASE_PRESERVING
;
273 RtlInitUnicodeString( &nameW
, DeviceW
);
274 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
276 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
277 len
= WideCharToMultiByte(CP_UNIXCP
, 0, data
, -1, NULL
, 0, NULL
, NULL
);
278 drive
->device
= HeapAlloc(GetProcessHeap(), 0, len
);
279 WideCharToMultiByte(CP_UNIXCP
, 0, data
, -1, drive
->device
, len
, NULL
, NULL
);
281 if (drive
->type
== DRIVE_CDROM
)
284 if ((cd_fd
= open(drive
->device
, O_RDONLY
|O_NONBLOCK
)) != -1)
286 CDROM_InitRegistry(cd_fd
);
292 /* Get the FailReadOnly flag */
293 RtlInitUnicodeString( &nameW
, FailReadOnlyW
);
294 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
296 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
297 if (IS_OPTION_TRUE(data
[0])) drive
->flags
|= DRIVE_FAIL_READ_ONLY
;
300 /* Make the first hard disk the current drive */
301 if ((DRIVE_CurDrive
== -1) && (drive
->type
== DRIVE_FIXED
))
305 TRACE("Drive %c: path=%s type=%s flags=%08x dev=%x ino=%x\n",
306 'A' + i
, drive
->root
, debugstr_w(DRIVE_Types
[drive
->type
]),
307 drive
->flags
, (int)drive
->dev
, (int)drive
->ino
);
316 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
317 /* Create a C drive pointing to Unix root dir */
318 DOSDrives
[2].root
= heap_strdup( "/" );
319 DOSDrives
[2].dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(DOSDrives
[2].dos_cwd
[0]));
320 DOSDrives
[2].unix_cwd
= heap_strdup( "" );
321 DOSDrives
[2].type
= DRIVE_FIXED
;
322 DOSDrives
[2].device
= NULL
;
323 DOSDrives
[2].flags
= 0;
327 /* Make sure the current drive is valid */
328 if (DRIVE_CurDrive
== -1)
330 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
332 if (drive
->root
&& !(drive
->flags
& DRIVE_DISABLED
))
340 /* get current working directory info for all drives */
341 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++, drive_env
[1]++)
343 if (!GetEnvironmentVariableW(drive_env
, path
, MAX_PATHNAME_LEN
)) continue;
345 if (toupperW(path
[0]) != drive_env
[1] || path
[1] != ':') continue;
346 DRIVE_Chdir( i
, path
+ 2 );
352 /***********************************************************************
355 int DRIVE_IsValid( int drive
)
357 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
358 return (DOSDrives
[drive
].root
&&
359 !(DOSDrives
[drive
].flags
& DRIVE_DISABLED
));
363 /***********************************************************************
364 * DRIVE_GetCurrentDrive
366 int DRIVE_GetCurrentDrive(void)
368 TDB
*pTask
= GlobalLock16(GetCurrentTask());
369 if (pTask
&& (pTask
->curdrive
& 0x80)) return pTask
->curdrive
& ~0x80;
370 return DRIVE_CurDrive
;
374 /***********************************************************************
375 * DRIVE_SetCurrentDrive
377 int DRIVE_SetCurrentDrive( int drive
)
379 TDB
*pTask
= GlobalLock16(GetCurrentTask());
380 if (!DRIVE_IsValid( drive
))
382 SetLastError( ERROR_INVALID_DRIVE
);
385 TRACE("%c:\n", 'A' + drive
);
386 DRIVE_CurDrive
= drive
;
387 if (pTask
) pTask
->curdrive
= drive
| 0x80;
392 /***********************************************************************
393 * DRIVE_FindDriveRoot
395 * Find a drive for which the root matches the beginning of the given path.
396 * This can be used to translate a Unix path into a drive + DOS path.
397 * Return value is the drive, or -1 on error. On success, path is modified
398 * to point to the beginning of the DOS path.
400 * Note: path must be in the encoding of the underlying Unix file system.
402 int DRIVE_FindDriveRoot( const char **path
)
404 /* Starting with the full path, check if the device and inode match any of
405 * the wine 'drives'. If not then remove the last path component and try
406 * again. If the last component was a '..' then skip a normal component
407 * since it's a directory that's ascended back out of.
409 int drive
, level
, len
;
410 char buffer
[MAX_PATHNAME_LEN
];
414 strcpy( buffer
, *path
);
415 for (p
= buffer
; *p
; p
++) if (*p
== '\\') *p
= '/';
418 /* strip off trailing slashes */
419 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
424 if (stat( buffer
, &st
) == 0 && S_ISDIR( st
.st_mode
))
426 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
428 if (!DOSDrives
[drive
].root
||
429 (DOSDrives
[drive
].flags
& DRIVE_DISABLED
))
432 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
433 (DOSDrives
[drive
].ino
== st
.st_ino
))
435 if (len
== 1) len
= 0; /* preserve root slash in returned path */
436 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
437 *path
, 'A' + drive
, buffer
, *path
+ len
);
439 if (!**path
) *path
= "\\";
444 if (len
<= 1) return -1; /* reached root */
449 /* find start of the last path component */
450 while (len
> 1 && buffer
[len
- 1] != '/') len
--;
451 if (!buffer
[len
]) break; /* empty component -> reached root */
452 /* does removing it take us up a level? */
453 if (strcmp( buffer
+ len
, "." ) != 0)
454 level
+= strcmp( buffer
+ len
, ".." ) ? 1 : -1;
456 /* strip off trailing slashes */
457 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
463 /***********************************************************************
464 * DRIVE_FindDriveRootW
466 * Unicode version of DRIVE_FindDriveRoot.
468 int DRIVE_FindDriveRootW( LPCWSTR
*path
)
470 int drive
, level
, len
;
471 WCHAR buffer
[MAX_PATHNAME_LEN
];
475 strcpyW( buffer
, *path
);
476 for (p
= buffer
; *p
; p
++) if (*p
== '\\') *p
= '/';
479 /* strip off trailing slashes */
480 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
484 char buffA
[MAX_PATHNAME_LEN
];
486 WideCharToMultiByte( CP_UNIXCP
, 0, buffer
, -1, buffA
, sizeof(buffA
), NULL
, NULL
);
487 if (stat( buffA
, &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 static const WCHAR rootW
[] = {'\\',0};
501 if (len
== 1) len
= 0; /* preserve root slash in returned path */
502 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
503 debugstr_w(*path
), 'A' + drive
, debugstr_w(buffer
), debugstr_w(*path
+ len
));
505 if (!**path
) *path
= rootW
;
510 if (len
<= 1) return -1; /* reached root */
515 static const WCHAR dotW
[] = {'.',0};
516 static const WCHAR dotdotW
[] = {'.','.',0};
518 /* find start of the last path component */
519 while (len
> 1 && buffer
[len
- 1] != '/') len
--;
520 if (!buffer
[len
]) break; /* empty component -> reached root */
521 /* does removing it take us up a level? */
522 if (strcmpW( buffer
+ len
, dotW
) != 0)
523 level
+= strcmpW( buffer
+ len
, dotdotW
) ? 1 : -1;
525 /* strip off trailing slashes */
526 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
532 /***********************************************************************
535 const char * DRIVE_GetRoot( int drive
)
537 if (!DRIVE_IsValid( drive
)) return NULL
;
538 return DOSDrives
[drive
].root
;
542 /***********************************************************************
545 LPCWSTR
DRIVE_GetDosCwd( int drive
)
547 TDB
*pTask
= GlobalLock16(GetCurrentTask());
548 if (!DRIVE_IsValid( drive
)) return NULL
;
550 /* Check if we need to change the directory to the new task. */
551 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
552 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
553 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
555 static const WCHAR rootW
[] = {'\\',0};
556 WCHAR curdirW
[MAX_PATH
];
557 MultiByteToWideChar(CP_ACP
, 0, pTask
->curdir
, -1, curdirW
, MAX_PATH
);
558 /* Perform the task-switch */
559 if (!DRIVE_Chdir( drive
, curdirW
)) DRIVE_Chdir( drive
, rootW
);
560 DRIVE_LastTask
= GetCurrentTask();
562 return DOSDrives
[drive
].dos_cwd
;
566 /***********************************************************************
569 const char * DRIVE_GetUnixCwd( int drive
)
571 TDB
*pTask
= GlobalLock16(GetCurrentTask());
572 if (!DRIVE_IsValid( drive
)) return NULL
;
574 /* Check if we need to change the directory to the new task. */
575 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
576 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
577 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
579 static const WCHAR rootW
[] = {'\\',0};
580 WCHAR curdirW
[MAX_PATH
];
581 MultiByteToWideChar(CP_ACP
, 0, pTask
->curdir
, -1, curdirW
, MAX_PATH
);
582 /* Perform the task-switch */
583 if (!DRIVE_Chdir( drive
, curdirW
)) DRIVE_Chdir( drive
, rootW
);
584 DRIVE_LastTask
= GetCurrentTask();
586 return DOSDrives
[drive
].unix_cwd
;
590 /***********************************************************************
593 const char * DRIVE_GetDevice( int drive
)
595 return (DRIVE_IsValid( drive
)) ? DOSDrives
[drive
].device
: NULL
;
598 /***********************************************************************
601 static UINT
DRIVE_GetType( int drive
)
603 if (!DRIVE_IsValid( drive
)) return DRIVE_NO_ROOT_DIR
;
604 return DOSDrives
[drive
].type
;
608 /***********************************************************************
611 UINT
DRIVE_GetFlags( int drive
)
613 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
614 return DOSDrives
[drive
].flags
;
617 /***********************************************************************
620 int DRIVE_Chdir( int drive
, LPCWSTR path
)
622 DOS_FULL_NAME full_name
;
623 WCHAR buffer
[MAX_PATHNAME_LEN
];
625 BY_HANDLE_FILE_INFORMATION info
;
626 TDB
*pTask
= GlobalLock16(GetCurrentTask());
628 buffer
[0] = 'A' + drive
;
631 TRACE("(%s,%s)\n", debugstr_w(buffer
), debugstr_w(path
) );
632 strncpyW( buffer
+ 2, path
, MAX_PATHNAME_LEN
- 2 );
633 buffer
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
635 if (!DOSFS_GetFullName( buffer
, TRUE
, &full_name
)) return 0;
636 if (!FILE_Stat( full_name
.long_name
, &info
, NULL
)) return 0;
637 if (!(info
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
639 SetLastError( ERROR_FILE_NOT_FOUND
);
642 unix_cwd
= full_name
.long_name
+ strlen( DOSDrives
[drive
].root
);
643 while (*unix_cwd
== '/') unix_cwd
++;
645 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
646 'A' + drive
, unix_cwd
, debugstr_w(full_name
.short_name
+ 3) );
648 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].dos_cwd
);
649 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].unix_cwd
);
650 DOSDrives
[drive
].dos_cwd
= HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name
.short_name
) - 2) * sizeof(WCHAR
));
651 strcpyW(DOSDrives
[drive
].dos_cwd
, full_name
.short_name
+ 3);
652 DOSDrives
[drive
].unix_cwd
= heap_strdup( unix_cwd
);
654 if (drive
== DRIVE_CurDrive
)
658 RtlInitUnicodeString( &dirW
, full_name
.short_name
);
659 RtlSetCurrentDirectory_U( &dirW
);
662 if (pTask
&& (pTask
->curdrive
& 0x80) &&
663 ((pTask
->curdrive
& ~0x80) == drive
))
665 WideCharToMultiByte(CP_ACP
, 0, full_name
.short_name
+ 2, -1,
666 pTask
->curdir
, sizeof(pTask
->curdir
), NULL
, NULL
);
667 DRIVE_LastTask
= GetCurrentTask();
673 /***********************************************************************
674 * DefineDosDeviceA (KERNEL32.@)
676 BOOL WINAPI
DefineDosDeviceA(DWORD flags
,LPCSTR devname
,LPCSTR targetpath
)
681 if (!RtlCreateUnicodeStringFromAsciiz(&d
, devname
))
683 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
686 if (!RtlCreateUnicodeStringFromAsciiz(&t
, targetpath
))
688 RtlFreeUnicodeString(&d
);
689 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
692 ret
= DefineDosDeviceW(flags
, d
.Buffer
, t
.Buffer
);
693 RtlFreeUnicodeString(&d
);
694 RtlFreeUnicodeString(&t
);
699 /***********************************************************************
700 * DefineDosDeviceA (KERNEL32.@)
702 BOOL WINAPI
DefineDosDeviceW(DWORD flags
,LPCWSTR devname
,LPCWSTR targetpath
)
706 /* this is a temporary hack for int21 support. better implementation has to be done */
707 if (flags
!= DDD_RAW_TARGET_PATH
||
708 !(toupperW(devname
[0]) >= 'A' && toupperW(devname
[0]) <= 'Z') ||
709 devname
[1] != ':' || devname
[2] != 0 ||
710 !(toupperW(targetpath
[0]) >= 'A' && toupperW(targetpath
[0]) <= 'Z') ||
711 targetpath
[1] != ':' || targetpath
[2] != '\\' || targetpath
[3] != 0)
713 FIXME("(0x%08lx,%s,%s),stub!\n", flags
, debugstr_w(devname
), debugstr_w(targetpath
));
714 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
718 old
= DOSDrives
+ devname
[0] - 'A';
719 new = DOSDrives
+ targetpath
[0] - 'A';
723 SetLastError( ERROR_INVALID_DRIVE
);
729 TRACE("Can't map drive %c: to already existing drive %c:\n",
730 devname
[0], targetpath
[0] );
731 /* it is already mapped there, so return success */
732 if (!strcmp(old
->root
,new->root
))
737 new->root
= heap_strdup( old
->root
);
738 new->dos_cwd
= HeapAlloc(GetProcessHeap(), 0, (strlenW(old
->dos_cwd
) + 1) * sizeof(WCHAR
));
739 strcpyW(new->dos_cwd
, old
->dos_cwd
);
740 new->unix_cwd
= heap_strdup( old
->unix_cwd
);
741 new->device
= heap_strdup( old
->device
);
742 new->type
= old
->type
;
743 new->flags
= old
->flags
;
747 TRACE("Drive %c: is now equal to drive %c:\n",
748 targetpath
[0], devname
[0] );
754 /***********************************************************************
757 static int DRIVE_GetFreeSpace( int drive
, PULARGE_INTEGER size
,
758 PULARGE_INTEGER available
)
762 if (!DRIVE_IsValid(drive
))
764 SetLastError( ERROR_PATH_NOT_FOUND
);
768 if (statvfs( DOSDrives
[drive
].root
, &info
) < 0)
771 WARN("cannot do statvfs(%s)\n", DOSDrives
[drive
].root
);
774 size
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_frsize
, info
.f_blocks
);
775 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
776 available
->QuadPart
= 0; /* ALWAYS 0, even if no real CD-ROM mounted there !! */
778 available
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_frsize
, info
.f_bavail
);
783 /***********************************************************************
784 * DRIVE_GetCurrentDirectory
785 * Returns "X:\\path\\etc\\".
787 * Despite the API description, return required length including the
788 * terminating null when buffer too small. This is the real behaviour.
790 static UINT
DRIVE_GetCurrentDirectory( UINT buflen
, LPWSTR buf
)
793 LPCWSTR dos_cwd
= DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
794 static const WCHAR driveA_rootW
[] = {'A',':','\\',0};
796 ret
= strlenW(dos_cwd
) + 3; /* length of WHOLE current directory */
797 if (ret
>= buflen
) return ret
+ 1;
799 strcpyW( buf
, driveA_rootW
);
800 buf
[0] += DRIVE_GetCurrentDrive();
801 strcatW( buf
, dos_cwd
);
806 /***********************************************************************
809 * Build the environment array containing the drives' current directories.
810 * Resulting pointer must be freed with HeapFree.
812 WCHAR
*DRIVE_BuildEnv(void)
815 LPCWSTR cwd
[MAX_DOS_DRIVES
];
818 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++)
820 if ((cwd
[i
] = DRIVE_GetDosCwd(i
)) && cwd
[i
][0])
821 length
+= strlenW(cwd
[i
]) + 8;
823 if (!(env
= HeapAlloc( GetProcessHeap(), 0, (length
+1) * sizeof(WCHAR
) ))) return NULL
;
824 for (i
= 0, p
= env
; i
< MAX_DOS_DRIVES
; i
++)
826 if (cwd
[i
] && cwd
[i
][0])
828 *p
++ = '='; *p
++ = 'A' + i
; *p
++ = ':';
829 *p
++ = '='; *p
++ = 'A' + i
; *p
++ = ':'; *p
++ = '\\';
830 strcpyW( p
, cwd
[i
] );
839 /***********************************************************************
840 * GetDiskFreeSpace (KERNEL.422)
842 BOOL16 WINAPI
GetDiskFreeSpace16( LPCSTR root
, LPDWORD cluster_sectors
,
843 LPDWORD sector_bytes
, LPDWORD free_clusters
,
844 LPDWORD total_clusters
)
846 return GetDiskFreeSpaceA( root
, cluster_sectors
, sector_bytes
,
847 free_clusters
, total_clusters
);
851 /***********************************************************************
852 * GetDiskFreeSpaceW (KERNEL32.@)
854 * Fails if expression resulting from current drive's dir and "root"
855 * is not a root dir of the target drive.
857 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
858 * if the corresponding info is unneeded.
860 * FIXME: needs to support UNC names from Win95 OSR2 on.
862 * Behaviour under Win95a:
863 * CurrDir root result
864 * "E:\\TEST" "E:" FALSE
868 * "E:\\TEST" "\\" TRUE
869 * "E:\\TEST" ":\\" FALSE
870 * "E:\\TEST" "E:\\" TRUE
871 * "E:\\TEST" "" FALSE
872 * "E:\\" "" FALSE (!)
874 * "E:\\TEST" 0x0 TRUE (!)
875 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
876 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
878 BOOL WINAPI
GetDiskFreeSpaceW( LPCWSTR root
, LPDWORD cluster_sectors
,
879 LPDWORD sector_bytes
, LPDWORD free_clusters
,
880 LPDWORD total_clusters
)
883 ULARGE_INTEGER size
,available
;
887 TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root
), cluster_sectors
, sector_bytes
,
888 free_clusters
, total_clusters
);
890 if (!root
|| root
[0] == '\\' || root
[0] == '/')
891 drive
= DRIVE_GetCurrentDrive();
893 if (root
[0] && root
[1] == ':') /* root contains drive tag */
895 drive
= toupperW(root
[0]) - 'A';
899 path
= DRIVE_GetDosCwd(drive
);
902 SetLastError(ERROR_PATH_NOT_FOUND
);
910 if (path
[0]) /* oops, we are in a subdir */
912 SetLastError(ERROR_INVALID_NAME
);
919 SetLastError(ERROR_PATH_NOT_FOUND
);
921 SetLastError(ERROR_INVALID_NAME
);
925 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
927 /* Cap the size and available at 2GB as per specs. */
928 if ((size
.u
.HighPart
) ||(size
.u
.LowPart
> 0x7fffffff))
931 size
.u
.LowPart
= 0x7fffffff;
933 if ((available
.u
.HighPart
) ||(available
.u
.LowPart
> 0x7fffffff))
935 available
.u
.HighPart
=0;
936 available
.u
.LowPart
= 0x7fffffff;
938 sec_size
= (DRIVE_GetType(drive
)==DRIVE_CDROM
) ? 2048 : 512;
939 size
.u
.LowPart
/= sec_size
;
940 available
.u
.LowPart
/= sec_size
;
941 /* FIXME: probably have to adjust those variables too for CDFS */
943 while (cluster_sec
* 65536 < size
.u
.LowPart
) cluster_sec
*= 2;
946 *cluster_sectors
= cluster_sec
;
948 *sector_bytes
= sec_size
;
950 *free_clusters
= available
.u
.LowPart
/ cluster_sec
;
952 *total_clusters
= size
.u
.LowPart
/ cluster_sec
;
957 /***********************************************************************
958 * GetDiskFreeSpaceA (KERNEL32.@)
960 BOOL WINAPI
GetDiskFreeSpaceA( LPCSTR root
, LPDWORD cluster_sectors
,
961 LPDWORD sector_bytes
, LPDWORD free_clusters
,
962 LPDWORD total_clusters
)
964 UNICODE_STRING rootW
;
969 if(!RtlCreateUnicodeStringFromAsciiz(&rootW
, root
))
971 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
978 ret
= GetDiskFreeSpaceW(rootW
.Buffer
, cluster_sectors
, sector_bytes
,
979 free_clusters
, total_clusters
);
980 RtlFreeUnicodeString(&rootW
);
986 /***********************************************************************
987 * GetDiskFreeSpaceExW (KERNEL32.@)
989 * This function is used to acquire the size of the available and
990 * total space on a logical volume.
994 * Zero on failure, nonzero upon success. Use GetLastError to obtain
995 * detailed error information.
998 BOOL WINAPI
GetDiskFreeSpaceExW( LPCWSTR root
,
999 PULARGE_INTEGER avail
,
1000 PULARGE_INTEGER total
,
1001 PULARGE_INTEGER totalfree
)
1004 ULARGE_INTEGER size
,available
;
1006 if (!root
) drive
= DRIVE_GetCurrentDrive();
1008 { /* C: always works for GetDiskFreeSpaceEx */
1009 if ((root
[1]) && ((root
[1] != ':') || (root
[2] && root
[2] != '\\')))
1011 FIXME("there are valid root names which are not supported yet\n");
1012 /* ..like UNC names, for instance. */
1014 WARN("invalid root '%s'\n", debugstr_w(root
));
1017 drive
= toupperW(root
[0]) - 'A';
1020 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1024 total
->u
.HighPart
= size
.u
.HighPart
;
1025 total
->u
.LowPart
= size
.u
.LowPart
;
1030 totalfree
->u
.HighPart
= available
.u
.HighPart
;
1031 totalfree
->u
.LowPart
= available
.u
.LowPart
;
1036 if (FIXME_ON(dosfs
))
1038 /* On Windows2000, we need to check the disk quota
1039 allocated for the user owning the calling process. We
1040 don't want to be more obtrusive than necessary with the
1041 FIXME messages, so don't print the FIXME unless Wine is
1042 actually masquerading as Windows2000. */
1044 RTL_OSVERSIONINFOEXW ovi
;
1045 ovi
.dwOSVersionInfoSize
= sizeof(ovi
);
1046 if (RtlGetVersion(&ovi
))
1048 if (ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
&& ovi
.dwMajorVersion
> 4)
1049 FIXME("no per-user quota support yet\n");
1053 /* Quick hack, should eventually be fixed to work 100% with
1054 Windows2000 (see comment above). */
1055 avail
->u
.HighPart
= available
.u
.HighPart
;
1056 avail
->u
.LowPart
= available
.u
.LowPart
;
1062 /***********************************************************************
1063 * GetDiskFreeSpaceExA (KERNEL32.@)
1065 BOOL WINAPI
GetDiskFreeSpaceExA( LPCSTR root
, PULARGE_INTEGER avail
,
1066 PULARGE_INTEGER total
,
1067 PULARGE_INTEGER totalfree
)
1069 UNICODE_STRING rootW
;
1072 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
1073 else rootW
.Buffer
= NULL
;
1075 ret
= GetDiskFreeSpaceExW( rootW
.Buffer
, avail
, total
, totalfree
);
1077 RtlFreeUnicodeString(&rootW
);
1081 /***********************************************************************
1082 * GetDriveType (KERNEL.136)
1083 * This function returns the type of a drive in Win16.
1084 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1085 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1086 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1087 * do any pseudo-clever changes.
1090 * drivetype DRIVE_xxx
1092 UINT16 WINAPI
GetDriveType16( UINT16 drive
) /* [in] number (NOT letter) of drive */
1094 UINT type
= DRIVE_GetType(drive
);
1095 TRACE("(%c:)\n", 'A' + drive
);
1096 if (type
== DRIVE_CDROM
) type
= DRIVE_REMOTE
;
1101 /***********************************************************************
1102 * GetDriveTypeW (KERNEL32.@)
1104 * Returns the type of the disk drive specified. If root is NULL the
1105 * root of the current directory is used.
1109 * Type of drive (from Win32 SDK):
1111 * DRIVE_UNKNOWN unable to find out anything about the drive
1112 * DRIVE_NO_ROOT_DIR nonexistent root dir
1113 * DRIVE_REMOVABLE the disk can be removed from the machine
1114 * DRIVE_FIXED the disk can not be removed from the machine
1115 * DRIVE_REMOTE network disk
1116 * DRIVE_CDROM CDROM drive
1117 * DRIVE_RAMDISK virtual disk in RAM
1119 UINT WINAPI
GetDriveTypeW(LPCWSTR root
) /* [in] String describing drive */
1122 TRACE("(%s)\n", debugstr_w(root
));
1124 if (NULL
== root
) drive
= DRIVE_GetCurrentDrive();
1127 if ((root
[1]) && (root
[1] != ':'))
1129 WARN("invalid root %s\n", debugstr_w(root
));
1130 return DRIVE_NO_ROOT_DIR
;
1132 drive
= toupperW(root
[0]) - 'A';
1134 return DRIVE_GetType(drive
);
1138 /***********************************************************************
1139 * GetDriveTypeA (KERNEL32.@)
1141 UINT WINAPI
GetDriveTypeA( LPCSTR root
)
1143 UNICODE_STRING rootW
;
1148 if( !RtlCreateUnicodeStringFromAsciiz(&rootW
, root
))
1150 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1155 rootW
.Buffer
= NULL
;
1157 ret
= GetDriveTypeW(rootW
.Buffer
);
1159 RtlFreeUnicodeString(&rootW
);
1165 /***********************************************************************
1166 * GetCurrentDirectory (KERNEL.411)
1168 UINT16 WINAPI
GetCurrentDirectory16( UINT16 buflen
, LPSTR buf
)
1170 WCHAR cur_dirW
[MAX_PATH
];
1172 DRIVE_GetCurrentDirectory(MAX_PATH
, cur_dirW
);
1173 return (UINT16
)WideCharToMultiByte(CP_ACP
, 0, cur_dirW
, -1, buf
, buflen
, NULL
, NULL
);
1177 /***********************************************************************
1178 * GetCurrentDirectoryW (KERNEL32.@)
1180 UINT WINAPI
GetCurrentDirectoryW( UINT buflen
, LPWSTR buf
)
1183 WCHAR longname
[MAX_PATHNAME_LEN
];
1184 WCHAR shortname
[MAX_PATHNAME_LEN
];
1186 ret
= DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN
, shortname
);
1187 if ( ret
> MAX_PATHNAME_LEN
) {
1188 ERR_(file
)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret
);
1191 GetLongPathNameW(shortname
, longname
, MAX_PATHNAME_LEN
);
1192 ret
= strlenW( longname
) + 1;
1193 if (ret
> buflen
) return ret
;
1194 strcpyW(buf
, longname
);
1198 /***********************************************************************
1199 * GetCurrentDirectoryA (KERNEL32.@)
1201 UINT WINAPI
GetCurrentDirectoryA( UINT buflen
, LPSTR buf
)
1203 WCHAR bufferW
[MAX_PATH
];
1206 retW
= GetCurrentDirectoryW(MAX_PATH
, bufferW
);
1210 else if (retW
> MAX_PATH
)
1212 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1217 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, NULL
, 0, NULL
, NULL
);
1220 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buf
, buflen
, NULL
, NULL
);
1221 ret
--; /* length without 0 */
1228 /***********************************************************************
1229 * SetCurrentDirectory (KERNEL.412)
1231 BOOL16 WINAPI
SetCurrentDirectory16( LPCSTR dir
)
1233 return SetCurrentDirectoryA( dir
);
1237 /***********************************************************************
1238 * SetCurrentDirectoryW (KERNEL32.@)
1240 BOOL WINAPI
SetCurrentDirectoryW( LPCWSTR dir
)
1242 int drive
, olddrive
= DRIVE_GetCurrentDrive();
1246 SetLastError(ERROR_INVALID_PARAMETER
);
1249 if (dir
[0] && (dir
[1]==':'))
1251 drive
= toupperW( *dir
) - 'A';
1257 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1258 sets pTask->curdir only if pTask->curdrive is drive */
1259 if (!(DRIVE_SetCurrentDrive( drive
)))
1262 /* FIXME: what about empty strings? Add a \\ ? */
1263 if (!DRIVE_Chdir( drive
, dir
)) {
1264 DRIVE_SetCurrentDrive(olddrive
);
1271 /***********************************************************************
1272 * SetCurrentDirectoryA (KERNEL32.@)
1274 BOOL WINAPI
SetCurrentDirectoryA( LPCSTR dir
)
1276 UNICODE_STRING dirW
;
1281 SetLastError(ERROR_INVALID_PARAMETER
);
1285 if (RtlCreateUnicodeStringFromAsciiz(&dirW
, dir
))
1287 ret
= SetCurrentDirectoryW(dirW
.Buffer
);
1288 RtlFreeUnicodeString(&dirW
);
1291 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1296 /***********************************************************************
1297 * GetLogicalDriveStringsA (KERNEL32.@)
1299 UINT WINAPI
GetLogicalDriveStringsA( UINT len
, LPSTR buffer
)
1303 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1304 if (DRIVE_IsValid(drive
)) count
++;
1305 if ((count
* 4) + 1 <= len
)
1308 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1309 if (DRIVE_IsValid(drive
))
1320 return (count
* 4) + 1; /* account for terminating null */
1321 /* The API tells about these different return values */
1325 /***********************************************************************
1326 * GetLogicalDriveStringsW (KERNEL32.@)
1328 UINT WINAPI
GetLogicalDriveStringsW( UINT len
, LPWSTR buffer
)
1332 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1333 if (DRIVE_IsValid(drive
)) count
++;
1334 if (count
* 4 * sizeof(WCHAR
) <= len
)
1337 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1338 if (DRIVE_IsValid(drive
))
1340 *p
++ = (WCHAR
)('a' + drive
);
1347 return count
* 4 * sizeof(WCHAR
);
1351 /***********************************************************************
1352 * GetLogicalDrives (KERNEL32.@)
1354 DWORD WINAPI
GetLogicalDrives(void)
1359 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1361 if ( (DRIVE_IsValid(drive
)) ||
1362 (DOSDrives
[drive
].type
== DRIVE_CDROM
)) /* audio CD is also valid */
1363 ret
|= (1 << drive
);