2 * DOS drives handling functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
7 * Label & serial number read support.
8 * (c) 1999 Petr Tomasek <tomasek@etf.cuni.cz>
9 * (c) 2000 Andreas Mohr (changes)
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include "wine/port.h"
35 #include <sys/types.h>
42 #ifdef HAVE_SYS_STATVFS_H
43 # include <sys/statvfs.h>
46 #define NONAMELESSUNION
47 #define NONAMELESSSTRUCT
53 #include "wine/winbase16.h" /* for GetCurrentTask */
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);
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 dev_t dev; /* unix device number */
75 ino_t ino; /* unix inode number */
79 static const WCHAR DRIVE_Types[][8] =
81 { 0 }, /* DRIVE_UNKNOWN */
82 { 0 }, /* DRIVE_NO_ROOT_DIR */
83 {'f','l','o','p','p','y',0}, /* DRIVE_REMOVABLE */
84 {'h','d',0}, /* DRIVE_FIXED */
85 {'n','e','t','w','o','r','k',0}, /* DRIVE_REMOTE */
86 {'c','d','r','o','m',0}, /* DRIVE_CDROM */
87 {'r','a','m','d','i','s','k',0} /* DRIVE_RAMDISK */
90 #define MAX_DOS_DRIVES 26
92 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
93 static int DRIVE_CurDrive = -1;
95 static HTASK16 DRIVE_LastTask = 0;
97 /* strdup on the process heap */
98 inline static char *heap_strdup( const char *str )
100 INT len = strlen(str) + 1;
101 LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
102 if (p) memcpy( p, str, len );
106 extern void CDROM_InitRegistry(int dev);
108 /***********************************************************************
111 static inline UINT DRIVE_GetDriveType( INT drive, LPCWSTR value )
115 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
117 if (!strcmpiW( value, DRIVE_Types[i] )) return i;
119 MESSAGE("Drive %c: unknown drive type %s, defaulting to 'hd'.\n",
120 'A' + drive, debugstr_w(value) );
125 /***********************************************************************
130 int i, len, symlink_count = 0, count = 0;
131 WCHAR driveW[] = {'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
132 'W','i','n','e','\\','W','i','n','e','\\',
133 'C','o','n','f','i','g','\\','D','r','i','v','e',' ','A',0};
134 WCHAR drive_env[] = {'=','A',':',0};
135 WCHAR path[MAX_PATHNAME_LEN];
136 char tmp[MAX_PATHNAME_LEN*sizeof(WCHAR) + sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
137 struct stat drive_stat_buffer;
142 OBJECT_ATTRIBUTES attr;
143 UNICODE_STRING nameW;
145 const char *config_dir = wine_get_config_dir();
147 static const WCHAR PathW[] = {'P','a','t','h',0};
148 static const WCHAR TypeW[] = {'T','y','p','e',0};
149 static const WCHAR DeviceW[] = {'D','e','v','i','c','e',0};
151 attr.Length = sizeof(attr);
152 attr.RootDirectory = 0;
153 attr.ObjectName = &nameW;
155 attr.SecurityDescriptor = NULL;
156 attr.SecurityQualityOfService = NULL;
158 /* get the root of the drives from symlinks */
161 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
165 root = HeapAlloc( GetProcessHeap(), 0, strlen(config_dir) + sizeof("/dosdevices/a:") );
166 strcpy( root, config_dir );
167 strcat( root, "/dosdevices/a:" );
169 root[strlen(root)-2] = 'a' + i;
170 if (stat( root, &drive_stat_buffer ))
172 if (!lstat( root, &drive_stat_buffer))
173 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
174 root, strerror(errno), 'a' + i);
177 if (!S_ISDIR(drive_stat_buffer.st_mode))
179 MESSAGE("%s is not a directory, ignoring drive %c:\n", root, 'a' + i );
183 drive->dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(drive->dos_cwd[0]));
184 drive->unix_cwd = heap_strdup( "" );
185 drive->device = NULL;
186 drive->dev = drive_stat_buffer.st_dev;
187 drive->ino = drive_stat_buffer.st_ino;
188 drive->type = DRIVE_FIXED;
192 if (root) HeapFree( GetProcessHeap(), 0, root );
194 /* now get the parameters from the config file */
196 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
198 RtlInitUnicodeString( &nameW, driveW );
199 nameW.Buffer[(nameW.Length / sizeof(WCHAR)) - 1] = 'A' + i;
200 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ) != STATUS_SUCCESS) continue;
202 /* Get the root path */
205 RtlInitUnicodeString( &nameW, PathW );
206 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
208 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
209 ExpandEnvironmentStringsW( data, path, sizeof(path)/sizeof(WCHAR) );
211 p = path + strlenW(path) - 1;
212 while ((p > path) && (*p == '/')) *p-- = '\0';
216 len = WideCharToMultiByte(CP_UNIXCP, 0, path, -1, NULL, 0, NULL, NULL);
217 drive->root = HeapAlloc(GetProcessHeap(), 0, len);
218 WideCharToMultiByte(CP_UNIXCP, 0, path, -1, drive->root, len, NULL, NULL);
222 /* relative paths are relative to config dir */
223 const char *config = wine_get_config_dir();
224 len = strlen(config);
225 len += WideCharToMultiByte(CP_UNIXCP, 0, path, -1, NULL, 0, NULL, NULL) + 2;
226 drive->root = HeapAlloc( GetProcessHeap(), 0, len );
227 len -= sprintf( drive->root, "%s/", config );
228 WideCharToMultiByte(CP_UNIXCP, 0, path, -1,
229 drive->root + strlen(drive->root), len, NULL, NULL);
232 if (stat( drive->root, &drive_stat_buffer ))
234 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
235 drive->root, strerror(errno), 'A' + i);
236 HeapFree( GetProcessHeap(), 0, drive->root );
240 if (!S_ISDIR(drive_stat_buffer.st_mode))
242 MESSAGE("%s is not a directory, ignoring drive %c:\n",
243 drive->root, 'A' + i );
244 HeapFree( GetProcessHeap(), 0, drive->root );
249 drive->dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(drive->dos_cwd[0]));
250 drive->unix_cwd = heap_strdup( "" );
251 drive->device = NULL;
252 drive->dev = drive_stat_buffer.st_dev;
253 drive->ino = drive_stat_buffer.st_ino;
254 drive->type = DRIVE_FIXED;
260 /* Get the drive type */
261 RtlInitUnicodeString( &nameW, TypeW );
262 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
264 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
265 drive->type = DRIVE_GetDriveType( i, data );
269 RtlInitUnicodeString( &nameW, DeviceW );
270 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
272 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
273 len = WideCharToMultiByte(CP_UNIXCP, 0, data, -1, NULL, 0, NULL, NULL);
274 drive->device = HeapAlloc(GetProcessHeap(), 0, len);
275 WideCharToMultiByte(CP_UNIXCP, 0, data, -1, drive->device, len, NULL, NULL);
277 if (drive->type == DRIVE_CDROM)
280 if ((cd_fd = open(drive->device, O_RDONLY|O_NONBLOCK)) != -1)
282 CDROM_InitRegistry(cd_fd);
288 /* Make the first hard disk the current drive */
289 if ((DRIVE_CurDrive == -1) && (drive->type == DRIVE_FIXED))
293 TRACE("Drive %c: path=%s type=%s dev=%x ino=%x\n",
294 'A' + i, drive->root, debugstr_w(DRIVE_Types[drive->type]),
295 (int)drive->dev, (int)drive->ino );
302 if (!count && !symlink_count)
304 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
305 /* Create a C drive pointing to Unix root dir */
306 DOSDrives[2].root = heap_strdup( "/" );
307 DOSDrives[2].dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DOSDrives[2].dos_cwd[0]));
308 DOSDrives[2].unix_cwd = heap_strdup( "" );
309 DOSDrives[2].type = DRIVE_FIXED;
310 DOSDrives[2].device = NULL;
314 /* Make sure the current drive is valid */
315 if (DRIVE_CurDrive == -1)
317 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
327 /* get current working directory info for all drives */
328 for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
330 if (!GetEnvironmentVariableW(drive_env, path, MAX_PATHNAME_LEN)) continue;
332 if (toupperW(path[0]) != drive_env[1] || path[1] != ':') continue;
333 DRIVE_Chdir( i, path + 2 );
339 /***********************************************************************
342 int DRIVE_IsValid( int drive )
344 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
345 return (DOSDrives[drive].root != NULL);
349 /***********************************************************************
350 * DRIVE_GetCurrentDrive
352 int DRIVE_GetCurrentDrive(void)
354 TDB *pTask = GlobalLock16(GetCurrentTask());
355 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
356 return DRIVE_CurDrive;
360 /***********************************************************************
361 * DRIVE_SetCurrentDrive
363 static int DRIVE_SetCurrentDrive( int drive )
365 TDB *pTask = GlobalLock16(GetCurrentTask());
366 if (!DRIVE_IsValid( drive ))
368 SetLastError( ERROR_INVALID_DRIVE );
371 TRACE("%c:\n", 'A' + drive );
372 DRIVE_CurDrive = drive;
373 if (pTask) pTask->curdrive = drive | 0x80;
378 /***********************************************************************
379 * DRIVE_FindDriveRoot
381 * Find a drive for which the root matches the beginning of the given path.
382 * This can be used to translate a Unix path into a drive + DOS path.
383 * Return value is the drive, or -1 on error. On success, path is modified
384 * to point to the beginning of the DOS path.
386 * Note: path must be in the encoding of the underlying Unix file system.
388 int DRIVE_FindDriveRoot( const char **path )
390 /* Starting with the full path, check if the device and inode match any of
391 * the wine 'drives'. If not then remove the last path component and try
392 * again. If the last component was a '..' then skip a normal component
393 * since it's a directory that's ascended back out of.
395 int drive, level, len;
396 char buffer[MAX_PATHNAME_LEN];
400 strcpy( buffer, *path );
401 for (p = buffer; *p; p++) if (*p == '\\') *p = '/';
404 /* strip off trailing slashes */
405 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
410 if (stat( buffer, &st ) == 0 && S_ISDIR( st.st_mode ))
412 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
414 if (!DOSDrives[drive].root) continue;
416 if ((DOSDrives[drive].dev == st.st_dev) &&
417 (DOSDrives[drive].ino == st.st_ino))
419 if (len == 1) len = 0; /* preserve root slash in returned path */
420 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
421 *path, 'A' + drive, buffer, *path + len);
423 if (!**path) *path = "\\";
428 if (len <= 1) return -1; /* reached root */
433 /* find start of the last path component */
434 while (len > 1 && buffer[len - 1] != '/') len--;
435 if (!buffer[len]) break; /* empty component -> reached root */
436 /* does removing it take us up a level? */
437 if (strcmp( buffer + len, "." ) != 0)
438 level += strcmp( buffer + len, ".." ) ? 1 : -1;
440 /* strip off trailing slashes */
441 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
447 /***********************************************************************
448 * DRIVE_FindDriveRootW
450 * Unicode version of DRIVE_FindDriveRoot.
452 int DRIVE_FindDriveRootW( LPCWSTR *path )
454 int drive, level, len;
455 WCHAR buffer[MAX_PATHNAME_LEN];
459 strcpyW( buffer, *path );
460 for (p = buffer; *p; p++) if (*p == '\\') *p = '/';
463 /* strip off trailing slashes */
464 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
468 char buffA[MAX_PATHNAME_LEN];
470 WideCharToMultiByte( CP_UNIXCP, 0, buffer, -1, buffA, sizeof(buffA), NULL, NULL );
471 if (stat( buffA, &st ) == 0 && S_ISDIR( st.st_mode ))
474 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
476 if (!DOSDrives[drive].root) continue;
478 if ((DOSDrives[drive].dev == st.st_dev) &&
479 (DOSDrives[drive].ino == st.st_ino))
481 static const WCHAR rootW[] = {'\\',0};
483 if (len == 1) len = 0; /* preserve root slash in returned path */
484 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
485 debugstr_w(*path), 'A' + drive, debugstr_w(buffer), debugstr_w(*path + len));
487 if (!**path) *path = rootW;
492 if (len <= 1) return -1; /* reached root */
497 static const WCHAR dotW[] = {'.',0};
498 static const WCHAR dotdotW[] = {'.','.',0};
500 /* find start of the last path component */
501 while (len > 1 && buffer[len - 1] != '/') len--;
502 if (!buffer[len]) break; /* empty component -> reached root */
503 /* does removing it take us up a level? */
504 if (strcmpW( buffer + len, dotW ) != 0)
505 level += strcmpW( buffer + len, dotdotW ) ? 1 : -1;
507 /* strip off trailing slashes */
508 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
514 /***********************************************************************
517 const char * DRIVE_GetRoot( int drive )
519 if (!DRIVE_IsValid( drive )) return NULL;
520 return DOSDrives[drive].root;
524 /***********************************************************************
527 LPCWSTR DRIVE_GetDosCwd( int drive )
529 TDB *pTask = GlobalLock16(GetCurrentTask());
530 if (!DRIVE_IsValid( drive )) return NULL;
532 /* Check if we need to change the directory to the new task. */
533 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
534 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
535 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
537 static const WCHAR rootW[] = {'\\',0};
538 WCHAR curdirW[MAX_PATH];
539 MultiByteToWideChar(CP_ACP, 0, pTask->curdir, -1, curdirW, MAX_PATH);
540 /* Perform the task-switch */
541 if (!DRIVE_Chdir( drive, curdirW )) DRIVE_Chdir( drive, rootW );
542 DRIVE_LastTask = GetCurrentTask();
544 return DOSDrives[drive].dos_cwd;
548 /***********************************************************************
551 const char * DRIVE_GetUnixCwd( int drive )
553 TDB *pTask = GlobalLock16(GetCurrentTask());
554 if (!DRIVE_IsValid( drive )) return NULL;
556 /* Check if we need to change the directory to the new task. */
557 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
558 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
559 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
561 static const WCHAR rootW[] = {'\\',0};
562 WCHAR curdirW[MAX_PATH];
563 MultiByteToWideChar(CP_ACP, 0, pTask->curdir, -1, curdirW, MAX_PATH);
564 /* Perform the task-switch */
565 if (!DRIVE_Chdir( drive, curdirW )) DRIVE_Chdir( drive, rootW );
566 DRIVE_LastTask = GetCurrentTask();
568 return DOSDrives[drive].unix_cwd;
572 /***********************************************************************
575 const char * DRIVE_GetDevice( int drive )
577 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
580 /***********************************************************************
583 static UINT DRIVE_GetType( int drive )
585 if (!DRIVE_IsValid( drive )) return DRIVE_NO_ROOT_DIR;
586 return DOSDrives[drive].type;
590 /***********************************************************************
593 int DRIVE_Chdir( int drive, LPCWSTR path )
595 DOS_FULL_NAME full_name;
596 WCHAR buffer[MAX_PATHNAME_LEN];
598 BY_HANDLE_FILE_INFORMATION info;
599 TDB *pTask = GlobalLock16(GetCurrentTask());
601 buffer[0] = 'A' + drive;
604 TRACE("(%s,%s)\n", debugstr_w(buffer), debugstr_w(path) );
605 strncpyW( buffer + 2, path, MAX_PATHNAME_LEN - 2 );
606 buffer[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
608 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
609 if (!FILE_Stat( full_name.long_name, &info, NULL )) return 0;
610 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
612 SetLastError( ERROR_FILE_NOT_FOUND );
615 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
616 while (*unix_cwd == '/') unix_cwd++;
618 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
619 'A' + drive, unix_cwd, debugstr_w(full_name.short_name + 3) );
621 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
622 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
623 DOSDrives[drive].dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name.short_name) - 2) * sizeof(WCHAR));
624 strcpyW(DOSDrives[drive].dos_cwd, full_name.short_name + 3);
625 DOSDrives[drive].unix_cwd = heap_strdup( unix_cwd );
627 if (drive == DRIVE_CurDrive)
631 RtlInitUnicodeString( &dirW, full_name.short_name );
632 RtlSetCurrentDirectory_U( &dirW );
635 if (pTask && (pTask->curdrive & 0x80) &&
636 ((pTask->curdrive & ~0x80) == drive))
638 WideCharToMultiByte(CP_ACP, 0, full_name.short_name + 2, -1,
639 pTask->curdir, sizeof(pTask->curdir), NULL, NULL);
640 DRIVE_LastTask = GetCurrentTask();
646 /***********************************************************************
649 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
650 PULARGE_INTEGER available )
654 if (!DRIVE_IsValid(drive))
656 SetLastError( ERROR_PATH_NOT_FOUND );
660 if (statvfs( DOSDrives[drive].root, &info ) < 0)
663 WARN("cannot do statvfs(%s)\n", DOSDrives[drive].root);
666 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_frsize, info.f_blocks );
667 if (DOSDrives[drive].type == DRIVE_CDROM)
668 available->QuadPart = 0; /* ALWAYS 0, even if no real CD-ROM mounted there !! */
670 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_frsize, info.f_bavail );
675 /***********************************************************************
676 * DRIVE_GetCurrentDirectory
677 * Returns "X:\\path\\etc\\".
679 * Despite the API description, return required length including the
680 * terminating null when buffer too small. This is the real behaviour.
682 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPWSTR buf )
685 LPCWSTR dos_cwd = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
686 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
688 ret = strlenW(dos_cwd) + 3; /* length of WHOLE current directory */
689 if (ret >= buflen) return ret + 1;
691 strcpyW( buf, driveA_rootW );
692 buf[0] += DRIVE_GetCurrentDrive();
693 strcatW( buf, dos_cwd );
698 /***********************************************************************
701 * Build the environment array containing the drives' current directories.
702 * Resulting pointer must be freed with HeapFree.
704 WCHAR *DRIVE_BuildEnv(void)
707 LPCWSTR cwd[MAX_DOS_DRIVES];
710 for (i = 0; i < MAX_DOS_DRIVES; i++)
712 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0])
713 length += strlenW(cwd[i]) + 8;
715 if (!(env = HeapAlloc( GetProcessHeap(), 0, (length+1) * sizeof(WCHAR) ))) return NULL;
716 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
718 if (cwd[i] && cwd[i][0])
720 *p++ = '='; *p++ = 'A' + i; *p++ = ':';
721 *p++ = '='; *p++ = 'A' + i; *p++ = ':'; *p++ = '\\';
722 strcpyW( p, cwd[i] );
731 /***********************************************************************
732 * GetDiskFreeSpaceW (KERNEL32.@)
734 * Fails if expression resulting from current drive's dir and "root"
735 * is not a root dir of the target drive.
737 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
738 * if the corresponding info is unneeded.
740 * FIXME: needs to support UNC names from Win95 OSR2 on.
742 * Behaviour under Win95a:
743 * CurrDir root result
744 * "E:\\TEST" "E:" FALSE
748 * "E:\\TEST" "\\" TRUE
749 * "E:\\TEST" ":\\" FALSE
750 * "E:\\TEST" "E:\\" TRUE
751 * "E:\\TEST" "" FALSE
752 * "E:\\" "" FALSE (!)
754 * "E:\\TEST" 0x0 TRUE (!)
755 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
756 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
758 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
759 LPDWORD sector_bytes, LPDWORD free_clusters,
760 LPDWORD total_clusters )
763 ULARGE_INTEGER size,available;
767 TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root), cluster_sectors, sector_bytes,
768 free_clusters, total_clusters);
770 if (!root || root[0] == '\\' || root[0] == '/')
771 drive = DRIVE_GetCurrentDrive();
773 if (root[0] && root[1] == ':') /* root contains drive tag */
775 drive = toupperW(root[0]) - 'A';
779 path = DRIVE_GetDosCwd(drive);
782 SetLastError(ERROR_PATH_NOT_FOUND);
790 if (path[0]) /* oops, we are in a subdir */
792 SetLastError(ERROR_INVALID_NAME);
799 SetLastError(ERROR_PATH_NOT_FOUND);
801 SetLastError(ERROR_INVALID_NAME);
805 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
807 /* Cap the size and available at 2GB as per specs. */
808 if ((size.u.HighPart) ||(size.u.LowPart > 0x7fffffff))
811 size.u.LowPart = 0x7fffffff;
813 if ((available.u.HighPart) ||(available.u.LowPart > 0x7fffffff))
815 available.u.HighPart =0;
816 available.u.LowPart = 0x7fffffff;
818 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
819 size.u.LowPart /= sec_size;
820 available.u.LowPart /= sec_size;
821 /* FIXME: probably have to adjust those variables too for CDFS */
823 while (cluster_sec * 65536 < size.u.LowPart) cluster_sec *= 2;
826 *cluster_sectors = cluster_sec;
828 *sector_bytes = sec_size;
830 *free_clusters = available.u.LowPart / cluster_sec;
832 *total_clusters = size.u.LowPart / cluster_sec;
837 /***********************************************************************
838 * GetDiskFreeSpaceA (KERNEL32.@)
840 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
841 LPDWORD sector_bytes, LPDWORD free_clusters,
842 LPDWORD total_clusters )
844 UNICODE_STRING rootW;
849 if(!RtlCreateUnicodeStringFromAsciiz(&rootW, root))
851 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
858 ret = GetDiskFreeSpaceW(rootW.Buffer, cluster_sectors, sector_bytes,
859 free_clusters, total_clusters );
860 RtlFreeUnicodeString(&rootW);
866 /***********************************************************************
867 * GetDiskFreeSpaceExW (KERNEL32.@)
869 * This function is used to acquire the size of the available and
870 * total space on a logical volume.
874 * Zero on failure, nonzero upon success. Use GetLastError to obtain
875 * detailed error information.
878 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root,
879 PULARGE_INTEGER avail,
880 PULARGE_INTEGER total,
881 PULARGE_INTEGER totalfree)
884 ULARGE_INTEGER size,available;
886 if (!root) drive = DRIVE_GetCurrentDrive();
888 { /* C: always works for GetDiskFreeSpaceEx */
889 if ((root[1]) && ((root[1] != ':') || (root[2] && root[2] != '\\')))
891 FIXME("there are valid root names which are not supported yet\n");
892 /* ..like UNC names, for instance. */
894 WARN("invalid root '%s'\n", debugstr_w(root));
897 drive = toupperW(root[0]) - 'A';
900 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
904 total->u.HighPart = size.u.HighPart;
905 total->u.LowPart = size.u.LowPart;
910 totalfree->u.HighPart = available.u.HighPart;
911 totalfree->u.LowPart = available.u.LowPart;
918 /* On Windows2000, we need to check the disk quota
919 allocated for the user owning the calling process. We
920 don't want to be more obtrusive than necessary with the
921 FIXME messages, so don't print the FIXME unless Wine is
922 actually masquerading as Windows2000. */
924 RTL_OSVERSIONINFOEXW ovi;
925 ovi.dwOSVersionInfoSize = sizeof(ovi);
926 if (RtlGetVersion(&ovi))
928 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
929 FIXME("no per-user quota support yet\n");
933 /* Quick hack, should eventually be fixed to work 100% with
934 Windows2000 (see comment above). */
935 avail->u.HighPart = available.u.HighPart;
936 avail->u.LowPart = available.u.LowPart;
942 /***********************************************************************
943 * GetDiskFreeSpaceExA (KERNEL32.@)
945 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root, PULARGE_INTEGER avail,
946 PULARGE_INTEGER total,
947 PULARGE_INTEGER totalfree)
949 UNICODE_STRING rootW;
952 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
953 else rootW.Buffer = NULL;
955 ret = GetDiskFreeSpaceExW( rootW.Buffer, avail, total, totalfree);
957 RtlFreeUnicodeString(&rootW);
961 /***********************************************************************
962 * GetDriveTypeW (KERNEL32.@)
964 * Returns the type of the disk drive specified. If root is NULL the
965 * root of the current directory is used.
969 * Type of drive (from Win32 SDK):
971 * DRIVE_UNKNOWN unable to find out anything about the drive
972 * DRIVE_NO_ROOT_DIR nonexistent root dir
973 * DRIVE_REMOVABLE the disk can be removed from the machine
974 * DRIVE_FIXED the disk can not be removed from the machine
975 * DRIVE_REMOTE network disk
976 * DRIVE_CDROM CDROM drive
977 * DRIVE_RAMDISK virtual disk in RAM
979 UINT WINAPI GetDriveTypeW(LPCWSTR root) /* [in] String describing drive */
982 TRACE("(%s)\n", debugstr_w(root));
984 if (NULL == root) drive = DRIVE_GetCurrentDrive();
987 if ((root[1]) && (root[1] != ':'))
989 WARN("invalid root %s\n", debugstr_w(root));
990 return DRIVE_NO_ROOT_DIR;
992 drive = toupperW(root[0]) - 'A';
994 return DRIVE_GetType(drive);
998 /***********************************************************************
999 * GetDriveTypeA (KERNEL32.@)
1001 UINT WINAPI GetDriveTypeA( LPCSTR root )
1003 UNICODE_STRING rootW;
1008 if( !RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1010 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1015 rootW.Buffer = NULL;
1017 ret = GetDriveTypeW(rootW.Buffer);
1019 RtlFreeUnicodeString(&rootW);
1025 /***********************************************************************
1026 * GetCurrentDirectory (KERNEL.411)
1028 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1030 WCHAR cur_dirW[MAX_PATH];
1032 DRIVE_GetCurrentDirectory(MAX_PATH, cur_dirW);
1033 return (UINT16)WideCharToMultiByte(CP_ACP, 0, cur_dirW, -1, buf, buflen, NULL, NULL);
1037 /***********************************************************************
1038 * GetCurrentDirectoryW (KERNEL32.@)
1040 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1043 WCHAR longname[MAX_PATHNAME_LEN];
1044 WCHAR shortname[MAX_PATHNAME_LEN];
1046 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1047 if ( ret > MAX_PATHNAME_LEN ) {
1048 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1051 GetLongPathNameW(shortname, longname, MAX_PATHNAME_LEN);
1052 ret = strlenW( longname ) + 1;
1053 if (ret > buflen) return ret;
1054 strcpyW(buf, longname);
1058 /***********************************************************************
1059 * GetCurrentDirectoryA (KERNEL32.@)
1061 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1063 WCHAR bufferW[MAX_PATH];
1066 retW = GetCurrentDirectoryW(MAX_PATH, bufferW);
1070 else if (retW > MAX_PATH)
1072 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1077 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1080 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buf, buflen, NULL, NULL);
1081 ret--; /* length without 0 */
1088 /***********************************************************************
1089 * SetCurrentDirectoryW (KERNEL32.@)
1091 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dir )
1093 int drive, olddrive = DRIVE_GetCurrentDrive();
1097 SetLastError(ERROR_INVALID_PARAMETER);
1100 if (dir[0] && (dir[1]==':'))
1102 drive = toupperW( *dir ) - 'A';
1108 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1109 sets pTask->curdir only if pTask->curdrive is drive */
1110 if (!(DRIVE_SetCurrentDrive( drive )))
1113 /* FIXME: what about empty strings? Add a \\ ? */
1114 if (!DRIVE_Chdir( drive, dir )) {
1115 DRIVE_SetCurrentDrive(olddrive);
1122 /***********************************************************************
1123 * SetCurrentDirectoryA (KERNEL32.@)
1125 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1127 UNICODE_STRING dirW;
1132 SetLastError(ERROR_INVALID_PARAMETER);
1136 if (RtlCreateUnicodeStringFromAsciiz(&dirW, dir))
1138 ret = SetCurrentDirectoryW(dirW.Buffer);
1139 RtlFreeUnicodeString(&dirW);
1142 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1147 /***********************************************************************
1148 * GetLogicalDriveStringsA (KERNEL32.@)
1150 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1154 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1155 if (DRIVE_IsValid(drive)) count++;
1156 if ((count * 4) + 1 <= len)
1159 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1160 if (DRIVE_IsValid(drive))
1171 return (count * 4) + 1; /* account for terminating null */
1172 /* The API tells about these different return values */
1176 /***********************************************************************
1177 * GetLogicalDriveStringsW (KERNEL32.@)
1179 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1183 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1184 if (DRIVE_IsValid(drive)) count++;
1185 if (count * 4 * sizeof(WCHAR) <= len)
1188 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1189 if (DRIVE_IsValid(drive))
1191 *p++ = (WCHAR)('a' + drive);
1198 return count * 4 * sizeof(WCHAR);
1202 /***********************************************************************
1203 * GetLogicalDrives (KERNEL32.@)
1205 DWORD WINAPI GetLogicalDrives(void)
1210 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1212 if ( (DRIVE_IsValid(drive)) ||
1213 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1214 ret |= (1 << drive);