Rewrote the collapsing of . and .. in RtlGetFullPathName_U for better
[wine.git] / files / drive.c
blobce43e43b189f36a151e38e78fb6ed691e2fc2648
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
42 #ifdef HAVE_SYS_STATVFS_H
43 # include <sys/statvfs.h>
44 #endif
46 #define NONAMELESSUNION
47 #define NONAMELESSSTRUCT
48 #include "ntstatus.h"
49 #include "windef.h"
50 #include "winbase.h"
51 #include "winreg.h"
52 #include "winternl.h"
53 #include "wine/winbase16.h" /* for GetCurrentTask */
54 #include "winerror.h"
55 #include "winioctl.h"
56 #include "ntddstor.h"
57 #include "ntddcdrm.h"
58 #include "file.h"
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);
67 typedef struct
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 */
77 } DOSDRIVE;
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 );
104 return p;
107 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
109 extern void CDROM_InitRegistry(int dev);
111 /***********************************************************************
112 * DRIVE_GetDriveType
114 static inline UINT DRIVE_GetDriveType( INT drive, LPCWSTR value )
116 int i;
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) );
124 return DRIVE_FIXED;
128 /***********************************************************************
129 * DRIVE_Init
131 int DRIVE_Init(void)
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;
141 WCHAR *p;
142 DOSDRIVE *drive;
143 HKEY hkey;
144 DWORD dummy;
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;
156 attr.Attributes = 0;
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';
176 if (path[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);
182 else
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 );
199 drive->root = NULL;
200 goto next;
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 );
207 drive->root = NULL;
208 goto next;
211 drive->dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(drive->dos_cwd[0]));
212 drive->unix_cwd = heap_strdup( "" );
213 drive->device = NULL;
214 drive->flags = 0;
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;
227 /* Get the device */
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)
238 int cd_fd;
239 if ((cd_fd = open(drive->device, O_RDONLY|O_NONBLOCK)) != -1)
241 CDROM_InitRegistry(cd_fd);
242 close(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))
257 DRIVE_CurDrive = i;
259 count++;
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 );
265 next:
266 NtClose( hkey );
269 if (!count)
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;
279 DRIVE_CurDrive = 2;
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++)
287 if (drive->root)
289 DRIVE_CurDrive = i;
290 break;
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;
299 /* sanity check */
300 if (toupperW(path[0]) != drive_env[1] || path[1] != ':') continue;
301 DRIVE_Chdir( i, path + 2 );
303 return 1;
307 /***********************************************************************
308 * DRIVE_IsValid
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 );
337 return 0;
339 TRACE("%c:\n", 'A' + drive );
340 DRIVE_CurDrive = drive;
341 if (pTask) pTask->curdrive = drive | 0x80;
342 return 1;
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];
365 char *p;
366 struct stat st;
368 strcpy( buffer, *path );
369 for (p = buffer; *p; p++) if (*p == '\\') *p = '/';
370 len = p - buffer;
372 /* strip off trailing slashes */
373 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
375 for (;;)
377 /* Find the drive */
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);
390 *path += len;
391 if (!**path) *path = "\\";
392 return drive;
396 if (len <= 1) return -1; /* reached root */
398 level = 0;
399 while (level < 1)
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;
407 buffer[len] = 0;
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];
424 WCHAR *p;
425 struct stat st;
427 strcpyW( buffer, *path );
428 for (p = buffer; *p; p++) if (*p == '\\') *p = '/';
429 len = p - buffer;
431 /* strip off trailing slashes */
432 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
434 for (;;)
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 ))
441 /* Find the drive */
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));
454 *path += len;
455 if (!**path) *path = rootW;
456 return drive;
460 if (len <= 1) return -1; /* reached root */
462 level = 0;
463 while (level < 1)
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;
474 buffer[len] = 0;
475 /* strip off trailing slashes */
476 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
482 /***********************************************************************
483 * DRIVE_GetRoot
485 const char * DRIVE_GetRoot( int drive )
487 if (!DRIVE_IsValid( drive )) return NULL;
488 return DOSDrives[drive].root;
492 /***********************************************************************
493 * DRIVE_GetDosCwd
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 /***********************************************************************
517 * DRIVE_GetUnixCwd
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 /***********************************************************************
541 * DRIVE_GetDevice
543 const char * DRIVE_GetDevice( int drive )
545 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
548 /***********************************************************************
549 * DRIVE_GetType
551 static UINT DRIVE_GetType( int drive )
553 if (!DRIVE_IsValid( drive )) return DRIVE_NO_ROOT_DIR;
554 return DOSDrives[drive].type;
558 /***********************************************************************
559 * DRIVE_GetFlags
561 UINT DRIVE_GetFlags( int drive )
563 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
564 return DOSDrives[drive].flags;
567 /***********************************************************************
568 * DRIVE_Chdir
570 int DRIVE_Chdir( int drive, LPCWSTR path )
572 DOS_FULL_NAME full_name;
573 WCHAR buffer[MAX_PATHNAME_LEN];
574 LPSTR unix_cwd;
575 BY_HANDLE_FILE_INFORMATION info;
576 TDB *pTask = GlobalLock16(GetCurrentTask());
578 buffer[0] = 'A' + drive;
579 buffer[1] = ':';
580 buffer[2] = 0;
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 );
590 return 0;
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)
606 UNICODE_STRING dirW;
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();
619 return 1;
623 /***********************************************************************
624 * DefineDosDeviceA (KERNEL32.@)
626 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath)
628 UNICODE_STRING d, t;
629 BOOL ret;
631 if (!RtlCreateUnicodeStringFromAsciiz(&d, devname))
633 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
634 return FALSE;
636 if (!RtlCreateUnicodeStringFromAsciiz(&t, targetpath))
638 RtlFreeUnicodeString(&d);
639 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
640 return FALSE;
642 ret = DefineDosDeviceW(flags, d.Buffer, t.Buffer);
643 RtlFreeUnicodeString(&d);
644 RtlFreeUnicodeString(&t);
645 return ret;
649 /***********************************************************************
650 * DefineDosDeviceA (KERNEL32.@)
652 BOOL WINAPI DefineDosDeviceW(DWORD flags,LPCWSTR devname,LPCWSTR targetpath)
654 DOSDRIVE *old, *new;
656 /* this is a temporary hack for int21 support. better implementation has to be done */
657 if (flags != DDD_RAW_TARGET_PATH ||
658 !(toupperW(devname[0]) >= 'A' && toupperW(devname[0]) <= 'Z') ||
659 devname[1] != ':' || devname[2] != 0 ||
660 !(toupperW(targetpath[0]) >= 'A' && toupperW(targetpath[0]) <= 'Z') ||
661 targetpath[1] != ':' || targetpath[2] != '\\' || targetpath[3] != 0)
663 FIXME("(0x%08lx,%s,%s),stub!\n", flags, debugstr_w(devname), debugstr_w(targetpath));
664 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
665 return FALSE;
668 old = DOSDrives + devname[0] - 'A';
669 new = DOSDrives + targetpath[0] - 'A';
671 if (!old->root)
673 SetLastError( ERROR_INVALID_DRIVE );
674 return 0;
677 if ( new->root )
679 TRACE("Can't map drive %c: to already existing drive %c:\n",
680 devname[0], targetpath[0] );
681 /* it is already mapped there, so return success */
682 if (!strcmp(old->root,new->root))
683 return 1;
684 return 0;
687 new->root = heap_strdup( old->root );
688 new->dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(old->dos_cwd) + 1) * sizeof(WCHAR));
689 strcpyW(new->dos_cwd, old->dos_cwd);
690 new->unix_cwd = heap_strdup( old->unix_cwd );
691 new->device = heap_strdup( old->device );
692 new->type = old->type;
693 new->flags = old->flags;
694 new->dev = old->dev;
695 new->ino = old->ino;
697 TRACE("Drive %c: is now equal to drive %c:\n",
698 targetpath[0], devname[0] );
700 return 1;
704 /***********************************************************************
705 * DRIVE_GetFreeSpace
707 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
708 PULARGE_INTEGER available )
710 struct statvfs info;
712 if (!DRIVE_IsValid(drive))
714 SetLastError( ERROR_PATH_NOT_FOUND );
715 return 0;
718 if (statvfs( DOSDrives[drive].root, &info ) < 0)
720 FILE_SetDosError();
721 WARN("cannot do statvfs(%s)\n", DOSDrives[drive].root);
722 return 0;
724 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_frsize, info.f_blocks );
725 if (DOSDrives[drive].type == DRIVE_CDROM)
726 available->QuadPart = 0; /* ALWAYS 0, even if no real CD-ROM mounted there !! */
727 else
728 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_frsize, info.f_bavail );
730 return 1;
733 /***********************************************************************
734 * DRIVE_GetCurrentDirectory
735 * Returns "X:\\path\\etc\\".
737 * Despite the API description, return required length including the
738 * terminating null when buffer too small. This is the real behaviour.
740 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPWSTR buf )
742 UINT ret;
743 LPCWSTR dos_cwd = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
744 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
746 ret = strlenW(dos_cwd) + 3; /* length of WHOLE current directory */
747 if (ret >= buflen) return ret + 1;
749 strcpyW( buf, driveA_rootW );
750 buf[0] += DRIVE_GetCurrentDrive();
751 strcatW( buf, dos_cwd );
752 return ret;
756 /***********************************************************************
757 * DRIVE_BuildEnv
759 * Build the environment array containing the drives' current directories.
760 * Resulting pointer must be freed with HeapFree.
762 WCHAR *DRIVE_BuildEnv(void)
764 int i, length = 0;
765 LPCWSTR cwd[MAX_DOS_DRIVES];
766 WCHAR *env, *p;
768 for (i = 0; i < MAX_DOS_DRIVES; i++)
770 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0])
771 length += strlenW(cwd[i]) + 8;
773 if (!(env = HeapAlloc( GetProcessHeap(), 0, (length+1) * sizeof(WCHAR) ))) return NULL;
774 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
776 if (cwd[i] && cwd[i][0])
778 *p++ = '='; *p++ = 'A' + i; *p++ = ':';
779 *p++ = '='; *p++ = 'A' + i; *p++ = ':'; *p++ = '\\';
780 strcpyW( p, cwd[i] );
781 p += strlenW(p) + 1;
784 *p = 0;
785 return env;
789 /***********************************************************************
790 * GetDiskFreeSpaceW (KERNEL32.@)
792 * Fails if expression resulting from current drive's dir and "root"
793 * is not a root dir of the target drive.
795 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
796 * if the corresponding info is unneeded.
798 * FIXME: needs to support UNC names from Win95 OSR2 on.
800 * Behaviour under Win95a:
801 * CurrDir root result
802 * "E:\\TEST" "E:" FALSE
803 * "E:\\" "E:" TRUE
804 * "E:\\" "E" FALSE
805 * "E:\\" "\\" TRUE
806 * "E:\\TEST" "\\" TRUE
807 * "E:\\TEST" ":\\" FALSE
808 * "E:\\TEST" "E:\\" TRUE
809 * "E:\\TEST" "" FALSE
810 * "E:\\" "" FALSE (!)
811 * "E:\\" 0x0 TRUE
812 * "E:\\TEST" 0x0 TRUE (!)
813 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
814 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
816 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
817 LPDWORD sector_bytes, LPDWORD free_clusters,
818 LPDWORD total_clusters )
820 int drive, sec_size;
821 ULARGE_INTEGER size,available;
822 LPCWSTR path;
823 DWORD cluster_sec;
825 TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root), cluster_sectors, sector_bytes,
826 free_clusters, total_clusters);
828 if (!root || root[0] == '\\' || root[0] == '/')
829 drive = DRIVE_GetCurrentDrive();
830 else
831 if (root[0] && root[1] == ':') /* root contains drive tag */
833 drive = toupperW(root[0]) - 'A';
834 path = &root[2];
835 if (path[0] == '\0')
837 path = DRIVE_GetDosCwd(drive);
838 if (!path)
840 SetLastError(ERROR_PATH_NOT_FOUND);
841 return FALSE;
844 else
845 if (path[0] == '\\')
846 path++;
848 if (path[0]) /* oops, we are in a subdir */
850 SetLastError(ERROR_INVALID_NAME);
851 return FALSE;
854 else
856 if (!root[0])
857 SetLastError(ERROR_PATH_NOT_FOUND);
858 else
859 SetLastError(ERROR_INVALID_NAME);
860 return FALSE;
863 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
865 /* Cap the size and available at 2GB as per specs. */
866 if ((size.u.HighPart) ||(size.u.LowPart > 0x7fffffff))
868 size.u.HighPart = 0;
869 size.u.LowPart = 0x7fffffff;
871 if ((available.u.HighPart) ||(available.u.LowPart > 0x7fffffff))
873 available.u.HighPart =0;
874 available.u.LowPart = 0x7fffffff;
876 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
877 size.u.LowPart /= sec_size;
878 available.u.LowPart /= sec_size;
879 /* FIXME: probably have to adjust those variables too for CDFS */
880 cluster_sec = 1;
881 while (cluster_sec * 65536 < size.u.LowPart) cluster_sec *= 2;
883 if (cluster_sectors)
884 *cluster_sectors = cluster_sec;
885 if (sector_bytes)
886 *sector_bytes = sec_size;
887 if (free_clusters)
888 *free_clusters = available.u.LowPart / cluster_sec;
889 if (total_clusters)
890 *total_clusters = size.u.LowPart / cluster_sec;
891 return TRUE;
895 /***********************************************************************
896 * GetDiskFreeSpaceA (KERNEL32.@)
898 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
899 LPDWORD sector_bytes, LPDWORD free_clusters,
900 LPDWORD total_clusters )
902 UNICODE_STRING rootW;
903 BOOL ret = FALSE;
905 if (root)
907 if(!RtlCreateUnicodeStringFromAsciiz(&rootW, root))
909 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
910 return FALSE;
913 else
914 rootW.Buffer = NULL;
916 ret = GetDiskFreeSpaceW(rootW.Buffer, cluster_sectors, sector_bytes,
917 free_clusters, total_clusters );
918 RtlFreeUnicodeString(&rootW);
920 return ret;
924 /***********************************************************************
925 * GetDiskFreeSpaceExW (KERNEL32.@)
927 * This function is used to acquire the size of the available and
928 * total space on a logical volume.
930 * RETURNS
932 * Zero on failure, nonzero upon success. Use GetLastError to obtain
933 * detailed error information.
936 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root,
937 PULARGE_INTEGER avail,
938 PULARGE_INTEGER total,
939 PULARGE_INTEGER totalfree)
941 int drive;
942 ULARGE_INTEGER size,available;
944 if (!root) drive = DRIVE_GetCurrentDrive();
945 else
946 { /* C: always works for GetDiskFreeSpaceEx */
947 if ((root[1]) && ((root[1] != ':') || (root[2] && root[2] != '\\')))
949 FIXME("there are valid root names which are not supported yet\n");
950 /* ..like UNC names, for instance. */
952 WARN("invalid root '%s'\n", debugstr_w(root));
953 return FALSE;
955 drive = toupperW(root[0]) - 'A';
958 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
960 if (total)
962 total->u.HighPart = size.u.HighPart;
963 total->u.LowPart = size.u.LowPart;
966 if (totalfree)
968 totalfree->u.HighPart = available.u.HighPart;
969 totalfree->u.LowPart = available.u.LowPart;
972 if (avail)
974 if (FIXME_ON(dosfs))
976 /* On Windows2000, we need to check the disk quota
977 allocated for the user owning the calling process. We
978 don't want to be more obtrusive than necessary with the
979 FIXME messages, so don't print the FIXME unless Wine is
980 actually masquerading as Windows2000. */
982 RTL_OSVERSIONINFOEXW ovi;
983 ovi.dwOSVersionInfoSize = sizeof(ovi);
984 if (RtlGetVersion(&ovi))
986 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
987 FIXME("no per-user quota support yet\n");
991 /* Quick hack, should eventually be fixed to work 100% with
992 Windows2000 (see comment above). */
993 avail->u.HighPart = available.u.HighPart;
994 avail->u.LowPart = available.u.LowPart;
997 return TRUE;
1000 /***********************************************************************
1001 * GetDiskFreeSpaceExA (KERNEL32.@)
1003 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root, PULARGE_INTEGER avail,
1004 PULARGE_INTEGER total,
1005 PULARGE_INTEGER totalfree)
1007 UNICODE_STRING rootW;
1008 BOOL ret;
1010 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
1011 else rootW.Buffer = NULL;
1013 ret = GetDiskFreeSpaceExW( rootW.Buffer, avail, total, totalfree);
1015 RtlFreeUnicodeString(&rootW);
1016 return ret;
1019 /***********************************************************************
1020 * GetDriveTypeW (KERNEL32.@)
1022 * Returns the type of the disk drive specified. If root is NULL the
1023 * root of the current directory is used.
1025 * RETURNS
1027 * Type of drive (from Win32 SDK):
1029 * DRIVE_UNKNOWN unable to find out anything about the drive
1030 * DRIVE_NO_ROOT_DIR nonexistent root dir
1031 * DRIVE_REMOVABLE the disk can be removed from the machine
1032 * DRIVE_FIXED the disk can not be removed from the machine
1033 * DRIVE_REMOTE network disk
1034 * DRIVE_CDROM CDROM drive
1035 * DRIVE_RAMDISK virtual disk in RAM
1037 UINT WINAPI GetDriveTypeW(LPCWSTR root) /* [in] String describing drive */
1039 int drive;
1040 TRACE("(%s)\n", debugstr_w(root));
1042 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1043 else
1045 if ((root[1]) && (root[1] != ':'))
1047 WARN("invalid root %s\n", debugstr_w(root));
1048 return DRIVE_NO_ROOT_DIR;
1050 drive = toupperW(root[0]) - 'A';
1052 return DRIVE_GetType(drive);
1056 /***********************************************************************
1057 * GetDriveTypeA (KERNEL32.@)
1059 UINT WINAPI GetDriveTypeA( LPCSTR root )
1061 UNICODE_STRING rootW;
1062 UINT ret = 0;
1064 if (root)
1066 if( !RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1068 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1069 return 0;
1072 else
1073 rootW.Buffer = NULL;
1075 ret = GetDriveTypeW(rootW.Buffer);
1077 RtlFreeUnicodeString(&rootW);
1078 return ret;
1083 /***********************************************************************
1084 * GetCurrentDirectory (KERNEL.411)
1086 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1088 WCHAR cur_dirW[MAX_PATH];
1090 DRIVE_GetCurrentDirectory(MAX_PATH, cur_dirW);
1091 return (UINT16)WideCharToMultiByte(CP_ACP, 0, cur_dirW, -1, buf, buflen, NULL, NULL);
1095 /***********************************************************************
1096 * GetCurrentDirectoryW (KERNEL32.@)
1098 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1100 UINT ret;
1101 WCHAR longname[MAX_PATHNAME_LEN];
1102 WCHAR shortname[MAX_PATHNAME_LEN];
1104 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1105 if ( ret > MAX_PATHNAME_LEN ) {
1106 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1107 return ret;
1109 GetLongPathNameW(shortname, longname, MAX_PATHNAME_LEN);
1110 ret = strlenW( longname ) + 1;
1111 if (ret > buflen) return ret;
1112 strcpyW(buf, longname);
1113 return ret - 1;
1116 /***********************************************************************
1117 * GetCurrentDirectoryA (KERNEL32.@)
1119 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1121 WCHAR bufferW[MAX_PATH];
1122 DWORD ret, retW;
1124 retW = GetCurrentDirectoryW(MAX_PATH, bufferW);
1126 if (!retW)
1127 ret = 0;
1128 else if (retW > MAX_PATH)
1130 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1131 ret = 0;
1133 else
1135 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1136 if (buflen >= ret)
1138 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buf, buflen, NULL, NULL);
1139 ret--; /* length without 0 */
1142 return ret;
1146 /***********************************************************************
1147 * SetCurrentDirectoryW (KERNEL32.@)
1149 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dir )
1151 int drive, olddrive = DRIVE_GetCurrentDrive();
1153 if (!dir)
1155 SetLastError(ERROR_INVALID_PARAMETER);
1156 return FALSE;
1158 if (dir[0] && (dir[1]==':'))
1160 drive = toupperW( *dir ) - 'A';
1161 dir += 2;
1163 else
1164 drive = olddrive;
1166 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1167 sets pTask->curdir only if pTask->curdrive is drive */
1168 if (!(DRIVE_SetCurrentDrive( drive )))
1169 return FALSE;
1171 /* FIXME: what about empty strings? Add a \\ ? */
1172 if (!DRIVE_Chdir( drive, dir )) {
1173 DRIVE_SetCurrentDrive(olddrive);
1174 return FALSE;
1176 return TRUE;
1180 /***********************************************************************
1181 * SetCurrentDirectoryA (KERNEL32.@)
1183 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1185 UNICODE_STRING dirW;
1186 BOOL ret = FALSE;
1188 if (!dir)
1190 SetLastError(ERROR_INVALID_PARAMETER);
1191 return FALSE;
1194 if (RtlCreateUnicodeStringFromAsciiz(&dirW, dir))
1196 ret = SetCurrentDirectoryW(dirW.Buffer);
1197 RtlFreeUnicodeString(&dirW);
1199 else
1200 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1201 return ret;
1205 /***********************************************************************
1206 * GetLogicalDriveStringsA (KERNEL32.@)
1208 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1210 int drive, count;
1212 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1213 if (DRIVE_IsValid(drive)) count++;
1214 if ((count * 4) + 1 <= len)
1216 LPSTR p = buffer;
1217 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1218 if (DRIVE_IsValid(drive))
1220 *p++ = 'a' + drive;
1221 *p++ = ':';
1222 *p++ = '\\';
1223 *p++ = '\0';
1225 *p = '\0';
1226 return count * 4;
1228 else
1229 return (count * 4) + 1; /* account for terminating null */
1230 /* The API tells about these different return values */
1234 /***********************************************************************
1235 * GetLogicalDriveStringsW (KERNEL32.@)
1237 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1239 int drive, count;
1241 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1242 if (DRIVE_IsValid(drive)) count++;
1243 if (count * 4 * sizeof(WCHAR) <= len)
1245 LPWSTR p = buffer;
1246 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1247 if (DRIVE_IsValid(drive))
1249 *p++ = (WCHAR)('a' + drive);
1250 *p++ = (WCHAR)':';
1251 *p++ = (WCHAR)'\\';
1252 *p++ = (WCHAR)'\0';
1254 *p = (WCHAR)'\0';
1256 return count * 4 * sizeof(WCHAR);
1260 /***********************************************************************
1261 * GetLogicalDrives (KERNEL32.@)
1263 DWORD WINAPI GetLogicalDrives(void)
1265 DWORD ret = 0;
1266 int drive;
1268 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1270 if ( (DRIVE_IsValid(drive)) ||
1271 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1272 ret |= (1 << drive);
1274 return ret;