Changed LoadLibraryEx32W16 to use OpenFile16 to look for the file
[wine.git] / files / drive.c
blob96a24f55ed3a1153ece800e900945f68ca972243
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 * DRIVE_GetFreeSpace
626 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
627 PULARGE_INTEGER available )
629 struct statvfs info;
631 if (!DRIVE_IsValid(drive))
633 SetLastError( ERROR_PATH_NOT_FOUND );
634 return 0;
637 if (statvfs( DOSDrives[drive].root, &info ) < 0)
639 FILE_SetDosError();
640 WARN("cannot do statvfs(%s)\n", DOSDrives[drive].root);
641 return 0;
643 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_frsize, info.f_blocks );
644 if (DOSDrives[drive].type == DRIVE_CDROM)
645 available->QuadPart = 0; /* ALWAYS 0, even if no real CD-ROM mounted there !! */
646 else
647 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_frsize, info.f_bavail );
649 return 1;
652 /***********************************************************************
653 * DRIVE_GetCurrentDirectory
654 * Returns "X:\\path\\etc\\".
656 * Despite the API description, return required length including the
657 * terminating null when buffer too small. This is the real behaviour.
659 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPWSTR buf )
661 UINT ret;
662 LPCWSTR dos_cwd = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
663 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
665 ret = strlenW(dos_cwd) + 3; /* length of WHOLE current directory */
666 if (ret >= buflen) return ret + 1;
668 strcpyW( buf, driveA_rootW );
669 buf[0] += DRIVE_GetCurrentDrive();
670 strcatW( buf, dos_cwd );
671 return ret;
675 /***********************************************************************
676 * DRIVE_BuildEnv
678 * Build the environment array containing the drives' current directories.
679 * Resulting pointer must be freed with HeapFree.
681 WCHAR *DRIVE_BuildEnv(void)
683 int i, length = 0;
684 LPCWSTR cwd[MAX_DOS_DRIVES];
685 WCHAR *env, *p;
687 for (i = 0; i < MAX_DOS_DRIVES; i++)
689 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0])
690 length += strlenW(cwd[i]) + 8;
692 if (!(env = HeapAlloc( GetProcessHeap(), 0, (length+1) * sizeof(WCHAR) ))) return NULL;
693 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
695 if (cwd[i] && cwd[i][0])
697 *p++ = '='; *p++ = 'A' + i; *p++ = ':';
698 *p++ = '='; *p++ = 'A' + i; *p++ = ':'; *p++ = '\\';
699 strcpyW( p, cwd[i] );
700 p += strlenW(p) + 1;
703 *p = 0;
704 return env;
708 /***********************************************************************
709 * GetDiskFreeSpaceW (KERNEL32.@)
711 * Fails if expression resulting from current drive's dir and "root"
712 * is not a root dir of the target drive.
714 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
715 * if the corresponding info is unneeded.
717 * FIXME: needs to support UNC names from Win95 OSR2 on.
719 * Behaviour under Win95a:
720 * CurrDir root result
721 * "E:\\TEST" "E:" FALSE
722 * "E:\\" "E:" TRUE
723 * "E:\\" "E" FALSE
724 * "E:\\" "\\" TRUE
725 * "E:\\TEST" "\\" TRUE
726 * "E:\\TEST" ":\\" FALSE
727 * "E:\\TEST" "E:\\" TRUE
728 * "E:\\TEST" "" FALSE
729 * "E:\\" "" FALSE (!)
730 * "E:\\" 0x0 TRUE
731 * "E:\\TEST" 0x0 TRUE (!)
732 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
733 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
735 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
736 LPDWORD sector_bytes, LPDWORD free_clusters,
737 LPDWORD total_clusters )
739 int drive, sec_size;
740 ULARGE_INTEGER size,available;
741 LPCWSTR path;
742 DWORD cluster_sec;
744 TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root), cluster_sectors, sector_bytes,
745 free_clusters, total_clusters);
747 if (!root || root[0] == '\\' || root[0] == '/')
748 drive = DRIVE_GetCurrentDrive();
749 else
750 if (root[0] && root[1] == ':') /* root contains drive tag */
752 drive = toupperW(root[0]) - 'A';
753 path = &root[2];
754 if (path[0] == '\0')
756 path = DRIVE_GetDosCwd(drive);
757 if (!path)
759 SetLastError(ERROR_PATH_NOT_FOUND);
760 return FALSE;
763 else
764 if (path[0] == '\\')
765 path++;
767 if (path[0]) /* oops, we are in a subdir */
769 SetLastError(ERROR_INVALID_NAME);
770 return FALSE;
773 else
775 if (!root[0])
776 SetLastError(ERROR_PATH_NOT_FOUND);
777 else
778 SetLastError(ERROR_INVALID_NAME);
779 return FALSE;
782 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
784 /* Cap the size and available at 2GB as per specs. */
785 if ((size.u.HighPart) ||(size.u.LowPart > 0x7fffffff))
787 size.u.HighPart = 0;
788 size.u.LowPart = 0x7fffffff;
790 if ((available.u.HighPart) ||(available.u.LowPart > 0x7fffffff))
792 available.u.HighPart =0;
793 available.u.LowPart = 0x7fffffff;
795 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
796 size.u.LowPart /= sec_size;
797 available.u.LowPart /= sec_size;
798 /* FIXME: probably have to adjust those variables too for CDFS */
799 cluster_sec = 1;
800 while (cluster_sec * 65536 < size.u.LowPart) cluster_sec *= 2;
802 if (cluster_sectors)
803 *cluster_sectors = cluster_sec;
804 if (sector_bytes)
805 *sector_bytes = sec_size;
806 if (free_clusters)
807 *free_clusters = available.u.LowPart / cluster_sec;
808 if (total_clusters)
809 *total_clusters = size.u.LowPart / cluster_sec;
810 return TRUE;
814 /***********************************************************************
815 * GetDiskFreeSpaceA (KERNEL32.@)
817 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
818 LPDWORD sector_bytes, LPDWORD free_clusters,
819 LPDWORD total_clusters )
821 UNICODE_STRING rootW;
822 BOOL ret = FALSE;
824 if (root)
826 if(!RtlCreateUnicodeStringFromAsciiz(&rootW, root))
828 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
829 return FALSE;
832 else
833 rootW.Buffer = NULL;
835 ret = GetDiskFreeSpaceW(rootW.Buffer, cluster_sectors, sector_bytes,
836 free_clusters, total_clusters );
837 RtlFreeUnicodeString(&rootW);
839 return ret;
843 /***********************************************************************
844 * GetDiskFreeSpaceExW (KERNEL32.@)
846 * This function is used to acquire the size of the available and
847 * total space on a logical volume.
849 * RETURNS
851 * Zero on failure, nonzero upon success. Use GetLastError to obtain
852 * detailed error information.
855 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root,
856 PULARGE_INTEGER avail,
857 PULARGE_INTEGER total,
858 PULARGE_INTEGER totalfree)
860 int drive;
861 ULARGE_INTEGER size,available;
863 if (!root) drive = DRIVE_GetCurrentDrive();
864 else
865 { /* C: always works for GetDiskFreeSpaceEx */
866 if ((root[1]) && ((root[1] != ':') || (root[2] && root[2] != '\\')))
868 FIXME("there are valid root names which are not supported yet\n");
869 /* ..like UNC names, for instance. */
871 WARN("invalid root '%s'\n", debugstr_w(root));
872 return FALSE;
874 drive = toupperW(root[0]) - 'A';
877 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
879 if (total)
881 total->u.HighPart = size.u.HighPart;
882 total->u.LowPart = size.u.LowPart;
885 if (totalfree)
887 totalfree->u.HighPart = available.u.HighPart;
888 totalfree->u.LowPart = available.u.LowPart;
891 if (avail)
893 if (FIXME_ON(dosfs))
895 /* On Windows2000, we need to check the disk quota
896 allocated for the user owning the calling process. We
897 don't want to be more obtrusive than necessary with the
898 FIXME messages, so don't print the FIXME unless Wine is
899 actually masquerading as Windows2000. */
901 RTL_OSVERSIONINFOEXW ovi;
902 ovi.dwOSVersionInfoSize = sizeof(ovi);
903 if (RtlGetVersion(&ovi))
905 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
906 FIXME("no per-user quota support yet\n");
910 /* Quick hack, should eventually be fixed to work 100% with
911 Windows2000 (see comment above). */
912 avail->u.HighPart = available.u.HighPart;
913 avail->u.LowPart = available.u.LowPart;
916 return TRUE;
919 /***********************************************************************
920 * GetDiskFreeSpaceExA (KERNEL32.@)
922 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root, PULARGE_INTEGER avail,
923 PULARGE_INTEGER total,
924 PULARGE_INTEGER totalfree)
926 UNICODE_STRING rootW;
927 BOOL ret;
929 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
930 else rootW.Buffer = NULL;
932 ret = GetDiskFreeSpaceExW( rootW.Buffer, avail, total, totalfree);
934 RtlFreeUnicodeString(&rootW);
935 return ret;
938 /***********************************************************************
939 * GetDriveTypeW (KERNEL32.@)
941 * Returns the type of the disk drive specified. If root is NULL the
942 * root of the current directory is used.
944 * RETURNS
946 * Type of drive (from Win32 SDK):
948 * DRIVE_UNKNOWN unable to find out anything about the drive
949 * DRIVE_NO_ROOT_DIR nonexistent root dir
950 * DRIVE_REMOVABLE the disk can be removed from the machine
951 * DRIVE_FIXED the disk can not be removed from the machine
952 * DRIVE_REMOTE network disk
953 * DRIVE_CDROM CDROM drive
954 * DRIVE_RAMDISK virtual disk in RAM
956 UINT WINAPI GetDriveTypeW(LPCWSTR root) /* [in] String describing drive */
958 int drive;
959 TRACE("(%s)\n", debugstr_w(root));
961 if (NULL == root) drive = DRIVE_GetCurrentDrive();
962 else
964 if ((root[1]) && (root[1] != ':'))
966 WARN("invalid root %s\n", debugstr_w(root));
967 return DRIVE_NO_ROOT_DIR;
969 drive = toupperW(root[0]) - 'A';
971 return DRIVE_GetType(drive);
975 /***********************************************************************
976 * GetDriveTypeA (KERNEL32.@)
978 UINT WINAPI GetDriveTypeA( LPCSTR root )
980 UNICODE_STRING rootW;
981 UINT ret = 0;
983 if (root)
985 if( !RtlCreateUnicodeStringFromAsciiz(&rootW, root))
987 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
988 return 0;
991 else
992 rootW.Buffer = NULL;
994 ret = GetDriveTypeW(rootW.Buffer);
996 RtlFreeUnicodeString(&rootW);
997 return ret;
1002 /***********************************************************************
1003 * GetCurrentDirectory (KERNEL.411)
1005 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1007 WCHAR cur_dirW[MAX_PATH];
1009 DRIVE_GetCurrentDirectory(MAX_PATH, cur_dirW);
1010 return (UINT16)WideCharToMultiByte(CP_ACP, 0, cur_dirW, -1, buf, buflen, NULL, NULL);
1014 /***********************************************************************
1015 * GetCurrentDirectoryW (KERNEL32.@)
1017 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1019 UINT ret;
1020 WCHAR longname[MAX_PATHNAME_LEN];
1021 WCHAR shortname[MAX_PATHNAME_LEN];
1023 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1024 if ( ret > MAX_PATHNAME_LEN ) {
1025 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1026 return ret;
1028 GetLongPathNameW(shortname, longname, MAX_PATHNAME_LEN);
1029 ret = strlenW( longname ) + 1;
1030 if (ret > buflen) return ret;
1031 strcpyW(buf, longname);
1032 return ret - 1;
1035 /***********************************************************************
1036 * GetCurrentDirectoryA (KERNEL32.@)
1038 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1040 WCHAR bufferW[MAX_PATH];
1041 DWORD ret, retW;
1043 retW = GetCurrentDirectoryW(MAX_PATH, bufferW);
1045 if (!retW)
1046 ret = 0;
1047 else if (retW > MAX_PATH)
1049 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1050 ret = 0;
1052 else
1054 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1055 if (buflen >= ret)
1057 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buf, buflen, NULL, NULL);
1058 ret--; /* length without 0 */
1061 return ret;
1065 /***********************************************************************
1066 * SetCurrentDirectoryW (KERNEL32.@)
1068 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dir )
1070 int drive, olddrive = DRIVE_GetCurrentDrive();
1072 if (!dir)
1074 SetLastError(ERROR_INVALID_PARAMETER);
1075 return FALSE;
1077 if (dir[0] && (dir[1]==':'))
1079 drive = toupperW( *dir ) - 'A';
1080 dir += 2;
1082 else
1083 drive = olddrive;
1085 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1086 sets pTask->curdir only if pTask->curdrive is drive */
1087 if (!(DRIVE_SetCurrentDrive( drive )))
1088 return FALSE;
1090 /* FIXME: what about empty strings? Add a \\ ? */
1091 if (!DRIVE_Chdir( drive, dir )) {
1092 DRIVE_SetCurrentDrive(olddrive);
1093 return FALSE;
1095 return TRUE;
1099 /***********************************************************************
1100 * SetCurrentDirectoryA (KERNEL32.@)
1102 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1104 UNICODE_STRING dirW;
1105 BOOL ret = FALSE;
1107 if (!dir)
1109 SetLastError(ERROR_INVALID_PARAMETER);
1110 return FALSE;
1113 if (RtlCreateUnicodeStringFromAsciiz(&dirW, dir))
1115 ret = SetCurrentDirectoryW(dirW.Buffer);
1116 RtlFreeUnicodeString(&dirW);
1118 else
1119 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1120 return ret;
1124 /***********************************************************************
1125 * GetLogicalDriveStringsA (KERNEL32.@)
1127 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1129 int drive, count;
1131 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1132 if (DRIVE_IsValid(drive)) count++;
1133 if ((count * 4) + 1 <= len)
1135 LPSTR p = buffer;
1136 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1137 if (DRIVE_IsValid(drive))
1139 *p++ = 'a' + drive;
1140 *p++ = ':';
1141 *p++ = '\\';
1142 *p++ = '\0';
1144 *p = '\0';
1145 return count * 4;
1147 else
1148 return (count * 4) + 1; /* account for terminating null */
1149 /* The API tells about these different return values */
1153 /***********************************************************************
1154 * GetLogicalDriveStringsW (KERNEL32.@)
1156 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1158 int drive, count;
1160 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1161 if (DRIVE_IsValid(drive)) count++;
1162 if (count * 4 * sizeof(WCHAR) <= len)
1164 LPWSTR p = buffer;
1165 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1166 if (DRIVE_IsValid(drive))
1168 *p++ = (WCHAR)('a' + drive);
1169 *p++ = (WCHAR)':';
1170 *p++ = (WCHAR)'\\';
1171 *p++ = (WCHAR)'\0';
1173 *p = (WCHAR)'\0';
1175 return count * 4 * sizeof(WCHAR);
1179 /***********************************************************************
1180 * GetLogicalDrives (KERNEL32.@)
1182 DWORD WINAPI GetLogicalDrives(void)
1184 DWORD ret = 0;
1185 int drive;
1187 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1189 if ( (DRIVE_IsValid(drive)) ||
1190 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1191 ret |= (1 << drive);
1193 return ret;