- more FIXME -> TRACE
[wine.git] / files / drive.c
blobf532dd71cfb7c782f3b7233e25fec433b3116a9a
1 /*
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
26 #include "config.h"
27 #include "wine/port.h"
29 #include <assert.h>
30 #include <ctype.h>
31 #include <string.h>
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <errno.h>
39 #ifdef HAVE_UNISTD_H
40 # include <unistd.h>
41 #endif
43 #define NONAMELESSUNION
44 #define NONAMELESSSTRUCT
45 #include "ntstatus.h"
46 #include "windef.h"
47 #include "winbase.h"
48 #include "winreg.h"
49 #include "winternl.h"
50 #include "wine/winbase16.h" /* for GetCurrentTask */
51 #include "winerror.h"
52 #include "winioctl.h"
53 #include "ntddstor.h"
54 #include "ntddcdrm.h"
55 #include "file.h"
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);
64 typedef struct
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 */
73 } DOSDRIVE;
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 );
100 return p;
103 extern void CDROM_InitRegistry(int dev);
105 /***********************************************************************
106 * DRIVE_GetDriveType
108 static inline UINT DRIVE_GetDriveType( INT drive, LPCWSTR value )
110 int i;
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) );
118 return DRIVE_FIXED;
122 /***********************************************************************
123 * DRIVE_Init
125 int DRIVE_Init(void)
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;
135 WCHAR *p;
136 DOSDRIVE *drive;
137 HKEY hkey;
138 DWORD dummy;
139 OBJECT_ATTRIBUTES attr;
140 UNICODE_STRING nameW;
141 char *root;
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;
151 attr.Attributes = 0;
152 attr.SecurityDescriptor = NULL;
153 attr.SecurityQualityOfService = NULL;
155 /* get the root of the drives from symlinks */
157 root = NULL;
158 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
160 if (!root)
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);
172 continue;
174 if (!S_ISDIR(drive_stat_buffer.st_mode))
176 MESSAGE("%s is not a directory, ignoring drive %c:\n", root, 'a' + i );
177 continue;
179 drive->root = root;
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;
186 root = NULL;
187 symlink_count++;
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 */
200 if (!symlink_count)
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';
211 if (path[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);
217 else
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 );
234 drive->root = NULL;
235 goto next;
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 );
242 drive->root = NULL;
243 goto next;
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;
255 if (drive->root)
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 );
265 /* Get the device */
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)
276 int cd_fd;
277 if ((cd_fd = open(drive->device, O_RDONLY|O_NONBLOCK)) != -1)
279 CDROM_InitRegistry(cd_fd);
280 close(cd_fd);
285 /* Make the first hard disk the current drive */
286 if ((DRIVE_CurDrive == -1) && (drive->type == DRIVE_FIXED))
287 DRIVE_CurDrive = i;
289 count++;
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 );
295 next:
296 NtClose( hkey );
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;
308 DRIVE_CurDrive = 2;
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++)
316 if (drive->root)
318 DRIVE_CurDrive = i;
319 break;
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;
328 /* sanity check */
329 if (toupperW(path[0]) != drive_env[1] || path[1] != ':') continue;
330 DRIVE_Chdir( i, path + 2 );
332 return 1;
336 /***********************************************************************
337 * DRIVE_IsValid
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 );
366 return 0;
368 TRACE("%c:\n", 'A' + drive );
369 DRIVE_CurDrive = drive;
370 if (pTask) pTask->curdrive = drive | 0x80;
371 return 1;
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];
394 char *p;
395 struct stat st;
397 strcpy( buffer, *path );
398 for (p = buffer; *p; p++) if (*p == '\\') *p = '/';
399 len = p - buffer;
401 /* strip off trailing slashes */
402 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
404 for (;;)
406 /* Find the drive */
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);
419 *path += len;
420 if (!**path) *path = "\\";
421 return drive;
425 if (len <= 1) return -1; /* reached root */
427 level = 0;
428 while (level < 1)
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;
436 buffer[len] = 0;
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];
453 WCHAR *p;
454 struct stat st;
456 strcpyW( buffer, *path );
457 for (p = buffer; *p; p++) if (*p == '\\') *p = '/';
458 len = p - buffer;
460 /* strip off trailing slashes */
461 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
463 for (;;)
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 ))
470 /* Find the drive */
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));
483 *path += len;
484 if (!**path) *path = rootW;
485 return drive;
489 if (len <= 1) return -1; /* reached root */
491 level = 0;
492 while (level < 1)
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;
503 buffer[len] = 0;
504 /* strip off trailing slashes */
505 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
511 /***********************************************************************
512 * DRIVE_GetRoot
514 const char * DRIVE_GetRoot( int drive )
516 if (!DRIVE_IsValid( drive )) return NULL;
517 return DOSDrives[drive].root;
521 /***********************************************************************
522 * DRIVE_GetDosCwd
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 /***********************************************************************
546 * DRIVE_GetUnixCwd
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 /***********************************************************************
570 * DRIVE_GetDevice
572 const char * DRIVE_GetDevice( int drive )
574 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
577 /***********************************************************************
578 * DRIVE_GetType
580 static UINT DRIVE_GetType( int drive )
582 if (!DRIVE_IsValid( drive )) return DRIVE_NO_ROOT_DIR;
583 return DOSDrives[drive].type;
587 /***********************************************************************
588 * DRIVE_Chdir
590 int DRIVE_Chdir( int drive, LPCWSTR path )
592 DOS_FULL_NAME full_name;
593 WCHAR buffer[MAX_PATHNAME_LEN];
594 LPSTR unix_cwd;
595 BY_HANDLE_FILE_INFORMATION info;
596 TDB *pTask = GlobalLock16(GetCurrentTask());
598 buffer[0] = 'A' + drive;
599 buffer[1] = ':';
600 buffer[2] = 0;
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 );
610 return 0;
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)
626 UNICODE_STRING dirW;
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();
639 return 1;
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 )
652 UINT ret;
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 );
662 return ret;
666 /***********************************************************************
667 * DRIVE_BuildEnv
669 * Build the environment array containing the drives' current directories.
670 * Resulting pointer must be freed with HeapFree.
672 WCHAR *DRIVE_BuildEnv(void)
674 int i, length = 0;
675 LPCWSTR cwd[MAX_DOS_DRIVES];
676 WCHAR *env, *p;
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] );
691 p += strlenW(p) + 1;
694 *p = 0;
695 return env;
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.
705 * RETURNS
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 */
719 int drive;
720 TRACE("(%s)\n", debugstr_w(root));
722 if (NULL == root) drive = DRIVE_GetCurrentDrive();
723 else
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;
742 UINT ret = 0;
744 if (root)
746 if( !RtlCreateUnicodeStringFromAsciiz(&rootW, root))
748 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
749 return 0;
752 else
753 rootW.Buffer = NULL;
755 ret = GetDriveTypeW(rootW.Buffer);
757 RtlFreeUnicodeString(&rootW);
758 return ret;
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 )
780 UINT ret;
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 );
787 return ret;
789 GetLongPathNameW(shortname, longname, MAX_PATHNAME_LEN);
790 ret = strlenW( longname ) + 1;
791 if (ret > buflen) return ret;
792 strcpyW(buf, longname);
793 return ret - 1;
796 /***********************************************************************
797 * GetCurrentDirectoryA (KERNEL32.@)
799 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
801 WCHAR bufferW[MAX_PATH];
802 DWORD ret, retW;
804 retW = GetCurrentDirectoryW(MAX_PATH, bufferW);
806 if (!retW)
807 ret = 0;
808 else if (retW > MAX_PATH)
810 SetLastError(ERROR_FILENAME_EXCED_RANGE);
811 ret = 0;
813 else
815 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
816 if (buflen >= ret)
818 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buf, buflen, NULL, NULL);
819 ret--; /* length without 0 */
822 return ret;
826 /***********************************************************************
827 * SetCurrentDirectoryW (KERNEL32.@)
829 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dir )
831 int drive, olddrive = DRIVE_GetCurrentDrive();
833 if (!dir)
835 SetLastError(ERROR_INVALID_PARAMETER);
836 return FALSE;
838 if (dir[0] && (dir[1]==':'))
840 drive = toupperW( *dir ) - 'A';
841 dir += 2;
843 else
844 drive = olddrive;
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 )))
849 return FALSE;
851 /* FIXME: what about empty strings? Add a \\ ? */
852 if (!DRIVE_Chdir( drive, dir )) {
853 DRIVE_SetCurrentDrive(olddrive);
854 return FALSE;
856 return TRUE;
860 /***********************************************************************
861 * SetCurrentDirectoryA (KERNEL32.@)
863 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
865 UNICODE_STRING dirW;
866 BOOL ret = FALSE;
868 if (!dir)
870 SetLastError(ERROR_INVALID_PARAMETER);
871 return FALSE;
874 if (RtlCreateUnicodeStringFromAsciiz(&dirW, dir))
876 ret = SetCurrentDirectoryW(dirW.Buffer);
877 RtlFreeUnicodeString(&dirW);
879 else
880 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
881 return ret;
885 /***********************************************************************
886 * GetLogicalDriveStringsA (KERNEL32.@)
888 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
890 int drive, count;
892 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
893 if (DRIVE_IsValid(drive)) count++;
894 if ((count * 4) + 1 <= len)
896 LPSTR p = buffer;
897 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
898 if (DRIVE_IsValid(drive))
900 *p++ = 'a' + drive;
901 *p++ = ':';
902 *p++ = '\\';
903 *p++ = '\0';
905 *p = '\0';
906 return count * 4;
908 else
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 )
919 int drive, count;
921 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
922 if (DRIVE_IsValid(drive)) count++;
923 if (count * 4 * sizeof(WCHAR) <= len)
925 LPWSTR p = buffer;
926 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
927 if (DRIVE_IsValid(drive))
929 *p++ = (WCHAR)('a' + drive);
930 *p++ = (WCHAR)':';
931 *p++ = (WCHAR)'\\';
932 *p++ = (WCHAR)'\0';
934 *p = (WCHAR)'\0';
936 return count * 4 * sizeof(WCHAR);
940 /***********************************************************************
941 * GetLogicalDrives (KERNEL32.@)
943 DWORD WINAPI GetLogicalDrives(void)
945 DWORD ret = 0;
946 int drive;
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 */
952 ret |= (1 << drive);
954 return ret;