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 dev_t dev
; /* unix device number */
71 ino_t ino
; /* unix inode number */
75 #define MAX_DOS_DRIVES 26
77 static DOSDRIVE DOSDrives
[MAX_DOS_DRIVES
];
78 static int DRIVE_CurDrive
= -1;
80 static HTASK16 DRIVE_LastTask
= 0;
82 /* strdup on the process heap */
83 inline static char *heap_strdup( const char *str
)
85 INT len
= strlen(str
) + 1;
86 LPSTR p
= HeapAlloc( GetProcessHeap(), 0, len
);
87 if (p
) memcpy( p
, str
, len
);
91 /***********************************************************************
96 int i
, len
, symlink_count
= 0, count
= 0;
97 WCHAR driveW
[] = {'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
98 'W','i','n','e','\\','W','i','n','e','\\',
99 'C','o','n','f','i','g','\\','D','r','i','v','e',' ','A',0};
100 WCHAR drive_env
[] = {'=','A',':',0};
101 WCHAR path
[MAX_PATHNAME_LEN
];
102 char tmp
[MAX_PATHNAME_LEN
*sizeof(WCHAR
) + sizeof(KEY_VALUE_PARTIAL_INFORMATION
)];
103 struct stat drive_stat_buffer
;
108 OBJECT_ATTRIBUTES attr
;
109 UNICODE_STRING nameW
;
111 const char *config_dir
= wine_get_config_dir();
113 static const WCHAR PathW
[] = {'P','a','t','h',0};
114 static const WCHAR DeviceW
[] = {'D','e','v','i','c','e',0};
116 attr
.Length
= sizeof(attr
);
117 attr
.RootDirectory
= 0;
118 attr
.ObjectName
= &nameW
;
120 attr
.SecurityDescriptor
= NULL
;
121 attr
.SecurityQualityOfService
= NULL
;
123 /* get the root of the drives from symlinks */
126 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
130 root
= HeapAlloc( GetProcessHeap(), 0, strlen(config_dir
) + sizeof("/dosdevices/a:") );
131 strcpy( root
, config_dir
);
132 strcat( root
, "/dosdevices/a:" );
134 root
[strlen(root
)-2] = 'a' + i
;
135 if (stat( root
, &drive_stat_buffer
))
137 if (!lstat( root
, &drive_stat_buffer
))
138 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
139 root
, strerror(errno
), 'a' + i
);
142 if (!S_ISDIR(drive_stat_buffer
.st_mode
))
144 MESSAGE("%s is not a directory, ignoring drive %c:\n", root
, 'a' + i
);
148 drive
->dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(drive
->dos_cwd
[0]));
149 drive
->unix_cwd
= heap_strdup( "" );
150 drive
->device
= NULL
;
151 drive
->dev
= drive_stat_buffer
.st_dev
;
152 drive
->ino
= drive_stat_buffer
.st_ino
;
156 if (root
) HeapFree( GetProcessHeap(), 0, root
);
158 /* now get the parameters from the config file */
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 */
169 RtlInitUnicodeString( &nameW
, PathW
);
170 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
172 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
173 ExpandEnvironmentStringsW( data
, path
, sizeof(path
)/sizeof(WCHAR
) );
175 p
= path
+ strlenW(path
) - 1;
176 while ((p
> path
) && (*p
== '/')) *p
-- = '\0';
180 len
= WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1, NULL
, 0, NULL
, NULL
);
181 drive
->root
= HeapAlloc(GetProcessHeap(), 0, len
);
182 WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1, drive
->root
, len
, NULL
, NULL
);
186 /* relative paths are relative to config dir */
187 const char *config
= wine_get_config_dir();
188 len
= strlen(config
);
189 len
+= WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1, NULL
, 0, NULL
, NULL
) + 2;
190 drive
->root
= HeapAlloc( GetProcessHeap(), 0, len
);
191 len
-= sprintf( drive
->root
, "%s/", config
);
192 WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1,
193 drive
->root
+ strlen(drive
->root
), len
, NULL
, NULL
);
196 if (stat( drive
->root
, &drive_stat_buffer
))
198 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
199 drive
->root
, strerror(errno
), 'A' + i
);
200 HeapFree( GetProcessHeap(), 0, drive
->root
);
204 if (!S_ISDIR(drive_stat_buffer
.st_mode
))
206 MESSAGE("%s is not a directory, ignoring drive %c:\n",
207 drive
->root
, 'A' + i
);
208 HeapFree( GetProcessHeap(), 0, drive
->root
);
213 drive
->dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(drive
->dos_cwd
[0]));
214 drive
->unix_cwd
= heap_strdup( "" );
215 drive
->device
= NULL
;
216 drive
->dev
= drive_stat_buffer
.st_dev
;
217 drive
->ino
= drive_stat_buffer
.st_ino
;
224 RtlInitUnicodeString( &nameW
, DeviceW
);
225 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
227 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
228 len
= WideCharToMultiByte(CP_UNIXCP
, 0, data
, -1, NULL
, 0, NULL
, NULL
);
229 drive
->device
= HeapAlloc(GetProcessHeap(), 0, len
);
230 WideCharToMultiByte(CP_UNIXCP
, 0, data
, -1, drive
->device
, len
, NULL
, NULL
);
234 TRACE("Drive %c: path=%s dev=%x ino=%x\n",
235 'A' + i
, drive
->root
, (int)drive
->dev
, (int)drive
->ino
);
242 if (!count
&& !symlink_count
)
244 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
245 /* Create a C drive pointing to Unix root dir */
246 DOSDrives
[2].root
= heap_strdup( "/" );
247 DOSDrives
[2].dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(DOSDrives
[2].dos_cwd
[0]));
248 DOSDrives
[2].unix_cwd
= heap_strdup( "" );
249 DOSDrives
[2].device
= NULL
;
253 /* Make sure the current drive is valid */
254 if (DRIVE_CurDrive
== -1)
256 for (i
= 2, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
266 /* get current working directory info for all drives */
267 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++, drive_env
[1]++)
269 if (!GetEnvironmentVariableW(drive_env
, path
, MAX_PATHNAME_LEN
)) continue;
271 if (toupperW(path
[0]) != drive_env
[1] || path
[1] != ':') continue;
272 DRIVE_Chdir( i
, path
+ 2 );
278 /***********************************************************************
281 int DRIVE_IsValid( int drive
)
283 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
284 return (DOSDrives
[drive
].root
!= NULL
);
288 /***********************************************************************
289 * DRIVE_GetCurrentDrive
291 int DRIVE_GetCurrentDrive(void)
293 TDB
*pTask
= GlobalLock16(GetCurrentTask());
294 if (pTask
&& (pTask
->curdrive
& 0x80)) return pTask
->curdrive
& ~0x80;
295 return DRIVE_CurDrive
;
299 /***********************************************************************
300 * DRIVE_SetCurrentDrive
302 static int DRIVE_SetCurrentDrive( int drive
)
304 TDB
*pTask
= GlobalLock16(GetCurrentTask());
305 if (!DRIVE_IsValid( drive
))
307 SetLastError( ERROR_INVALID_DRIVE
);
310 TRACE("%c:\n", 'A' + drive
);
311 DRIVE_CurDrive
= drive
;
312 if (pTask
) pTask
->curdrive
= drive
| 0x80;
317 /***********************************************************************
318 * DRIVE_FindDriveRoot
320 * Find a drive for which the root matches the beginning of the given path.
321 * This can be used to translate a Unix path into a drive + DOS path.
322 * Return value is the drive, or -1 on error. On success, path is modified
323 * to point to the beginning of the DOS path.
325 * Note: path must be in the encoding of the underlying Unix file system.
327 int DRIVE_FindDriveRoot( const char **path
)
329 /* Starting with the full path, check if the device and inode match any of
330 * the wine 'drives'. If not then remove the last path component and try
331 * again. If the last component was a '..' then skip a normal component
332 * since it's a directory that's ascended back out of.
334 int drive
, level
, len
;
335 char buffer
[MAX_PATHNAME_LEN
];
339 strcpy( buffer
, *path
);
340 for (p
= buffer
; *p
; p
++) if (*p
== '\\') *p
= '/';
343 /* strip off trailing slashes */
344 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
349 if (stat( buffer
, &st
) == 0 && S_ISDIR( st
.st_mode
))
351 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
353 if (!DOSDrives
[drive
].root
) continue;
355 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
356 (DOSDrives
[drive
].ino
== st
.st_ino
))
358 if (len
== 1) len
= 0; /* preserve root slash in returned path */
359 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
360 *path
, 'A' + drive
, buffer
, *path
+ len
);
362 if (!**path
) *path
= "\\";
367 if (len
<= 1) return -1; /* reached root */
372 /* find start of the last path component */
373 while (len
> 1 && buffer
[len
- 1] != '/') len
--;
374 if (!buffer
[len
]) break; /* empty component -> reached root */
375 /* does removing it take us up a level? */
376 if (strcmp( buffer
+ len
, "." ) != 0)
377 level
+= strcmp( buffer
+ len
, ".." ) ? 1 : -1;
379 /* strip off trailing slashes */
380 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
386 /***********************************************************************
387 * DRIVE_FindDriveRootW
389 * Unicode version of DRIVE_FindDriveRoot.
391 int DRIVE_FindDriveRootW( LPCWSTR
*path
)
393 int drive
, level
, len
;
394 WCHAR buffer
[MAX_PATHNAME_LEN
];
398 strcpyW( buffer
, *path
);
399 for (p
= buffer
; *p
; p
++) if (*p
== '\\') *p
= '/';
402 /* strip off trailing slashes */
403 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
407 char buffA
[MAX_PATHNAME_LEN
];
409 WideCharToMultiByte( CP_UNIXCP
, 0, buffer
, -1, buffA
, sizeof(buffA
), NULL
, NULL
);
410 if (stat( buffA
, &st
) == 0 && S_ISDIR( st
.st_mode
))
413 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
415 if (!DOSDrives
[drive
].root
) continue;
417 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
418 (DOSDrives
[drive
].ino
== st
.st_ino
))
420 static const WCHAR rootW
[] = {'\\',0};
422 if (len
== 1) len
= 0; /* preserve root slash in returned path */
423 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
424 debugstr_w(*path
), 'A' + drive
, debugstr_w(buffer
), debugstr_w(*path
+ len
));
426 if (!**path
) *path
= rootW
;
431 if (len
<= 1) return -1; /* reached root */
436 static const WCHAR dotW
[] = {'.',0};
437 static const WCHAR dotdotW
[] = {'.','.',0};
439 /* find start of the last path component */
440 while (len
> 1 && buffer
[len
- 1] != '/') len
--;
441 if (!buffer
[len
]) break; /* empty component -> reached root */
442 /* does removing it take us up a level? */
443 if (strcmpW( buffer
+ len
, dotW
) != 0)
444 level
+= strcmpW( buffer
+ len
, dotdotW
) ? 1 : -1;
446 /* strip off trailing slashes */
447 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
453 /***********************************************************************
456 const char * DRIVE_GetRoot( int drive
)
458 if (!DRIVE_IsValid( drive
)) return NULL
;
459 return DOSDrives
[drive
].root
;
463 /***********************************************************************
466 LPCWSTR
DRIVE_GetDosCwd( int drive
)
468 TDB
*pTask
= GlobalLock16(GetCurrentTask());
469 if (!DRIVE_IsValid( drive
)) return NULL
;
471 /* Check if we need to change the directory to the new task. */
472 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
473 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
474 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
476 static const WCHAR rootW
[] = {'\\',0};
477 WCHAR curdirW
[MAX_PATH
];
478 MultiByteToWideChar(CP_ACP
, 0, pTask
->curdir
, -1, curdirW
, MAX_PATH
);
479 /* Perform the task-switch */
480 if (!DRIVE_Chdir( drive
, curdirW
)) DRIVE_Chdir( drive
, rootW
);
481 DRIVE_LastTask
= GetCurrentTask();
483 return DOSDrives
[drive
].dos_cwd
;
487 /***********************************************************************
490 const char * DRIVE_GetUnixCwd( int drive
)
492 TDB
*pTask
= GlobalLock16(GetCurrentTask());
493 if (!DRIVE_IsValid( drive
)) return NULL
;
495 /* Check if we need to change the directory to the new task. */
496 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
497 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
498 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
500 static const WCHAR rootW
[] = {'\\',0};
501 WCHAR curdirW
[MAX_PATH
];
502 MultiByteToWideChar(CP_ACP
, 0, pTask
->curdir
, -1, curdirW
, MAX_PATH
);
503 /* Perform the task-switch */
504 if (!DRIVE_Chdir( drive
, curdirW
)) DRIVE_Chdir( drive
, rootW
);
505 DRIVE_LastTask
= GetCurrentTask();
507 return DOSDrives
[drive
].unix_cwd
;
511 /***********************************************************************
514 const char * DRIVE_GetDevice( int drive
)
516 return (DRIVE_IsValid( drive
)) ? DOSDrives
[drive
].device
: NULL
;
519 /***********************************************************************
522 int DRIVE_Chdir( int drive
, LPCWSTR path
)
524 DOS_FULL_NAME full_name
;
525 WCHAR buffer
[MAX_PATHNAME_LEN
];
527 BY_HANDLE_FILE_INFORMATION info
;
528 TDB
*pTask
= GlobalLock16(GetCurrentTask());
530 buffer
[0] = 'A' + drive
;
533 TRACE("(%s,%s)\n", debugstr_w(buffer
), debugstr_w(path
) );
534 strncpyW( buffer
+ 2, path
, MAX_PATHNAME_LEN
- 2 );
535 buffer
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
537 if (!DOSFS_GetFullName( buffer
, TRUE
, &full_name
)) return 0;
538 if (!FILE_Stat( full_name
.long_name
, &info
, NULL
)) return 0;
539 if (!(info
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
541 SetLastError( ERROR_FILE_NOT_FOUND
);
544 unix_cwd
= full_name
.long_name
+ strlen( DOSDrives
[drive
].root
);
545 while (*unix_cwd
== '/') unix_cwd
++;
547 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
548 'A' + drive
, unix_cwd
, debugstr_w(full_name
.short_name
+ 3) );
550 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].dos_cwd
);
551 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].unix_cwd
);
552 DOSDrives
[drive
].dos_cwd
= HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name
.short_name
) - 2) * sizeof(WCHAR
));
553 strcpyW(DOSDrives
[drive
].dos_cwd
, full_name
.short_name
+ 3);
554 DOSDrives
[drive
].unix_cwd
= heap_strdup( unix_cwd
);
556 if (drive
== DRIVE_CurDrive
)
560 RtlInitUnicodeString( &dirW
, full_name
.short_name
);
561 RtlSetCurrentDirectory_U( &dirW
);
564 if (pTask
&& (pTask
->curdrive
& 0x80) &&
565 ((pTask
->curdrive
& ~0x80) == drive
))
567 WideCharToMultiByte(CP_ACP
, 0, full_name
.short_name
+ 2, -1,
568 pTask
->curdir
, sizeof(pTask
->curdir
), NULL
, NULL
);
569 DRIVE_LastTask
= GetCurrentTask();
575 /***********************************************************************
576 * DRIVE_GetCurrentDirectory
577 * Returns "X:\\path\\etc\\".
579 * Despite the API description, return required length including the
580 * terminating null when buffer too small. This is the real behaviour.
582 static UINT
DRIVE_GetCurrentDirectory( UINT buflen
, LPWSTR buf
)
585 LPCWSTR dos_cwd
= DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
586 static const WCHAR driveA_rootW
[] = {'A',':','\\',0};
588 ret
= strlenW(dos_cwd
) + 3; /* length of WHOLE current directory */
589 if (ret
>= buflen
) return ret
+ 1;
591 strcpyW( buf
, driveA_rootW
);
592 buf
[0] += DRIVE_GetCurrentDrive();
593 strcatW( buf
, dos_cwd
);
598 /***********************************************************************
601 * Build the environment array containing the drives' current directories.
602 * Resulting pointer must be freed with HeapFree.
604 WCHAR
*DRIVE_BuildEnv(void)
607 LPCWSTR cwd
[MAX_DOS_DRIVES
];
610 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++)
612 if ((cwd
[i
] = DRIVE_GetDosCwd(i
)) && cwd
[i
][0])
613 length
+= strlenW(cwd
[i
]) + 8;
615 if (!(env
= HeapAlloc( GetProcessHeap(), 0, (length
+1) * sizeof(WCHAR
) ))) return NULL
;
616 for (i
= 0, p
= env
; i
< MAX_DOS_DRIVES
; i
++)
618 if (cwd
[i
] && cwd
[i
][0])
620 *p
++ = '='; *p
++ = 'A' + i
; *p
++ = ':';
621 *p
++ = '='; *p
++ = 'A' + i
; *p
++ = ':'; *p
++ = '\\';
622 strcpyW( p
, cwd
[i
] );
631 /***********************************************************************
632 * GetCurrentDirectory (KERNEL.411)
634 UINT16 WINAPI
GetCurrentDirectory16( UINT16 buflen
, LPSTR buf
)
636 WCHAR cur_dirW
[MAX_PATH
];
638 DRIVE_GetCurrentDirectory(MAX_PATH
, cur_dirW
);
639 return (UINT16
)WideCharToMultiByte(CP_ACP
, 0, cur_dirW
, -1, buf
, buflen
, NULL
, NULL
);
643 /***********************************************************************
644 * GetCurrentDirectoryW (KERNEL32.@)
646 UINT WINAPI
GetCurrentDirectoryW( UINT buflen
, LPWSTR buf
)
649 WCHAR longname
[MAX_PATHNAME_LEN
];
650 WCHAR shortname
[MAX_PATHNAME_LEN
];
652 ret
= DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN
, shortname
);
653 if ( ret
> MAX_PATHNAME_LEN
) {
654 ERR_(file
)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret
);
657 GetLongPathNameW(shortname
, longname
, MAX_PATHNAME_LEN
);
658 ret
= strlenW( longname
) + 1;
659 if (ret
> buflen
) return ret
;
660 strcpyW(buf
, longname
);
664 /***********************************************************************
665 * GetCurrentDirectoryA (KERNEL32.@)
667 UINT WINAPI
GetCurrentDirectoryA( UINT buflen
, LPSTR buf
)
669 WCHAR bufferW
[MAX_PATH
];
672 retW
= GetCurrentDirectoryW(MAX_PATH
, bufferW
);
676 else if (retW
> MAX_PATH
)
678 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
683 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, NULL
, 0, NULL
, NULL
);
686 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buf
, buflen
, NULL
, NULL
);
687 ret
--; /* length without 0 */
694 /***********************************************************************
695 * SetCurrentDirectoryW (KERNEL32.@)
697 BOOL WINAPI
SetCurrentDirectoryW( LPCWSTR dir
)
699 int drive
, olddrive
= DRIVE_GetCurrentDrive();
703 SetLastError(ERROR_INVALID_PARAMETER
);
706 if (dir
[0] && (dir
[1]==':'))
708 drive
= toupperW( *dir
) - 'A';
714 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
715 sets pTask->curdir only if pTask->curdrive is drive */
716 if (!(DRIVE_SetCurrentDrive( drive
)))
719 /* FIXME: what about empty strings? Add a \\ ? */
720 if (!DRIVE_Chdir( drive
, dir
)) {
721 DRIVE_SetCurrentDrive(olddrive
);
728 /***********************************************************************
729 * SetCurrentDirectoryA (KERNEL32.@)
731 BOOL WINAPI
SetCurrentDirectoryA( LPCSTR dir
)
738 SetLastError(ERROR_INVALID_PARAMETER
);
742 if (RtlCreateUnicodeStringFromAsciiz(&dirW
, dir
))
744 ret
= SetCurrentDirectoryW(dirW
.Buffer
);
745 RtlFreeUnicodeString(&dirW
);
748 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);