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 */
59 #include "wine/unicode.h"
60 #include "wine/library.h"
61 #include "wine/server.h"
62 #include "wine/debug.h"
64 WINE_DEFAULT_DEBUG_CHANNEL(dosfs
);
65 WINE_DECLARE_DEBUG_CHANNEL(file
);
69 char *root
; /* root dir in Unix format without trailing / */
70 LPWSTR dos_cwd
; /* cwd in DOS format without leading or trailing \ */
71 char *unix_cwd
; /* cwd in Unix format without leading or trailing / */
72 char *device
; /* raw device path */
73 UINT type
; /* drive type */
74 UINT flags
; /* drive flags */
75 dev_t dev
; /* unix device number */
76 ino_t ino
; /* unix inode number */
80 static const WCHAR DRIVE_Types
[][8] =
82 { 0 }, /* DRIVE_UNKNOWN */
83 { 0 }, /* DRIVE_NO_ROOT_DIR */
84 {'f','l','o','p','p','y',0}, /* DRIVE_REMOVABLE */
85 {'h','d',0}, /* DRIVE_FIXED */
86 {'n','e','t','w','o','r','k',0}, /* DRIVE_REMOTE */
87 {'c','d','r','o','m',0}, /* DRIVE_CDROM */
88 {'r','a','m','d','i','s','k',0} /* DRIVE_RAMDISK */
91 #define MAX_DOS_DRIVES 26
93 static DOSDRIVE DOSDrives
[MAX_DOS_DRIVES
];
94 static int DRIVE_CurDrive
= -1;
96 static HTASK16 DRIVE_LastTask
= 0;
98 /* strdup on the process heap */
99 inline static char *heap_strdup( const char *str
)
101 INT len
= strlen(str
) + 1;
102 LPSTR p
= HeapAlloc( GetProcessHeap(), 0, len
);
103 if (p
) memcpy( p
, str
, len
);
107 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
109 extern void CDROM_InitRegistry(int dev
);
111 /***********************************************************************
114 static inline UINT
DRIVE_GetDriveType( INT drive
, LPCWSTR value
)
118 for (i
= 0; i
< sizeof(DRIVE_Types
)/sizeof(DRIVE_Types
[0]); i
++)
120 if (!strcmpiW( value
, DRIVE_Types
[i
] )) return i
;
122 MESSAGE("Drive %c: unknown drive type %s, defaulting to 'hd'.\n",
123 'A' + drive
, debugstr_w(value
) );
128 /***********************************************************************
133 int i
, len
, count
= 0;
134 WCHAR driveW
[] = {'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
135 'W','i','n','e','\\','W','i','n','e','\\',
136 'C','o','n','f','i','g','\\','D','r','i','v','e',' ','A',0};
137 WCHAR drive_env
[] = {'=','A',':',0};
138 WCHAR path
[MAX_PATHNAME_LEN
];
139 char tmp
[MAX_PATHNAME_LEN
*sizeof(WCHAR
) + sizeof(KEY_VALUE_PARTIAL_INFORMATION
)];
140 struct stat drive_stat_buffer
;
145 OBJECT_ATTRIBUTES attr
;
146 UNICODE_STRING nameW
;
148 static const WCHAR PathW
[] = {'P','a','t','h',0};
149 static const WCHAR TypeW
[] = {'T','y','p','e',0};
150 static const WCHAR DeviceW
[] = {'D','e','v','i','c','e',0};
151 static const WCHAR FailReadOnlyW
[] = {'F','a','i','l','R','e','a','d','O','n','l','y',0};
153 attr
.Length
= sizeof(attr
);
154 attr
.RootDirectory
= 0;
155 attr
.ObjectName
= &nameW
;
157 attr
.SecurityDescriptor
= NULL
;
158 attr
.SecurityQualityOfService
= NULL
;
160 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
162 RtlInitUnicodeString( &nameW
, driveW
);
163 nameW
.Buffer
[(nameW
.Length
/ sizeof(WCHAR
)) - 1] = 'A' + i
;
164 if (NtOpenKey( &hkey
, KEY_ALL_ACCESS
, &attr
) != STATUS_SUCCESS
) continue;
166 /* Get the root path */
167 RtlInitUnicodeString( &nameW
, PathW
);
168 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
170 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
171 ExpandEnvironmentStringsW( data
, path
, sizeof(path
)/sizeof(WCHAR
) );
173 p
= path
+ strlenW(path
) - 1;
174 while ((p
> path
) && (*p
== '/')) *p
-- = '\0';
178 len
= WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1, NULL
, 0, NULL
, NULL
);
179 drive
->root
= HeapAlloc(GetProcessHeap(), 0, len
);
180 WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1, drive
->root
, len
, NULL
, NULL
);
184 /* relative paths are relative to config dir */
185 const char *config
= wine_get_config_dir();
186 len
= strlen(config
);
187 len
+= WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1, NULL
, 0, NULL
, NULL
) + 2;
188 drive
->root
= HeapAlloc( GetProcessHeap(), 0, len
);
189 len
-= sprintf( drive
->root
, "%s/", config
);
190 WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1,
191 drive
->root
+ strlen(drive
->root
), len
, NULL
, NULL
);
194 if (stat( drive
->root
, &drive_stat_buffer
))
196 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
197 drive
->root
, strerror(errno
), 'A' + i
);
198 HeapFree( GetProcessHeap(), 0, drive
->root
);
202 if (!S_ISDIR(drive_stat_buffer
.st_mode
))
204 MESSAGE("%s is not a directory, ignoring drive %c:\n",
205 drive
->root
, 'A' + i
);
206 HeapFree( GetProcessHeap(), 0, drive
->root
);
211 drive
->dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(drive
->dos_cwd
[0]));
212 drive
->unix_cwd
= heap_strdup( "" );
213 drive
->device
= NULL
;
215 drive
->dev
= drive_stat_buffer
.st_dev
;
216 drive
->ino
= drive_stat_buffer
.st_ino
;
218 /* Get the drive type */
219 RtlInitUnicodeString( &nameW
, TypeW
);
220 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
222 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
223 drive
->type
= DRIVE_GetDriveType( i
, data
);
225 else drive
->type
= DRIVE_FIXED
;
228 RtlInitUnicodeString( &nameW
, DeviceW
);
229 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
231 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
232 len
= WideCharToMultiByte(CP_UNIXCP
, 0, data
, -1, NULL
, 0, NULL
, NULL
);
233 drive
->device
= HeapAlloc(GetProcessHeap(), 0, len
);
234 WideCharToMultiByte(CP_UNIXCP
, 0, data
, -1, drive
->device
, len
, NULL
, NULL
);
236 if (drive
->type
== DRIVE_CDROM
)
239 if ((cd_fd
= open(drive
->device
, O_RDONLY
|O_NONBLOCK
)) != -1)
241 CDROM_InitRegistry(cd_fd
);
247 /* Get the FailReadOnly flag */
248 RtlInitUnicodeString( &nameW
, FailReadOnlyW
);
249 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
251 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
252 if (IS_OPTION_TRUE(data
[0])) drive
->flags
|= DRIVE_FAIL_READ_ONLY
;
255 /* Make the first hard disk the current drive */
256 if ((DRIVE_CurDrive
== -1) && (drive
->type
== DRIVE_FIXED
))
260 TRACE("Drive %c: path=%s type=%s flags=%08x dev=%x ino=%x\n",
261 'A' + i
, drive
->root
, debugstr_w(DRIVE_Types
[drive
->type
]),
262 drive
->flags
, (int)drive
->dev
, (int)drive
->ino
);
271 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
272 /* Create a C drive pointing to Unix root dir */
273 DOSDrives
[2].root
= heap_strdup( "/" );
274 DOSDrives
[2].dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(DOSDrives
[2].dos_cwd
[0]));
275 DOSDrives
[2].unix_cwd
= heap_strdup( "" );
276 DOSDrives
[2].type
= DRIVE_FIXED
;
277 DOSDrives
[2].device
= NULL
;
278 DOSDrives
[2].flags
= 0;
282 /* Make sure the current drive is valid */
283 if (DRIVE_CurDrive
== -1)
285 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
295 /* get current working directory info for all drives */
296 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++, drive_env
[1]++)
298 if (!GetEnvironmentVariableW(drive_env
, path
, MAX_PATHNAME_LEN
)) continue;
300 if (toupperW(path
[0]) != drive_env
[1] || path
[1] != ':') continue;
301 DRIVE_Chdir( i
, path
+ 2 );
307 /***********************************************************************
310 int DRIVE_IsValid( int drive
)
312 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
313 return (DOSDrives
[drive
].root
!= NULL
);
317 /***********************************************************************
318 * DRIVE_GetCurrentDrive
320 int DRIVE_GetCurrentDrive(void)
322 TDB
*pTask
= GlobalLock16(GetCurrentTask());
323 if (pTask
&& (pTask
->curdrive
& 0x80)) return pTask
->curdrive
& ~0x80;
324 return DRIVE_CurDrive
;
328 /***********************************************************************
329 * DRIVE_SetCurrentDrive
331 int DRIVE_SetCurrentDrive( int drive
)
333 TDB
*pTask
= GlobalLock16(GetCurrentTask());
334 if (!DRIVE_IsValid( drive
))
336 SetLastError( ERROR_INVALID_DRIVE
);
339 TRACE("%c:\n", 'A' + drive
);
340 DRIVE_CurDrive
= drive
;
341 if (pTask
) pTask
->curdrive
= drive
| 0x80;
346 /***********************************************************************
347 * DRIVE_FindDriveRoot
349 * Find a drive for which the root matches the beginning of the given path.
350 * This can be used to translate a Unix path into a drive + DOS path.
351 * Return value is the drive, or -1 on error. On success, path is modified
352 * to point to the beginning of the DOS path.
354 * Note: path must be in the encoding of the underlying Unix file system.
356 int DRIVE_FindDriveRoot( const char **path
)
358 /* Starting with the full path, check if the device and inode match any of
359 * the wine 'drives'. If not then remove the last path component and try
360 * again. If the last component was a '..' then skip a normal component
361 * since it's a directory that's ascended back out of.
363 int drive
, level
, len
;
364 char buffer
[MAX_PATHNAME_LEN
];
368 strcpy( buffer
, *path
);
369 for (p
= buffer
; *p
; p
++) if (*p
== '\\') *p
= '/';
372 /* strip off trailing slashes */
373 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
378 if (stat( buffer
, &st
) == 0 && S_ISDIR( st
.st_mode
))
380 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
382 if (!DOSDrives
[drive
].root
) continue;
384 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
385 (DOSDrives
[drive
].ino
== st
.st_ino
))
387 if (len
== 1) len
= 0; /* preserve root slash in returned path */
388 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
389 *path
, 'A' + drive
, buffer
, *path
+ len
);
391 if (!**path
) *path
= "\\";
396 if (len
<= 1) return -1; /* reached root */
401 /* find start of the last path component */
402 while (len
> 1 && buffer
[len
- 1] != '/') len
--;
403 if (!buffer
[len
]) break; /* empty component -> reached root */
404 /* does removing it take us up a level? */
405 if (strcmp( buffer
+ len
, "." ) != 0)
406 level
+= strcmp( buffer
+ len
, ".." ) ? 1 : -1;
408 /* strip off trailing slashes */
409 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
415 /***********************************************************************
416 * DRIVE_FindDriveRootW
418 * Unicode version of DRIVE_FindDriveRoot.
420 int DRIVE_FindDriveRootW( LPCWSTR
*path
)
422 int drive
, level
, len
;
423 WCHAR buffer
[MAX_PATHNAME_LEN
];
427 strcpyW( buffer
, *path
);
428 for (p
= buffer
; *p
; p
++) if (*p
== '\\') *p
= '/';
431 /* strip off trailing slashes */
432 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
436 char buffA
[MAX_PATHNAME_LEN
];
438 WideCharToMultiByte( CP_UNIXCP
, 0, buffer
, -1, buffA
, sizeof(buffA
), NULL
, NULL
);
439 if (stat( buffA
, &st
) == 0 && S_ISDIR( st
.st_mode
))
442 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
444 if (!DOSDrives
[drive
].root
) continue;
446 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
447 (DOSDrives
[drive
].ino
== st
.st_ino
))
449 static const WCHAR rootW
[] = {'\\',0};
451 if (len
== 1) len
= 0; /* preserve root slash in returned path */
452 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
453 debugstr_w(*path
), 'A' + drive
, debugstr_w(buffer
), debugstr_w(*path
+ len
));
455 if (!**path
) *path
= rootW
;
460 if (len
<= 1) return -1; /* reached root */
465 static const WCHAR dotW
[] = {'.',0};
466 static const WCHAR dotdotW
[] = {'.','.',0};
468 /* find start of the last path component */
469 while (len
> 1 && buffer
[len
- 1] != '/') len
--;
470 if (!buffer
[len
]) break; /* empty component -> reached root */
471 /* does removing it take us up a level? */
472 if (strcmpW( buffer
+ len
, dotW
) != 0)
473 level
+= strcmpW( buffer
+ len
, dotdotW
) ? 1 : -1;
475 /* strip off trailing slashes */
476 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
482 /***********************************************************************
485 const char * DRIVE_GetRoot( int drive
)
487 if (!DRIVE_IsValid( drive
)) return NULL
;
488 return DOSDrives
[drive
].root
;
492 /***********************************************************************
495 LPCWSTR
DRIVE_GetDosCwd( int drive
)
497 TDB
*pTask
= GlobalLock16(GetCurrentTask());
498 if (!DRIVE_IsValid( drive
)) return NULL
;
500 /* Check if we need to change the directory to the new task. */
501 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
502 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
503 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
505 static const WCHAR rootW
[] = {'\\',0};
506 WCHAR curdirW
[MAX_PATH
];
507 MultiByteToWideChar(CP_ACP
, 0, pTask
->curdir
, -1, curdirW
, MAX_PATH
);
508 /* Perform the task-switch */
509 if (!DRIVE_Chdir( drive
, curdirW
)) DRIVE_Chdir( drive
, rootW
);
510 DRIVE_LastTask
= GetCurrentTask();
512 return DOSDrives
[drive
].dos_cwd
;
516 /***********************************************************************
519 const char * DRIVE_GetUnixCwd( int drive
)
521 TDB
*pTask
= GlobalLock16(GetCurrentTask());
522 if (!DRIVE_IsValid( drive
)) return NULL
;
524 /* Check if we need to change the directory to the new task. */
525 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
526 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
527 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
529 static const WCHAR rootW
[] = {'\\',0};
530 WCHAR curdirW
[MAX_PATH
];
531 MultiByteToWideChar(CP_ACP
, 0, pTask
->curdir
, -1, curdirW
, MAX_PATH
);
532 /* Perform the task-switch */
533 if (!DRIVE_Chdir( drive
, curdirW
)) DRIVE_Chdir( drive
, rootW
);
534 DRIVE_LastTask
= GetCurrentTask();
536 return DOSDrives
[drive
].unix_cwd
;
540 /***********************************************************************
543 const char * DRIVE_GetDevice( int drive
)
545 return (DRIVE_IsValid( drive
)) ? DOSDrives
[drive
].device
: NULL
;
548 /***********************************************************************
551 static UINT
DRIVE_GetType( int drive
)
553 if (!DRIVE_IsValid( drive
)) return DRIVE_NO_ROOT_DIR
;
554 return DOSDrives
[drive
].type
;
558 /***********************************************************************
561 UINT
DRIVE_GetFlags( int drive
)
563 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
564 return DOSDrives
[drive
].flags
;
567 /***********************************************************************
570 int DRIVE_Chdir( int drive
, LPCWSTR path
)
572 DOS_FULL_NAME full_name
;
573 WCHAR buffer
[MAX_PATHNAME_LEN
];
575 BY_HANDLE_FILE_INFORMATION info
;
576 TDB
*pTask
= GlobalLock16(GetCurrentTask());
578 buffer
[0] = 'A' + drive
;
581 TRACE("(%s,%s)\n", debugstr_w(buffer
), debugstr_w(path
) );
582 strncpyW( buffer
+ 2, path
, MAX_PATHNAME_LEN
- 2 );
583 buffer
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
585 if (!DOSFS_GetFullName( buffer
, TRUE
, &full_name
)) return 0;
586 if (!FILE_Stat( full_name
.long_name
, &info
, NULL
)) return 0;
587 if (!(info
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
589 SetLastError( ERROR_FILE_NOT_FOUND
);
592 unix_cwd
= full_name
.long_name
+ strlen( DOSDrives
[drive
].root
);
593 while (*unix_cwd
== '/') unix_cwd
++;
595 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
596 'A' + drive
, unix_cwd
, debugstr_w(full_name
.short_name
+ 3) );
598 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].dos_cwd
);
599 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].unix_cwd
);
600 DOSDrives
[drive
].dos_cwd
= HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name
.short_name
) - 2) * sizeof(WCHAR
));
601 strcpyW(DOSDrives
[drive
].dos_cwd
, full_name
.short_name
+ 3);
602 DOSDrives
[drive
].unix_cwd
= heap_strdup( unix_cwd
);
604 if (drive
== DRIVE_CurDrive
)
608 RtlInitUnicodeString( &dirW
, full_name
.short_name
);
609 RtlSetCurrentDirectory_U( &dirW
);
612 if (pTask
&& (pTask
->curdrive
& 0x80) &&
613 ((pTask
->curdrive
& ~0x80) == drive
))
615 WideCharToMultiByte(CP_ACP
, 0, full_name
.short_name
+ 2, -1,
616 pTask
->curdir
, sizeof(pTask
->curdir
), NULL
, NULL
);
617 DRIVE_LastTask
= GetCurrentTask();
623 /***********************************************************************
626 static int DRIVE_GetFreeSpace( int drive
, PULARGE_INTEGER size
,
627 PULARGE_INTEGER available
)
631 if (!DRIVE_IsValid(drive
))
633 SetLastError( ERROR_PATH_NOT_FOUND
);
637 if (statvfs( DOSDrives
[drive
].root
, &info
) < 0)
640 WARN("cannot do statvfs(%s)\n", DOSDrives
[drive
].root
);
643 size
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_frsize
, info
.f_blocks
);
644 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
645 available
->QuadPart
= 0; /* ALWAYS 0, even if no real CD-ROM mounted there !! */
647 available
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_frsize
, info
.f_bavail
);
652 /***********************************************************************
653 * DRIVE_GetCurrentDirectory
654 * Returns "X:\\path\\etc\\".
656 * Despite the API description, return required length including the
657 * terminating null when buffer too small. This is the real behaviour.
659 static UINT
DRIVE_GetCurrentDirectory( UINT buflen
, LPWSTR buf
)
662 LPCWSTR dos_cwd
= DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
663 static const WCHAR driveA_rootW
[] = {'A',':','\\',0};
665 ret
= strlenW(dos_cwd
) + 3; /* length of WHOLE current directory */
666 if (ret
>= buflen
) return ret
+ 1;
668 strcpyW( buf
, driveA_rootW
);
669 buf
[0] += DRIVE_GetCurrentDrive();
670 strcatW( buf
, dos_cwd
);
675 /***********************************************************************
678 * Build the environment array containing the drives' current directories.
679 * Resulting pointer must be freed with HeapFree.
681 WCHAR
*DRIVE_BuildEnv(void)
684 LPCWSTR cwd
[MAX_DOS_DRIVES
];
687 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++)
689 if ((cwd
[i
] = DRIVE_GetDosCwd(i
)) && cwd
[i
][0])
690 length
+= strlenW(cwd
[i
]) + 8;
692 if (!(env
= HeapAlloc( GetProcessHeap(), 0, (length
+1) * sizeof(WCHAR
) ))) return NULL
;
693 for (i
= 0, p
= env
; i
< MAX_DOS_DRIVES
; i
++)
695 if (cwd
[i
] && cwd
[i
][0])
697 *p
++ = '='; *p
++ = 'A' + i
; *p
++ = ':';
698 *p
++ = '='; *p
++ = 'A' + i
; *p
++ = ':'; *p
++ = '\\';
699 strcpyW( p
, cwd
[i
] );
708 /***********************************************************************
709 * GetDiskFreeSpaceW (KERNEL32.@)
711 * Fails if expression resulting from current drive's dir and "root"
712 * is not a root dir of the target drive.
714 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
715 * if the corresponding info is unneeded.
717 * FIXME: needs to support UNC names from Win95 OSR2 on.
719 * Behaviour under Win95a:
720 * CurrDir root result
721 * "E:\\TEST" "E:" FALSE
725 * "E:\\TEST" "\\" TRUE
726 * "E:\\TEST" ":\\" FALSE
727 * "E:\\TEST" "E:\\" TRUE
728 * "E:\\TEST" "" FALSE
729 * "E:\\" "" FALSE (!)
731 * "E:\\TEST" 0x0 TRUE (!)
732 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
733 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
735 BOOL WINAPI
GetDiskFreeSpaceW( LPCWSTR root
, LPDWORD cluster_sectors
,
736 LPDWORD sector_bytes
, LPDWORD free_clusters
,
737 LPDWORD total_clusters
)
740 ULARGE_INTEGER size
,available
;
744 TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root
), cluster_sectors
, sector_bytes
,
745 free_clusters
, total_clusters
);
747 if (!root
|| root
[0] == '\\' || root
[0] == '/')
748 drive
= DRIVE_GetCurrentDrive();
750 if (root
[0] && root
[1] == ':') /* root contains drive tag */
752 drive
= toupperW(root
[0]) - 'A';
756 path
= DRIVE_GetDosCwd(drive
);
759 SetLastError(ERROR_PATH_NOT_FOUND
);
767 if (path
[0]) /* oops, we are in a subdir */
769 SetLastError(ERROR_INVALID_NAME
);
776 SetLastError(ERROR_PATH_NOT_FOUND
);
778 SetLastError(ERROR_INVALID_NAME
);
782 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
784 /* Cap the size and available at 2GB as per specs. */
785 if ((size
.u
.HighPart
) ||(size
.u
.LowPart
> 0x7fffffff))
788 size
.u
.LowPart
= 0x7fffffff;
790 if ((available
.u
.HighPart
) ||(available
.u
.LowPart
> 0x7fffffff))
792 available
.u
.HighPart
=0;
793 available
.u
.LowPart
= 0x7fffffff;
795 sec_size
= (DRIVE_GetType(drive
)==DRIVE_CDROM
) ? 2048 : 512;
796 size
.u
.LowPart
/= sec_size
;
797 available
.u
.LowPart
/= sec_size
;
798 /* FIXME: probably have to adjust those variables too for CDFS */
800 while (cluster_sec
* 65536 < size
.u
.LowPart
) cluster_sec
*= 2;
803 *cluster_sectors
= cluster_sec
;
805 *sector_bytes
= sec_size
;
807 *free_clusters
= available
.u
.LowPart
/ cluster_sec
;
809 *total_clusters
= size
.u
.LowPart
/ cluster_sec
;
814 /***********************************************************************
815 * GetDiskFreeSpaceA (KERNEL32.@)
817 BOOL WINAPI
GetDiskFreeSpaceA( LPCSTR root
, LPDWORD cluster_sectors
,
818 LPDWORD sector_bytes
, LPDWORD free_clusters
,
819 LPDWORD total_clusters
)
821 UNICODE_STRING rootW
;
826 if(!RtlCreateUnicodeStringFromAsciiz(&rootW
, root
))
828 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
835 ret
= GetDiskFreeSpaceW(rootW
.Buffer
, cluster_sectors
, sector_bytes
,
836 free_clusters
, total_clusters
);
837 RtlFreeUnicodeString(&rootW
);
843 /***********************************************************************
844 * GetDiskFreeSpaceExW (KERNEL32.@)
846 * This function is used to acquire the size of the available and
847 * total space on a logical volume.
851 * Zero on failure, nonzero upon success. Use GetLastError to obtain
852 * detailed error information.
855 BOOL WINAPI
GetDiskFreeSpaceExW( LPCWSTR root
,
856 PULARGE_INTEGER avail
,
857 PULARGE_INTEGER total
,
858 PULARGE_INTEGER totalfree
)
861 ULARGE_INTEGER size
,available
;
863 if (!root
) drive
= DRIVE_GetCurrentDrive();
865 { /* C: always works for GetDiskFreeSpaceEx */
866 if ((root
[1]) && ((root
[1] != ':') || (root
[2] && root
[2] != '\\')))
868 FIXME("there are valid root names which are not supported yet\n");
869 /* ..like UNC names, for instance. */
871 WARN("invalid root '%s'\n", debugstr_w(root
));
874 drive
= toupperW(root
[0]) - 'A';
877 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
881 total
->u
.HighPart
= size
.u
.HighPart
;
882 total
->u
.LowPart
= size
.u
.LowPart
;
887 totalfree
->u
.HighPart
= available
.u
.HighPart
;
888 totalfree
->u
.LowPart
= available
.u
.LowPart
;
895 /* On Windows2000, we need to check the disk quota
896 allocated for the user owning the calling process. We
897 don't want to be more obtrusive than necessary with the
898 FIXME messages, so don't print the FIXME unless Wine is
899 actually masquerading as Windows2000. */
901 RTL_OSVERSIONINFOEXW ovi
;
902 ovi
.dwOSVersionInfoSize
= sizeof(ovi
);
903 if (RtlGetVersion(&ovi
))
905 if (ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
&& ovi
.dwMajorVersion
> 4)
906 FIXME("no per-user quota support yet\n");
910 /* Quick hack, should eventually be fixed to work 100% with
911 Windows2000 (see comment above). */
912 avail
->u
.HighPart
= available
.u
.HighPart
;
913 avail
->u
.LowPart
= available
.u
.LowPart
;
919 /***********************************************************************
920 * GetDiskFreeSpaceExA (KERNEL32.@)
922 BOOL WINAPI
GetDiskFreeSpaceExA( LPCSTR root
, PULARGE_INTEGER avail
,
923 PULARGE_INTEGER total
,
924 PULARGE_INTEGER totalfree
)
926 UNICODE_STRING rootW
;
929 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
930 else rootW
.Buffer
= NULL
;
932 ret
= GetDiskFreeSpaceExW( rootW
.Buffer
, avail
, total
, totalfree
);
934 RtlFreeUnicodeString(&rootW
);
938 /***********************************************************************
939 * GetDriveTypeW (KERNEL32.@)
941 * Returns the type of the disk drive specified. If root is NULL the
942 * root of the current directory is used.
946 * Type of drive (from Win32 SDK):
948 * DRIVE_UNKNOWN unable to find out anything about the drive
949 * DRIVE_NO_ROOT_DIR nonexistent root dir
950 * DRIVE_REMOVABLE the disk can be removed from the machine
951 * DRIVE_FIXED the disk can not be removed from the machine
952 * DRIVE_REMOTE network disk
953 * DRIVE_CDROM CDROM drive
954 * DRIVE_RAMDISK virtual disk in RAM
956 UINT WINAPI
GetDriveTypeW(LPCWSTR root
) /* [in] String describing drive */
959 TRACE("(%s)\n", debugstr_w(root
));
961 if (NULL
== root
) drive
= DRIVE_GetCurrentDrive();
964 if ((root
[1]) && (root
[1] != ':'))
966 WARN("invalid root %s\n", debugstr_w(root
));
967 return DRIVE_NO_ROOT_DIR
;
969 drive
= toupperW(root
[0]) - 'A';
971 return DRIVE_GetType(drive
);
975 /***********************************************************************
976 * GetDriveTypeA (KERNEL32.@)
978 UINT WINAPI
GetDriveTypeA( LPCSTR root
)
980 UNICODE_STRING rootW
;
985 if( !RtlCreateUnicodeStringFromAsciiz(&rootW
, root
))
987 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
994 ret
= GetDriveTypeW(rootW
.Buffer
);
996 RtlFreeUnicodeString(&rootW
);
1002 /***********************************************************************
1003 * GetCurrentDirectory (KERNEL.411)
1005 UINT16 WINAPI
GetCurrentDirectory16( UINT16 buflen
, LPSTR buf
)
1007 WCHAR cur_dirW
[MAX_PATH
];
1009 DRIVE_GetCurrentDirectory(MAX_PATH
, cur_dirW
);
1010 return (UINT16
)WideCharToMultiByte(CP_ACP
, 0, cur_dirW
, -1, buf
, buflen
, NULL
, NULL
);
1014 /***********************************************************************
1015 * GetCurrentDirectoryW (KERNEL32.@)
1017 UINT WINAPI
GetCurrentDirectoryW( UINT buflen
, LPWSTR buf
)
1020 WCHAR longname
[MAX_PATHNAME_LEN
];
1021 WCHAR shortname
[MAX_PATHNAME_LEN
];
1023 ret
= DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN
, shortname
);
1024 if ( ret
> MAX_PATHNAME_LEN
) {
1025 ERR_(file
)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret
);
1028 GetLongPathNameW(shortname
, longname
, MAX_PATHNAME_LEN
);
1029 ret
= strlenW( longname
) + 1;
1030 if (ret
> buflen
) return ret
;
1031 strcpyW(buf
, longname
);
1035 /***********************************************************************
1036 * GetCurrentDirectoryA (KERNEL32.@)
1038 UINT WINAPI
GetCurrentDirectoryA( UINT buflen
, LPSTR buf
)
1040 WCHAR bufferW
[MAX_PATH
];
1043 retW
= GetCurrentDirectoryW(MAX_PATH
, bufferW
);
1047 else if (retW
> MAX_PATH
)
1049 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1054 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, NULL
, 0, NULL
, NULL
);
1057 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buf
, buflen
, NULL
, NULL
);
1058 ret
--; /* length without 0 */
1065 /***********************************************************************
1066 * SetCurrentDirectoryW (KERNEL32.@)
1068 BOOL WINAPI
SetCurrentDirectoryW( LPCWSTR dir
)
1070 int drive
, olddrive
= DRIVE_GetCurrentDrive();
1074 SetLastError(ERROR_INVALID_PARAMETER
);
1077 if (dir
[0] && (dir
[1]==':'))
1079 drive
= toupperW( *dir
) - 'A';
1085 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1086 sets pTask->curdir only if pTask->curdrive is drive */
1087 if (!(DRIVE_SetCurrentDrive( drive
)))
1090 /* FIXME: what about empty strings? Add a \\ ? */
1091 if (!DRIVE_Chdir( drive
, dir
)) {
1092 DRIVE_SetCurrentDrive(olddrive
);
1099 /***********************************************************************
1100 * SetCurrentDirectoryA (KERNEL32.@)
1102 BOOL WINAPI
SetCurrentDirectoryA( LPCSTR dir
)
1104 UNICODE_STRING dirW
;
1109 SetLastError(ERROR_INVALID_PARAMETER
);
1113 if (RtlCreateUnicodeStringFromAsciiz(&dirW
, dir
))
1115 ret
= SetCurrentDirectoryW(dirW
.Buffer
);
1116 RtlFreeUnicodeString(&dirW
);
1119 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1124 /***********************************************************************
1125 * GetLogicalDriveStringsA (KERNEL32.@)
1127 UINT WINAPI
GetLogicalDriveStringsA( UINT len
, LPSTR buffer
)
1131 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1132 if (DRIVE_IsValid(drive
)) count
++;
1133 if ((count
* 4) + 1 <= len
)
1136 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1137 if (DRIVE_IsValid(drive
))
1148 return (count
* 4) + 1; /* account for terminating null */
1149 /* The API tells about these different return values */
1153 /***********************************************************************
1154 * GetLogicalDriveStringsW (KERNEL32.@)
1156 UINT WINAPI
GetLogicalDriveStringsW( UINT len
, LPWSTR buffer
)
1160 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1161 if (DRIVE_IsValid(drive
)) count
++;
1162 if (count
* 4 * sizeof(WCHAR
) <= len
)
1165 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1166 if (DRIVE_IsValid(drive
))
1168 *p
++ = (WCHAR
)('a' + drive
);
1175 return count
* 4 * sizeof(WCHAR
);
1179 /***********************************************************************
1180 * GetLogicalDrives (KERNEL32.@)
1182 DWORD WINAPI
GetLogicalDrives(void)
1187 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1189 if ( (DRIVE_IsValid(drive
)) ||
1190 (DOSDrives
[drive
].type
== DRIVE_CDROM
)) /* audio CD is also valid */
1191 ret
|= (1 << drive
);