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>
43 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
50 #include "wine/winbase16.h" /* for GetCurrentTask */
56 #include "wine/unicode.h"
57 #include "wine/library.h"
58 #include "wine/server.h"
59 #include "wine/debug.h"
61 WINE_DEFAULT_DEBUG_CHANNEL(dosfs
);
62 WINE_DECLARE_DEBUG_CHANNEL(file
);
66 char *root
; /* root dir in Unix format without trailing / */
67 LPWSTR dos_cwd
; /* cwd in DOS format without leading or trailing \ */
68 char *unix_cwd
; /* cwd in Unix format without leading or trailing / */
69 char *device
; /* raw device path */
70 UINT type
; /* drive type */
71 dev_t dev
; /* unix device number */
72 ino_t ino
; /* unix inode number */
76 static const WCHAR DRIVE_Types
[][8] =
78 { 0 }, /* DRIVE_UNKNOWN */
79 { 0 }, /* DRIVE_NO_ROOT_DIR */
80 {'f','l','o','p','p','y',0}, /* DRIVE_REMOVABLE */
81 {'h','d',0}, /* DRIVE_FIXED */
82 {'n','e','t','w','o','r','k',0}, /* DRIVE_REMOTE */
83 {'c','d','r','o','m',0}, /* DRIVE_CDROM */
84 {'r','a','m','d','i','s','k',0} /* DRIVE_RAMDISK */
87 #define MAX_DOS_DRIVES 26
89 static DOSDRIVE DOSDrives
[MAX_DOS_DRIVES
];
90 static int DRIVE_CurDrive
= -1;
92 static HTASK16 DRIVE_LastTask
= 0;
94 /* strdup on the process heap */
95 inline static char *heap_strdup( const char *str
)
97 INT len
= strlen(str
) + 1;
98 LPSTR p
= HeapAlloc( GetProcessHeap(), 0, len
);
99 if (p
) memcpy( p
, str
, len
);
103 extern void CDROM_InitRegistry(int dev
);
105 /***********************************************************************
108 static inline UINT
DRIVE_GetDriveType( INT drive
, LPCWSTR value
)
112 for (i
= 0; i
< sizeof(DRIVE_Types
)/sizeof(DRIVE_Types
[0]); i
++)
114 if (!strcmpiW( value
, DRIVE_Types
[i
] )) return i
;
116 MESSAGE("Drive %c: unknown drive type %s, defaulting to 'hd'.\n",
117 'A' + drive
, debugstr_w(value
) );
122 /***********************************************************************
127 int i
, len
, symlink_count
= 0, count
= 0;
128 WCHAR driveW
[] = {'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
129 'W','i','n','e','\\','W','i','n','e','\\',
130 'C','o','n','f','i','g','\\','D','r','i','v','e',' ','A',0};
131 WCHAR drive_env
[] = {'=','A',':',0};
132 WCHAR path
[MAX_PATHNAME_LEN
];
133 char tmp
[MAX_PATHNAME_LEN
*sizeof(WCHAR
) + sizeof(KEY_VALUE_PARTIAL_INFORMATION
)];
134 struct stat drive_stat_buffer
;
139 OBJECT_ATTRIBUTES attr
;
140 UNICODE_STRING nameW
;
142 const char *config_dir
= wine_get_config_dir();
144 static const WCHAR PathW
[] = {'P','a','t','h',0};
145 static const WCHAR TypeW
[] = {'T','y','p','e',0};
146 static const WCHAR DeviceW
[] = {'D','e','v','i','c','e',0};
148 attr
.Length
= sizeof(attr
);
149 attr
.RootDirectory
= 0;
150 attr
.ObjectName
= &nameW
;
152 attr
.SecurityDescriptor
= NULL
;
153 attr
.SecurityQualityOfService
= NULL
;
155 /* get the root of the drives from symlinks */
158 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
162 root
= HeapAlloc( GetProcessHeap(), 0, strlen(config_dir
) + sizeof("/dosdevices/a:") );
163 strcpy( root
, config_dir
);
164 strcat( root
, "/dosdevices/a:" );
166 root
[strlen(root
)-2] = 'a' + i
;
167 if (stat( root
, &drive_stat_buffer
))
169 if (!lstat( root
, &drive_stat_buffer
))
170 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
171 root
, strerror(errno
), 'a' + i
);
174 if (!S_ISDIR(drive_stat_buffer
.st_mode
))
176 MESSAGE("%s is not a directory, ignoring drive %c:\n", root
, 'a' + i
);
180 drive
->dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(drive
->dos_cwd
[0]));
181 drive
->unix_cwd
= heap_strdup( "" );
182 drive
->device
= NULL
;
183 drive
->dev
= drive_stat_buffer
.st_dev
;
184 drive
->ino
= drive_stat_buffer
.st_ino
;
185 drive
->type
= DRIVE_FIXED
;
189 if (root
) HeapFree( GetProcessHeap(), 0, root
);
191 /* now get the parameters from the config file */
193 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
195 RtlInitUnicodeString( &nameW
, driveW
);
196 nameW
.Buffer
[(nameW
.Length
/ sizeof(WCHAR
)) - 1] = 'A' + i
;
197 if (NtOpenKey( &hkey
, KEY_ALL_ACCESS
, &attr
) != STATUS_SUCCESS
) continue;
199 /* Get the root path */
202 RtlInitUnicodeString( &nameW
, PathW
);
203 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
205 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
206 ExpandEnvironmentStringsW( data
, path
, sizeof(path
)/sizeof(WCHAR
) );
208 p
= path
+ strlenW(path
) - 1;
209 while ((p
> path
) && (*p
== '/')) *p
-- = '\0';
213 len
= WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1, NULL
, 0, NULL
, NULL
);
214 drive
->root
= HeapAlloc(GetProcessHeap(), 0, len
);
215 WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1, drive
->root
, len
, NULL
, NULL
);
219 /* relative paths are relative to config dir */
220 const char *config
= wine_get_config_dir();
221 len
= strlen(config
);
222 len
+= WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1, NULL
, 0, NULL
, NULL
) + 2;
223 drive
->root
= HeapAlloc( GetProcessHeap(), 0, len
);
224 len
-= sprintf( drive
->root
, "%s/", config
);
225 WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1,
226 drive
->root
+ strlen(drive
->root
), len
, NULL
, NULL
);
229 if (stat( drive
->root
, &drive_stat_buffer
))
231 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
232 drive
->root
, strerror(errno
), 'A' + i
);
233 HeapFree( GetProcessHeap(), 0, drive
->root
);
237 if (!S_ISDIR(drive_stat_buffer
.st_mode
))
239 MESSAGE("%s is not a directory, ignoring drive %c:\n",
240 drive
->root
, 'A' + i
);
241 HeapFree( GetProcessHeap(), 0, drive
->root
);
246 drive
->dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(drive
->dos_cwd
[0]));
247 drive
->unix_cwd
= heap_strdup( "" );
248 drive
->device
= NULL
;
249 drive
->dev
= drive_stat_buffer
.st_dev
;
250 drive
->ino
= drive_stat_buffer
.st_ino
;
251 drive
->type
= DRIVE_FIXED
;
257 /* Get the drive type */
258 RtlInitUnicodeString( &nameW
, TypeW
);
259 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
261 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
262 drive
->type
= DRIVE_GetDriveType( i
, data
);
266 RtlInitUnicodeString( &nameW
, DeviceW
);
267 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
269 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
270 len
= WideCharToMultiByte(CP_UNIXCP
, 0, data
, -1, NULL
, 0, NULL
, NULL
);
271 drive
->device
= HeapAlloc(GetProcessHeap(), 0, len
);
272 WideCharToMultiByte(CP_UNIXCP
, 0, data
, -1, drive
->device
, len
, NULL
, NULL
);
274 if (drive
->type
== DRIVE_CDROM
)
277 if ((cd_fd
= open(drive
->device
, O_RDONLY
|O_NONBLOCK
)) != -1)
279 CDROM_InitRegistry(cd_fd
);
285 /* Make the first hard disk the current drive */
286 if ((DRIVE_CurDrive
== -1) && (drive
->type
== DRIVE_FIXED
))
290 TRACE("Drive %c: path=%s type=%s dev=%x ino=%x\n",
291 'A' + i
, drive
->root
, debugstr_w(DRIVE_Types
[drive
->type
]),
292 (int)drive
->dev
, (int)drive
->ino
);
299 if (!count
&& !symlink_count
)
301 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
302 /* Create a C drive pointing to Unix root dir */
303 DOSDrives
[2].root
= heap_strdup( "/" );
304 DOSDrives
[2].dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(DOSDrives
[2].dos_cwd
[0]));
305 DOSDrives
[2].unix_cwd
= heap_strdup( "" );
306 DOSDrives
[2].type
= DRIVE_FIXED
;
307 DOSDrives
[2].device
= NULL
;
311 /* Make sure the current drive is valid */
312 if (DRIVE_CurDrive
== -1)
314 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
324 /* get current working directory info for all drives */
325 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++, drive_env
[1]++)
327 if (!GetEnvironmentVariableW(drive_env
, path
, MAX_PATHNAME_LEN
)) continue;
329 if (toupperW(path
[0]) != drive_env
[1] || path
[1] != ':') continue;
330 DRIVE_Chdir( i
, path
+ 2 );
336 /***********************************************************************
339 int DRIVE_IsValid( int drive
)
341 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
342 return (DOSDrives
[drive
].root
!= NULL
);
346 /***********************************************************************
347 * DRIVE_GetCurrentDrive
349 int DRIVE_GetCurrentDrive(void)
351 TDB
*pTask
= GlobalLock16(GetCurrentTask());
352 if (pTask
&& (pTask
->curdrive
& 0x80)) return pTask
->curdrive
& ~0x80;
353 return DRIVE_CurDrive
;
357 /***********************************************************************
358 * DRIVE_SetCurrentDrive
360 static int DRIVE_SetCurrentDrive( int drive
)
362 TDB
*pTask
= GlobalLock16(GetCurrentTask());
363 if (!DRIVE_IsValid( drive
))
365 SetLastError( ERROR_INVALID_DRIVE
);
368 TRACE("%c:\n", 'A' + drive
);
369 DRIVE_CurDrive
= drive
;
370 if (pTask
) pTask
->curdrive
= drive
| 0x80;
375 /***********************************************************************
376 * DRIVE_FindDriveRoot
378 * Find a drive for which the root matches the beginning of the given path.
379 * This can be used to translate a Unix path into a drive + DOS path.
380 * Return value is the drive, or -1 on error. On success, path is modified
381 * to point to the beginning of the DOS path.
383 * Note: path must be in the encoding of the underlying Unix file system.
385 int DRIVE_FindDriveRoot( const char **path
)
387 /* Starting with the full path, check if the device and inode match any of
388 * the wine 'drives'. If not then remove the last path component and try
389 * again. If the last component was a '..' then skip a normal component
390 * since it's a directory that's ascended back out of.
392 int drive
, level
, len
;
393 char buffer
[MAX_PATHNAME_LEN
];
397 strcpy( buffer
, *path
);
398 for (p
= buffer
; *p
; p
++) if (*p
== '\\') *p
= '/';
401 /* strip off trailing slashes */
402 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
407 if (stat( buffer
, &st
) == 0 && S_ISDIR( st
.st_mode
))
409 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
411 if (!DOSDrives
[drive
].root
) continue;
413 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
414 (DOSDrives
[drive
].ino
== st
.st_ino
))
416 if (len
== 1) len
= 0; /* preserve root slash in returned path */
417 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
418 *path
, 'A' + drive
, buffer
, *path
+ len
);
420 if (!**path
) *path
= "\\";
425 if (len
<= 1) return -1; /* reached root */
430 /* find start of the last path component */
431 while (len
> 1 && buffer
[len
- 1] != '/') len
--;
432 if (!buffer
[len
]) break; /* empty component -> reached root */
433 /* does removing it take us up a level? */
434 if (strcmp( buffer
+ len
, "." ) != 0)
435 level
+= strcmp( buffer
+ len
, ".." ) ? 1 : -1;
437 /* strip off trailing slashes */
438 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
444 /***********************************************************************
445 * DRIVE_FindDriveRootW
447 * Unicode version of DRIVE_FindDriveRoot.
449 int DRIVE_FindDriveRootW( LPCWSTR
*path
)
451 int drive
, level
, len
;
452 WCHAR buffer
[MAX_PATHNAME_LEN
];
456 strcpyW( buffer
, *path
);
457 for (p
= buffer
; *p
; p
++) if (*p
== '\\') *p
= '/';
460 /* strip off trailing slashes */
461 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
465 char buffA
[MAX_PATHNAME_LEN
];
467 WideCharToMultiByte( CP_UNIXCP
, 0, buffer
, -1, buffA
, sizeof(buffA
), NULL
, NULL
);
468 if (stat( buffA
, &st
) == 0 && S_ISDIR( st
.st_mode
))
471 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
473 if (!DOSDrives
[drive
].root
) continue;
475 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
476 (DOSDrives
[drive
].ino
== st
.st_ino
))
478 static const WCHAR rootW
[] = {'\\',0};
480 if (len
== 1) len
= 0; /* preserve root slash in returned path */
481 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
482 debugstr_w(*path
), 'A' + drive
, debugstr_w(buffer
), debugstr_w(*path
+ len
));
484 if (!**path
) *path
= rootW
;
489 if (len
<= 1) return -1; /* reached root */
494 static const WCHAR dotW
[] = {'.',0};
495 static const WCHAR dotdotW
[] = {'.','.',0};
497 /* find start of the last path component */
498 while (len
> 1 && buffer
[len
- 1] != '/') len
--;
499 if (!buffer
[len
]) break; /* empty component -> reached root */
500 /* does removing it take us up a level? */
501 if (strcmpW( buffer
+ len
, dotW
) != 0)
502 level
+= strcmpW( buffer
+ len
, dotdotW
) ? 1 : -1;
504 /* strip off trailing slashes */
505 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
511 /***********************************************************************
514 const char * DRIVE_GetRoot( int drive
)
516 if (!DRIVE_IsValid( drive
)) return NULL
;
517 return DOSDrives
[drive
].root
;
521 /***********************************************************************
524 LPCWSTR
DRIVE_GetDosCwd( int drive
)
526 TDB
*pTask
= GlobalLock16(GetCurrentTask());
527 if (!DRIVE_IsValid( drive
)) return NULL
;
529 /* Check if we need to change the directory to the new task. */
530 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
531 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
532 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
534 static const WCHAR rootW
[] = {'\\',0};
535 WCHAR curdirW
[MAX_PATH
];
536 MultiByteToWideChar(CP_ACP
, 0, pTask
->curdir
, -1, curdirW
, MAX_PATH
);
537 /* Perform the task-switch */
538 if (!DRIVE_Chdir( drive
, curdirW
)) DRIVE_Chdir( drive
, rootW
);
539 DRIVE_LastTask
= GetCurrentTask();
541 return DOSDrives
[drive
].dos_cwd
;
545 /***********************************************************************
548 const char * DRIVE_GetUnixCwd( int drive
)
550 TDB
*pTask
= GlobalLock16(GetCurrentTask());
551 if (!DRIVE_IsValid( drive
)) return NULL
;
553 /* Check if we need to change the directory to the new task. */
554 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
555 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
556 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
558 static const WCHAR rootW
[] = {'\\',0};
559 WCHAR curdirW
[MAX_PATH
];
560 MultiByteToWideChar(CP_ACP
, 0, pTask
->curdir
, -1, curdirW
, MAX_PATH
);
561 /* Perform the task-switch */
562 if (!DRIVE_Chdir( drive
, curdirW
)) DRIVE_Chdir( drive
, rootW
);
563 DRIVE_LastTask
= GetCurrentTask();
565 return DOSDrives
[drive
].unix_cwd
;
569 /***********************************************************************
572 const char * DRIVE_GetDevice( int drive
)
574 return (DRIVE_IsValid( drive
)) ? DOSDrives
[drive
].device
: NULL
;
577 /***********************************************************************
580 static UINT
DRIVE_GetType( int drive
)
582 if (!DRIVE_IsValid( drive
)) return DRIVE_NO_ROOT_DIR
;
583 return DOSDrives
[drive
].type
;
587 /***********************************************************************
590 int DRIVE_Chdir( int drive
, LPCWSTR path
)
592 DOS_FULL_NAME full_name
;
593 WCHAR buffer
[MAX_PATHNAME_LEN
];
595 BY_HANDLE_FILE_INFORMATION info
;
596 TDB
*pTask
= GlobalLock16(GetCurrentTask());
598 buffer
[0] = 'A' + drive
;
601 TRACE("(%s,%s)\n", debugstr_w(buffer
), debugstr_w(path
) );
602 strncpyW( buffer
+ 2, path
, MAX_PATHNAME_LEN
- 2 );
603 buffer
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
605 if (!DOSFS_GetFullName( buffer
, TRUE
, &full_name
)) return 0;
606 if (!FILE_Stat( full_name
.long_name
, &info
, NULL
)) return 0;
607 if (!(info
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
609 SetLastError( ERROR_FILE_NOT_FOUND
);
612 unix_cwd
= full_name
.long_name
+ strlen( DOSDrives
[drive
].root
);
613 while (*unix_cwd
== '/') unix_cwd
++;
615 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
616 'A' + drive
, unix_cwd
, debugstr_w(full_name
.short_name
+ 3) );
618 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].dos_cwd
);
619 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].unix_cwd
);
620 DOSDrives
[drive
].dos_cwd
= HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name
.short_name
) - 2) * sizeof(WCHAR
));
621 strcpyW(DOSDrives
[drive
].dos_cwd
, full_name
.short_name
+ 3);
622 DOSDrives
[drive
].unix_cwd
= heap_strdup( unix_cwd
);
624 if (drive
== DRIVE_CurDrive
)
628 RtlInitUnicodeString( &dirW
, full_name
.short_name
);
629 RtlSetCurrentDirectory_U( &dirW
);
632 if (pTask
&& (pTask
->curdrive
& 0x80) &&
633 ((pTask
->curdrive
& ~0x80) == drive
))
635 WideCharToMultiByte(CP_ACP
, 0, full_name
.short_name
+ 2, -1,
636 pTask
->curdir
, sizeof(pTask
->curdir
), NULL
, NULL
);
637 DRIVE_LastTask
= GetCurrentTask();
643 /***********************************************************************
644 * DRIVE_GetCurrentDirectory
645 * Returns "X:\\path\\etc\\".
647 * Despite the API description, return required length including the
648 * terminating null when buffer too small. This is the real behaviour.
650 static UINT
DRIVE_GetCurrentDirectory( UINT buflen
, LPWSTR buf
)
653 LPCWSTR dos_cwd
= DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
654 static const WCHAR driveA_rootW
[] = {'A',':','\\',0};
656 ret
= strlenW(dos_cwd
) + 3; /* length of WHOLE current directory */
657 if (ret
>= buflen
) return ret
+ 1;
659 strcpyW( buf
, driveA_rootW
);
660 buf
[0] += DRIVE_GetCurrentDrive();
661 strcatW( buf
, dos_cwd
);
666 /***********************************************************************
669 * Build the environment array containing the drives' current directories.
670 * Resulting pointer must be freed with HeapFree.
672 WCHAR
*DRIVE_BuildEnv(void)
675 LPCWSTR cwd
[MAX_DOS_DRIVES
];
678 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++)
680 if ((cwd
[i
] = DRIVE_GetDosCwd(i
)) && cwd
[i
][0])
681 length
+= strlenW(cwd
[i
]) + 8;
683 if (!(env
= HeapAlloc( GetProcessHeap(), 0, (length
+1) * sizeof(WCHAR
) ))) return NULL
;
684 for (i
= 0, p
= env
; i
< MAX_DOS_DRIVES
; i
++)
686 if (cwd
[i
] && cwd
[i
][0])
688 *p
++ = '='; *p
++ = 'A' + i
; *p
++ = ':';
689 *p
++ = '='; *p
++ = 'A' + i
; *p
++ = ':'; *p
++ = '\\';
690 strcpyW( p
, cwd
[i
] );
699 /***********************************************************************
700 * GetDriveTypeW (KERNEL32.@)
702 * Returns the type of the disk drive specified. If root is NULL the
703 * root of the current directory is used.
707 * Type of drive (from Win32 SDK):
709 * DRIVE_UNKNOWN unable to find out anything about the drive
710 * DRIVE_NO_ROOT_DIR nonexistent root dir
711 * DRIVE_REMOVABLE the disk can be removed from the machine
712 * DRIVE_FIXED the disk can not be removed from the machine
713 * DRIVE_REMOTE network disk
714 * DRIVE_CDROM CDROM drive
715 * DRIVE_RAMDISK virtual disk in RAM
717 UINT WINAPI
GetDriveTypeW(LPCWSTR root
) /* [in] String describing drive */
720 TRACE("(%s)\n", debugstr_w(root
));
722 if (NULL
== root
) drive
= DRIVE_GetCurrentDrive();
725 if ((root
[1]) && (root
[1] != ':'))
727 WARN("invalid root %s\n", debugstr_w(root
));
728 return DRIVE_NO_ROOT_DIR
;
730 drive
= toupperW(root
[0]) - 'A';
732 return DRIVE_GetType(drive
);
736 /***********************************************************************
737 * GetDriveTypeA (KERNEL32.@)
739 UINT WINAPI
GetDriveTypeA( LPCSTR root
)
741 UNICODE_STRING rootW
;
746 if( !RtlCreateUnicodeStringFromAsciiz(&rootW
, root
))
748 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
755 ret
= GetDriveTypeW(rootW
.Buffer
);
757 RtlFreeUnicodeString(&rootW
);
763 /***********************************************************************
764 * GetCurrentDirectory (KERNEL.411)
766 UINT16 WINAPI
GetCurrentDirectory16( UINT16 buflen
, LPSTR buf
)
768 WCHAR cur_dirW
[MAX_PATH
];
770 DRIVE_GetCurrentDirectory(MAX_PATH
, cur_dirW
);
771 return (UINT16
)WideCharToMultiByte(CP_ACP
, 0, cur_dirW
, -1, buf
, buflen
, NULL
, NULL
);
775 /***********************************************************************
776 * GetCurrentDirectoryW (KERNEL32.@)
778 UINT WINAPI
GetCurrentDirectoryW( UINT buflen
, LPWSTR buf
)
781 WCHAR longname
[MAX_PATHNAME_LEN
];
782 WCHAR shortname
[MAX_PATHNAME_LEN
];
784 ret
= DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN
, shortname
);
785 if ( ret
> MAX_PATHNAME_LEN
) {
786 ERR_(file
)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret
);
789 GetLongPathNameW(shortname
, longname
, MAX_PATHNAME_LEN
);
790 ret
= strlenW( longname
) + 1;
791 if (ret
> buflen
) return ret
;
792 strcpyW(buf
, longname
);
796 /***********************************************************************
797 * GetCurrentDirectoryA (KERNEL32.@)
799 UINT WINAPI
GetCurrentDirectoryA( UINT buflen
, LPSTR buf
)
801 WCHAR bufferW
[MAX_PATH
];
804 retW
= GetCurrentDirectoryW(MAX_PATH
, bufferW
);
808 else if (retW
> MAX_PATH
)
810 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
815 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, NULL
, 0, NULL
, NULL
);
818 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buf
, buflen
, NULL
, NULL
);
819 ret
--; /* length without 0 */
826 /***********************************************************************
827 * SetCurrentDirectoryW (KERNEL32.@)
829 BOOL WINAPI
SetCurrentDirectoryW( LPCWSTR dir
)
831 int drive
, olddrive
= DRIVE_GetCurrentDrive();
835 SetLastError(ERROR_INVALID_PARAMETER
);
838 if (dir
[0] && (dir
[1]==':'))
840 drive
= toupperW( *dir
) - 'A';
846 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
847 sets pTask->curdir only if pTask->curdrive is drive */
848 if (!(DRIVE_SetCurrentDrive( drive
)))
851 /* FIXME: what about empty strings? Add a \\ ? */
852 if (!DRIVE_Chdir( drive
, dir
)) {
853 DRIVE_SetCurrentDrive(olddrive
);
860 /***********************************************************************
861 * SetCurrentDirectoryA (KERNEL32.@)
863 BOOL WINAPI
SetCurrentDirectoryA( LPCSTR dir
)
870 SetLastError(ERROR_INVALID_PARAMETER
);
874 if (RtlCreateUnicodeStringFromAsciiz(&dirW
, dir
))
876 ret
= SetCurrentDirectoryW(dirW
.Buffer
);
877 RtlFreeUnicodeString(&dirW
);
880 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
885 /***********************************************************************
886 * GetLogicalDriveStringsA (KERNEL32.@)
888 UINT WINAPI
GetLogicalDriveStringsA( UINT len
, LPSTR buffer
)
892 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
893 if (DRIVE_IsValid(drive
)) count
++;
894 if ((count
* 4) + 1 <= len
)
897 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
898 if (DRIVE_IsValid(drive
))
909 return (count
* 4) + 1; /* account for terminating null */
910 /* The API tells about these different return values */
914 /***********************************************************************
915 * GetLogicalDriveStringsW (KERNEL32.@)
917 UINT WINAPI
GetLogicalDriveStringsW( UINT len
, LPWSTR buffer
)
921 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
922 if (DRIVE_IsValid(drive
)) count
++;
923 if (count
* 4 * sizeof(WCHAR
) <= len
)
926 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
927 if (DRIVE_IsValid(drive
))
929 *p
++ = (WCHAR
)('a' + drive
);
936 return count
* 4 * sizeof(WCHAR
);
940 /***********************************************************************
941 * GetLogicalDrives (KERNEL32.@)
943 DWORD WINAPI
GetLogicalDrives(void)
948 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
950 if ( (DRIVE_IsValid(drive
)) ||
951 (DOSDrives
[drive
].type
== DRIVE_CDROM
)) /* audio CD is also valid */