Hack to update ntdll current directory value in DRIVE_Chdir.
[wine.git] / files / drive.c
blobb34b15b772d0bdf8a08009b2596b8f82fd87eaf6
1 /*
2 * DOS drives handling functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
7 * Label & serial number read support.
8 * (c) 1999 Petr Tomasek <tomasek@etf.cuni.cz>
9 * (c) 2000 Andreas Mohr (changes)
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "config.h"
27 #include "wine/port.h"
29 #include <assert.h>
30 #include <ctype.h>
31 #include <string.h>
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <errno.h>
39 #ifdef HAVE_UNISTD_H
40 # include <unistd.h>
41 #endif
43 #ifdef HAVE_SYS_PARAM_H
44 # include <sys/param.h>
45 #endif
46 #ifdef STATFS_DEFINED_BY_SYS_VFS
47 # include <sys/vfs.h>
48 #else
49 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
50 # include <sys/mount.h>
51 # else
52 # ifdef STATFS_DEFINED_BY_SYS_STATFS
53 # include <sys/statfs.h>
54 # endif
55 # endif
56 #endif
58 #define NONAMELESSUNION
59 #define NONAMELESSSTRUCT
60 #include "ntstatus.h"
61 #include "windef.h"
62 #include "winbase.h"
63 #include "winreg.h"
64 #include "winternl.h"
65 #include "wine/winbase16.h" /* for GetCurrentTask */
66 #include "winerror.h"
67 #include "winioctl.h"
68 #include "ntddstor.h"
69 #include "ntddcdrm.h"
70 #include "drive.h"
71 #include "file.h"
72 #include "msdos.h"
73 #include "task.h"
74 #include "wine/unicode.h"
75 #include "wine/library.h"
76 #include "wine/server.h"
77 #include "wine/debug.h"
79 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
80 WINE_DECLARE_DEBUG_CHANNEL(file);
82 typedef struct
84 char *root; /* root dir in Unix format without trailing / */
85 LPWSTR dos_cwd; /* cwd in DOS format without leading or trailing \ */
86 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
87 char *device; /* raw device path */
88 WCHAR label_conf[12]; /* drive label as cfg'd in wine config */
89 WCHAR label_read[12]; /* drive label as read from device */
90 DWORD serial_conf; /* drive serial number as cfg'd in wine config */
91 UINT type; /* drive type */
92 UINT flags; /* drive flags */
93 UINT codepage; /* drive code page */
94 dev_t dev; /* unix device number */
95 ino_t ino; /* unix inode number */
96 } DOSDRIVE;
99 static const WCHAR DRIVE_Types[][8] =
101 { 0 }, /* DRIVE_UNKNOWN */
102 { 0 }, /* DRIVE_NO_ROOT_DIR */
103 {'f','l','o','p','p','y',0}, /* DRIVE_REMOVABLE */
104 {'h','d',0}, /* DRIVE_FIXED */
105 {'n','e','t','w','o','r','k',0}, /* DRIVE_REMOTE */
106 {'c','d','r','o','m',0}, /* DRIVE_CDROM */
107 {'r','a','m','d','i','s','k',0} /* DRIVE_RAMDISK */
111 /* Known filesystem types */
113 typedef struct
115 const WCHAR name[6];
116 UINT flags;
117 } FS_DESCR;
119 static const FS_DESCR DRIVE_Filesystems[] =
121 { {'u','n','i','x',0}, DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
122 { {'m','s','d','o','s',0}, DRIVE_SHORT_NAMES },
123 { {'d','o','s',0}, DRIVE_SHORT_NAMES },
124 { {'f','a','t',0}, DRIVE_SHORT_NAMES },
125 { {'v','f','a','t',0}, DRIVE_CASE_PRESERVING },
126 { {'w','i','n','9','5',0}, DRIVE_CASE_PRESERVING },
127 { { 0 }, 0 }
131 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
132 static int DRIVE_CurDrive = -1;
134 static HTASK16 DRIVE_LastTask = 0;
136 /* strdup on the process heap */
137 inline static char *heap_strdup( const char *str )
139 INT len = strlen(str) + 1;
140 LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
141 if (p) memcpy( p, str, len );
142 return p;
145 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
147 extern void CDROM_InitRegistry(int dev);
149 /***********************************************************************
150 * DRIVE_GetDriveType
152 static inline UINT DRIVE_GetDriveType( INT drive, LPCWSTR value )
154 int i;
156 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
158 if (!strcmpiW( value, DRIVE_Types[i] )) return i;
160 MESSAGE("Drive %c: unknown drive type %s, defaulting to 'hd'.\n",
161 'A' + drive, debugstr_w(value) );
162 return DRIVE_FIXED;
166 /***********************************************************************
167 * DRIVE_GetFSFlags
169 static UINT DRIVE_GetFSFlags( INT drive, LPCWSTR value )
171 const FS_DESCR *descr;
173 for (descr = DRIVE_Filesystems; *descr->name; descr++)
174 if (!strcmpiW( value, descr->name )) return descr->flags;
175 MESSAGE("Drive %c: unknown filesystem type %s, defaulting to 'win95'.\n",
176 'A' + drive, debugstr_w(value) );
177 return DRIVE_CASE_PRESERVING;
181 /***********************************************************************
182 * DRIVE_Init
184 int DRIVE_Init(void)
186 int i, len, count = 0;
187 WCHAR driveW[] = {'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
188 'W','i','n','e','\\','W','i','n','e','\\',
189 'C','o','n','f','i','g','\\','D','r','i','v','e',' ','A',0};
190 WCHAR drive_env[] = {'=','A',':',0};
191 WCHAR path[MAX_PATHNAME_LEN];
192 char tmp[MAX_PATHNAME_LEN*sizeof(WCHAR) + sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
193 struct stat drive_stat_buffer;
194 WCHAR *p;
195 DOSDRIVE *drive;
196 HKEY hkey;
197 DWORD dummy;
198 OBJECT_ATTRIBUTES attr;
199 UNICODE_STRING nameW;
201 static const WCHAR PathW[] = {'P','a','t','h',0};
202 static const WCHAR CodepageW[] = {'C','o','d','e','p','a','g','e',0};
203 static const WCHAR LabelW[] = {'L','a','b','e','l',0};
204 static const WCHAR SerialW[] = {'S','e','r','i','a','l',0};
205 static const WCHAR TypeW[] = {'T','y','p','e',0};
206 static const WCHAR FilesystemW[] = {'F','i','l','e','s','y','s','t','e','m',0};
207 static const WCHAR DeviceW[] = {'D','e','v','i','c','e',0};
208 static const WCHAR ReadVolInfoW[] = {'R','e','a','d','V','o','l','I','n','f','o',0};
209 static const WCHAR FailReadOnlyW[] = {'F','a','i','l','R','e','a','d','O','n','l','y',0};
210 static const WCHAR driveC_labelW[] = {'D','r','i','v','e',' ','C',' ',' ',' ',' ',0};
213 attr.Length = sizeof(attr);
214 attr.RootDirectory = 0;
215 attr.ObjectName = &nameW;
216 attr.Attributes = 0;
217 attr.SecurityDescriptor = NULL;
218 attr.SecurityQualityOfService = NULL;
220 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
222 RtlInitUnicodeString( &nameW, driveW );
223 nameW.Buffer[(nameW.Length / sizeof(WCHAR)) - 1] = 'A' + i;
224 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ) != STATUS_SUCCESS) continue;
226 /* Get the code page number */
227 RtlInitUnicodeString( &nameW, CodepageW );
228 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
230 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
231 drive->codepage = strtolW( data, NULL, 10 );
234 /* Get the root path */
235 RtlInitUnicodeString( &nameW, PathW );
236 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
238 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
239 ExpandEnvironmentStringsW( data, path, sizeof(path)/sizeof(WCHAR) );
241 p = path + strlenW(path) - 1;
242 while ((p > path) && (*p == '/')) *p-- = '\0';
244 if (path[0] == '/')
246 len = WideCharToMultiByte(drive->codepage, 0, path, -1, NULL, 0, NULL, NULL);
247 drive->root = HeapAlloc(GetProcessHeap(), 0, len);
248 WideCharToMultiByte(drive->codepage, 0, path, -1, drive->root, len, NULL, NULL);
250 else
252 /* relative paths are relative to config dir */
253 const char *config = wine_get_config_dir();
254 len = strlen(config);
255 len += WideCharToMultiByte(drive->codepage, 0, path, -1, NULL, 0, NULL, NULL) + 2;
256 drive->root = HeapAlloc( GetProcessHeap(), 0, len );
257 len -= sprintf( drive->root, "%s/", config );
258 WideCharToMultiByte(drive->codepage, 0, path, -1,
259 drive->root + strlen(drive->root), len, NULL, NULL);
262 if (stat( drive->root, &drive_stat_buffer ))
264 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
265 drive->root, strerror(errno), 'A' + i);
266 HeapFree( GetProcessHeap(), 0, drive->root );
267 drive->root = NULL;
268 goto next;
270 if (!S_ISDIR(drive_stat_buffer.st_mode))
272 MESSAGE("%s is not a directory, ignoring drive %c:\n",
273 drive->root, 'A' + i );
274 HeapFree( GetProcessHeap(), 0, drive->root );
275 drive->root = NULL;
276 goto next;
279 drive->dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(drive->dos_cwd[0]));
280 drive->unix_cwd = heap_strdup( "" );
281 drive->device = NULL;
282 drive->flags = 0;
283 drive->dev = drive_stat_buffer.st_dev;
284 drive->ino = drive_stat_buffer.st_ino;
286 /* Get the drive type */
287 RtlInitUnicodeString( &nameW, TypeW );
288 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
290 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
291 drive->type = DRIVE_GetDriveType( i, data );
293 else drive->type = DRIVE_FIXED;
295 /* Get the drive label */
296 RtlInitUnicodeString( &nameW, LabelW );
297 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
299 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
300 lstrcpynW( drive->label_conf, data, 12 );
302 if ((len = strlenW(drive->label_conf)) < 11)
304 /* Pad label with spaces */
305 while(len < 11) drive->label_conf[len++] = ' ';
306 drive->label_conf[11] = '\0';
309 /* Get the serial number */
310 RtlInitUnicodeString( &nameW, SerialW );
311 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
313 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
314 drive->serial_conf = strtoulW( data, NULL, 16 );
316 else drive->serial_conf = 12345678;
318 /* Get the filesystem type */
319 RtlInitUnicodeString( &nameW, FilesystemW );
320 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
322 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
323 drive->flags = DRIVE_GetFSFlags( i, data );
325 else drive->flags = DRIVE_CASE_PRESERVING;
327 /* Get the device */
328 RtlInitUnicodeString( &nameW, DeviceW );
329 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
331 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
332 len = WideCharToMultiByte(CP_ACP, 0, data, -1, NULL, 0, NULL, NULL);
333 drive->device = HeapAlloc(GetProcessHeap(), 0, len);
334 WideCharToMultiByte(drive->codepage, 0, data, -1, drive->device, len, NULL, NULL);
336 RtlInitUnicodeString( &nameW, ReadVolInfoW );
337 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
339 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
340 if (IS_OPTION_TRUE(data[0])) drive->flags |= DRIVE_READ_VOL_INFO;
342 else drive->flags |= DRIVE_READ_VOL_INFO;
344 if (drive->type == DRIVE_CDROM)
346 int cd_fd;
347 if ((cd_fd = open(drive->device, O_RDONLY|O_NONBLOCK)) != -1)
349 CDROM_InitRegistry(cd_fd);
350 close(cd_fd);
355 /* Get the FailReadOnly flag */
356 RtlInitUnicodeString( &nameW, FailReadOnlyW );
357 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
359 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
360 if (IS_OPTION_TRUE(data[0])) drive->flags |= DRIVE_FAIL_READ_ONLY;
363 /* Make the first hard disk the current drive */
364 if ((DRIVE_CurDrive == -1) && (drive->type == DRIVE_FIXED))
365 DRIVE_CurDrive = i;
367 count++;
368 TRACE("Drive %c: path=%s type=%s label=%s serial=%08lx "
369 "flags=%08x codepage=%u dev=%x ino=%x\n",
370 'A' + i, drive->root, debugstr_w(DRIVE_Types[drive->type]),
371 debugstr_w(drive->label_conf), drive->serial_conf, drive->flags,
372 drive->codepage, (int)drive->dev, (int)drive->ino );
375 next:
376 NtClose( hkey );
379 if (!count)
381 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
382 /* Create a C drive pointing to Unix root dir */
383 DOSDrives[2].root = heap_strdup( "/" );
384 DOSDrives[2].dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DOSDrives[2].dos_cwd[0]));
385 DOSDrives[2].unix_cwd = heap_strdup( "" );
386 strcpyW( DOSDrives[2].label_conf, driveC_labelW );
387 DOSDrives[2].serial_conf = 12345678;
388 DOSDrives[2].type = DRIVE_FIXED;
389 DOSDrives[2].device = NULL;
390 DOSDrives[2].flags = 0;
391 DRIVE_CurDrive = 2;
394 /* Make sure the current drive is valid */
395 if (DRIVE_CurDrive == -1)
397 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
399 if (drive->root && !(drive->flags & DRIVE_DISABLED))
401 DRIVE_CurDrive = i;
402 break;
407 /* get current working directory info for all drives */
408 for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
410 if (!GetEnvironmentVariableW(drive_env, path, MAX_PATHNAME_LEN)) continue;
411 /* sanity check */
412 if (toupperW(path[0]) != drive_env[1] || path[1] != ':') continue;
413 DRIVE_Chdir( i, path + 2 );
415 return 1;
419 /***********************************************************************
420 * DRIVE_IsValid
422 int DRIVE_IsValid( int drive )
424 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
425 return (DOSDrives[drive].root &&
426 !(DOSDrives[drive].flags & DRIVE_DISABLED));
430 /***********************************************************************
431 * DRIVE_GetCurrentDrive
433 int DRIVE_GetCurrentDrive(void)
435 TDB *pTask = GlobalLock16(GetCurrentTask());
436 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
437 return DRIVE_CurDrive;
441 /***********************************************************************
442 * DRIVE_SetCurrentDrive
444 int DRIVE_SetCurrentDrive( int drive )
446 TDB *pTask = GlobalLock16(GetCurrentTask());
447 if (!DRIVE_IsValid( drive ))
449 SetLastError( ERROR_INVALID_DRIVE );
450 return 0;
452 TRACE("%c:\n", 'A' + drive );
453 DRIVE_CurDrive = drive;
454 if (pTask) pTask->curdrive = drive | 0x80;
455 return 1;
459 /***********************************************************************
460 * DRIVE_FindDriveRoot
462 * Find a drive for which the root matches the beginning of the given path.
463 * This can be used to translate a Unix path into a drive + DOS path.
464 * Return value is the drive, or -1 on error. On success, path is modified
465 * to point to the beginning of the DOS path.
467 * Note: path must be in the encoding of the underlying Unix file system.
469 int DRIVE_FindDriveRoot( const char **path )
471 /* Starting with the full path, check if the device and inode match any of
472 * the wine 'drives'. If not then remove the last path component and try
473 * again. If the last component was a '..' then skip a normal component
474 * since it's a directory that's ascended back out of.
476 int drive, level, len;
477 char buffer[MAX_PATHNAME_LEN];
478 char *p;
479 struct stat st;
481 strcpy( buffer, *path );
482 while ((p = strchr( buffer, '\\' )) != NULL)
483 *p = '/';
484 len = strlen(buffer);
486 /* strip off trailing slashes */
487 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
489 for (;;)
491 /* Find the drive */
492 if (stat( buffer, &st ) == 0 && S_ISDIR( st.st_mode ))
494 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
496 if (!DOSDrives[drive].root ||
497 (DOSDrives[drive].flags & DRIVE_DISABLED))
498 continue;
500 if ((DOSDrives[drive].dev == st.st_dev) &&
501 (DOSDrives[drive].ino == st.st_ino))
503 if (len == 1) len = 0; /* preserve root slash in returned path */
504 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
505 *path, 'A' + drive, buffer, *path + len);
506 *path += len;
507 if (!**path) *path = "\\";
508 return drive;
512 if (len <= 1) return -1; /* reached root */
514 level = 0;
515 while (level < 1)
517 /* find start of the last path component */
518 while (len > 1 && buffer[len - 1] != '/') len--;
519 if (!buffer[len]) break; /* empty component -> reached root */
520 /* does removing it take us up a level? */
521 if (strcmp( buffer + len, "." ) != 0)
522 level += strcmp( buffer + len, ".." ) ? 1 : -1;
523 buffer[len] = 0;
524 /* strip off trailing slashes */
525 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
531 /***********************************************************************
532 * DRIVE_FindDriveRootW
534 * Unicode version of DRIVE_FindDriveRoot.
536 int DRIVE_FindDriveRootW( LPCWSTR *path )
538 int drive, level, len;
539 WCHAR buffer[MAX_PATHNAME_LEN];
540 WCHAR *p;
541 struct stat st;
543 strcpyW( buffer, *path );
544 while ((p = strchrW( buffer, '\\' )) != NULL)
545 *p = '/';
546 len = strlenW(buffer);
548 /* strip off trailing slashes */
549 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
551 for (;;)
553 int codepage = -1;
555 /* Find the drive */
556 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
558 char buffA[MAX_PATHNAME_LEN];
560 if (!DOSDrives[drive].root ||
561 (DOSDrives[drive].flags & DRIVE_DISABLED))
562 continue;
564 if (codepage != DOSDrives[drive].codepage)
566 WideCharToMultiByte( DOSDrives[drive].codepage, 0, buffer, -1,
567 buffA, sizeof(buffA), NULL, NULL );
568 if (stat( buffA, &st ) == -1 || !S_ISDIR( st.st_mode ))
570 codepage = -1;
571 continue;
573 codepage = DOSDrives[drive].codepage;
576 if ((DOSDrives[drive].dev == st.st_dev) &&
577 (DOSDrives[drive].ino == st.st_ino))
579 static const WCHAR rootW[] = {'\\',0};
581 if (len == 1) len = 0; /* preserve root slash in returned path */
582 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
583 debugstr_w(*path), 'A' + drive, debugstr_w(buffer), debugstr_w(*path + len));
584 *path += len;
585 if (!**path) *path = rootW;
586 return drive;
589 if (len <= 1) return -1; /* reached root */
591 level = 0;
592 while (level < 1)
594 static const WCHAR dotW[] = {'.',0};
595 static const WCHAR dotdotW[] = {'.','.',0};
597 /* find start of the last path component */
598 while (len > 1 && buffer[len - 1] != '/') len--;
599 if (!buffer[len]) break; /* empty component -> reached root */
600 /* does removing it take us up a level? */
601 if (strcmpW( buffer + len, dotW ) != 0)
602 level += strcmpW( buffer + len, dotdotW ) ? 1 : -1;
603 buffer[len] = 0;
604 /* strip off trailing slashes */
605 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
611 /***********************************************************************
612 * DRIVE_GetRoot
614 const char * DRIVE_GetRoot( int drive )
616 if (!DRIVE_IsValid( drive )) return NULL;
617 return DOSDrives[drive].root;
621 /***********************************************************************
622 * DRIVE_GetDosCwd
624 LPCWSTR DRIVE_GetDosCwd( int drive )
626 TDB *pTask = GlobalLock16(GetCurrentTask());
627 if (!DRIVE_IsValid( drive )) return NULL;
629 /* Check if we need to change the directory to the new task. */
630 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
631 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
632 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
634 static const WCHAR rootW[] = {'\\',0};
635 WCHAR curdirW[MAX_PATH];
636 MultiByteToWideChar(CP_ACP, 0, pTask->curdir, -1, curdirW, MAX_PATH);
637 /* Perform the task-switch */
638 if (!DRIVE_Chdir( drive, curdirW )) DRIVE_Chdir( drive, rootW );
639 DRIVE_LastTask = GetCurrentTask();
641 return DOSDrives[drive].dos_cwd;
645 /***********************************************************************
646 * DRIVE_GetUnixCwd
648 const char * DRIVE_GetUnixCwd( int drive )
650 TDB *pTask = GlobalLock16(GetCurrentTask());
651 if (!DRIVE_IsValid( drive )) return NULL;
653 /* Check if we need to change the directory to the new task. */
654 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
655 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
656 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
658 static const WCHAR rootW[] = {'\\',0};
659 WCHAR curdirW[MAX_PATH];
660 MultiByteToWideChar(CP_ACP, 0, pTask->curdir, -1, curdirW, MAX_PATH);
661 /* Perform the task-switch */
662 if (!DRIVE_Chdir( drive, curdirW )) DRIVE_Chdir( drive, rootW );
663 DRIVE_LastTask = GetCurrentTask();
665 return DOSDrives[drive].unix_cwd;
669 /***********************************************************************
670 * DRIVE_GetDevice
672 const char * DRIVE_GetDevice( int drive )
674 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
677 /******************************************************************
678 * static WORD CDROM_Data_FindBestVoldesc
682 static WORD CDROM_Data_FindBestVoldesc(int fd)
684 BYTE cur_vd_type, max_vd_type = 0;
685 unsigned int offs, best_offs = 0, extra_offs = 0;
686 char sig[3];
688 for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
690 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
691 * the volume label is displaced forward by 8
693 lseek(fd, offs + 11, SEEK_SET); /* check for non-ISO9660 signature */
694 read(fd, &sig, 3);
695 if ((sig[0] == 'R') && (sig[1] == 'O') && (sig[2]=='M'))
697 extra_offs = 8;
699 lseek(fd, offs + extra_offs, SEEK_SET);
700 read(fd, &cur_vd_type, 1);
701 if (cur_vd_type == 0xff) /* voldesc set terminator */
702 break;
703 if (cur_vd_type > max_vd_type)
705 max_vd_type = cur_vd_type;
706 best_offs = offs + extra_offs;
709 return best_offs;
712 /***********************************************************************
713 * DRIVE_ReadSuperblock
715 * NOTE
716 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
717 * to check, that they are writing on a FAT filesystem !
719 int DRIVE_ReadSuperblock (int drive, char * buff)
721 #define DRIVE_SUPER 96
722 int fd;
723 off_t offs;
724 int ret = 0;
725 struct stat stat_buf;
727 memset(buff, 0, DRIVE_SUPER);
728 /* O_NONBLOCK in case we're opening FIFO; will be reset later */
729 if ((fd = open(DOSDrives[drive].device, O_RDONLY|O_NOCTTY|O_NONBLOCK)) != -1) {
730 if (fstat(fd, &stat_buf) < 0) { /* shouldn't happen since we just opened that file */
731 ERR("fstat() failed for opened device '%s' (drive %c:) ! IT SHOULDN'T HAPPEN !!!\n",
732 DOSDrives[drive].device, 'A'+drive);
733 ret = -1;
734 } else if (!S_ISBLK(stat_buf.st_mode)) {
735 ERR("Device '%s' (drive %c:) is not a block device - check your config\n",
736 DOSDrives[drive].device, 'A'+drive);
737 ret = -1;
738 /* reset O_NONBLOCK */
739 } else if (fcntl(fd, F_SETFL, 0) < 0 || fcntl(fd, F_GETFL) & O_NONBLOCK) {
740 ERR("fcntl() failed to reset O_NONBLOCK for device '%s' (drive %c:)\n",
741 DOSDrives[drive].device, 'A'+drive);
742 ret = -1;
744 if (ret) {
745 close(fd);
746 fd = -1;
748 } else {
749 if (!DOSDrives[drive].device)
750 ERR("No device configured for drive %c: !\n", 'A'+drive);
751 else
752 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
753 (stat(DOSDrives[drive].device, &stat_buf)) ?
754 "not available or symlink not valid ?" : "no permission");
756 if (fd == -1) {
757 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
758 return -1;
761 switch(DOSDrives[drive].type)
763 case DRIVE_REMOVABLE:
764 case DRIVE_FIXED:
765 offs = 0;
766 break;
767 case DRIVE_CDROM:
768 offs = CDROM_Data_FindBestVoldesc(fd);
769 break;
770 default:
771 offs = 0;
772 break;
775 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs))
777 ret = -4;
778 goto the_end;
780 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER)
782 ret = -2;
783 goto the_end;
786 switch(DOSDrives[drive].type)
788 case DRIVE_REMOVABLE:
789 case DRIVE_FIXED:
790 if ((buff[0x26]!=0x29) || /* Check for FAT present */
791 /* FIXME: do really all FAT have their name beginning with
792 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
793 memcmp( buff+0x36,"FAT",3))
795 ERR("The filesystem is not FAT !! (device=%s)\n",
796 DOSDrives[drive].device);
797 ret = -3;
798 goto the_end;
800 break;
801 case DRIVE_CDROM:
802 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
804 ret = -3;
805 goto the_end;
807 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
808 break;
809 default:
810 ret = -3;
811 goto the_end;
814 return close(fd);
815 the_end:
816 close(fd);
817 return ret;
821 /***********************************************************************
822 * DRIVE_WriteSuperblockEntry
824 * NOTE
825 * We are writing as little as possible (ie. not the whole SuperBlock)
826 * not to interfere with kernel. The drive can be mounted !
828 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
830 int fd;
832 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
834 ERR("Cannot open the device %s (for writing)\n",
835 DOSDrives[drive].device);
836 return -1;
838 if (lseek(fd,ofs,SEEK_SET)!=ofs)
840 ERR("lseek failed on device %s !\n",
841 DOSDrives[drive].device);
842 close(fd);
843 return -2;
845 if (write(fd,buff,len)!=len)
847 close(fd);
848 ERR("Cannot write on %s !\n",
849 DOSDrives[drive].device);
850 return -3;
852 return close (fd);
855 /******************************************************************
856 * static HANDLE CDROM_Open
860 static HANDLE CDROM_Open(int drive)
862 WCHAR root[] = {'\\','\\','.','\\','A',':',0};
863 root[4] += drive;
864 return CreateFileW(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
867 /**************************************************************************
868 * CDROM_Data_GetLabel [internal]
870 DWORD CDROM_Data_GetLabel(int drive, WCHAR *label)
872 #define LABEL_LEN 32+1
873 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
874 WORD offs = CDROM_Data_FindBestVoldesc(dev);
875 WCHAR label_read[LABEL_LEN]; /* Unicode possible, too */
876 DWORD unicode_id = 0;
878 if (offs)
880 if ((lseek(dev, offs+0x58, SEEK_SET) == offs+0x58)
881 && (read(dev, &unicode_id, 3) == 3))
883 int ver = (unicode_id & 0xff0000) >> 16;
885 if ((lseek(dev, offs+0x28, SEEK_SET) != offs+0x28)
886 || (read(dev, &label_read, LABEL_LEN) != LABEL_LEN))
887 goto failure;
889 close(dev);
890 if ((LOWORD(unicode_id) == 0x2f25) /* Unicode ID */
891 && ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
892 { /* yippee, unicode */
893 int i;
894 WORD ch;
895 for (i=0; i<LABEL_LEN;i++)
896 { /* Motorola -> Intel Unicode conversion :-\ */
897 ch = label_read[i];
898 label_read[i] = (ch << 8) | (ch >> 8);
900 strncpyW(label, label_read, 11);
901 label[11] = 0;
903 else
905 MultiByteToWideChar(DOSDrives[drive].codepage, 0, (LPSTR)label_read, -1, label, 11);
906 label[11] = '\0';
908 return 1;
911 failure:
912 close(dev);
913 ERR("error reading label !\n");
914 return 0;
917 /**************************************************************************
918 * CDROM_GetLabel [internal]
920 static DWORD CDROM_GetLabel(int drive, WCHAR *label)
922 HANDLE h;
923 CDROM_DISK_DATA cdd;
924 DWORD br, ret = 1;
925 BOOL r;
927 h = CDROM_Open(drive);
928 if( !h )
929 return 0;
930 r = DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL,
931 0, &cdd, sizeof(cdd), &br, 0);
932 CloseHandle( h );
933 if( !r )
934 return 0;
936 switch (cdd.DiskData & 0x03)
938 case CDROM_DISK_DATA_TRACK:
939 if (!CDROM_Data_GetLabel(drive, label))
940 ret = 0;
941 break;
942 case CDROM_DISK_AUDIO_TRACK:
944 static const WCHAR audioCD[] = {'A','u','d','i','o',' ','C','D',' ',' ',' ',0};
945 strcpyW(label, audioCD);
946 break;
948 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
949 FIXME("Need to get the label of a mixed mode CD!\n");
950 /* This assumes that the first track is a data track! */
951 /* I guess the correct way would be to enumerate all data tracks
952 and check each for the title */
953 if (!CDROM_Data_GetLabel(drive, label))
954 ret = 0;
955 break;
956 case 0:
957 ret = 0;
958 break;
960 TRACE("CD: label is %s\n", debugstr_w(label));
962 return ret;
964 /***********************************************************************
965 * DRIVE_GetLabel
967 LPCWSTR DRIVE_GetLabel( int drive )
969 int read = 0;
970 char buff[DRIVE_SUPER];
971 int offs = -1;
973 if (!DRIVE_IsValid( drive )) return NULL;
974 if (DOSDrives[drive].type == DRIVE_CDROM)
976 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
978 else
979 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
981 if (DRIVE_ReadSuperblock(drive,(char *) buff))
982 ERR("Invalid or unreadable superblock on %s (%c:).\n",
983 DOSDrives[drive].device, (char)(drive+'A'));
984 else {
985 if (DOSDrives[drive].type == DRIVE_REMOVABLE ||
986 DOSDrives[drive].type == DRIVE_FIXED)
987 offs = 0x2b;
989 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
990 if (offs != -1)
991 MultiByteToWideChar(DOSDrives[drive].codepage, 0, buff+offs, 11,
992 DOSDrives[drive].label_read, 11);
993 DOSDrives[drive].label_read[11]='\0';
994 read = 1;
998 return (read) ?
999 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
1002 #define CDFRAMES_PERSEC 75
1003 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
1004 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
1005 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
1007 /**************************************************************************
1008 * CDROM_Audio_GetSerial [internal]
1010 static DWORD CDROM_Audio_GetSerial(HANDLE h)
1012 unsigned long serial = 0;
1013 int i;
1014 WORD wMagic;
1015 DWORD dwStart, dwEnd, br;
1016 CDROM_TOC toc;
1018 if (!DeviceIoControl(h, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0))
1019 return 0;
1022 * wMagic collects the wFrames from track 1
1023 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
1024 * frames.
1025 * There it is collected for correcting the serial when there are less than
1026 * 3 tracks.
1028 wMagic = toc.TrackData[0].Address[2];
1029 dwStart = FRAME_OF_TOC(toc, toc.FirstTrack);
1031 for (i = 0; i <= toc.LastTrack - toc.FirstTrack; i++) {
1032 serial += (toc.TrackData[i].Address[0] << 16) |
1033 (toc.TrackData[i].Address[1] << 8) | toc.TrackData[i].Address[2];
1035 dwEnd = FRAME_OF_TOC(toc, toc.LastTrack + 1);
1037 if (toc.LastTrack - toc.FirstTrack + 1 < 3)
1038 serial += wMagic + (dwEnd - dwStart);
1040 return serial;
1043 /**************************************************************************
1044 * CDROM_Data_GetSerial [internal]
1046 static DWORD CDROM_Data_GetSerial(int drive)
1048 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
1049 WORD offs;
1050 union {
1051 unsigned long val;
1052 unsigned char p[4];
1053 } serial;
1054 BYTE b0 = 0, b1 = 1, b2 = 2, b3 = 3;
1057 if (dev == -1) return 0;
1058 offs = CDROM_Data_FindBestVoldesc(dev);
1060 serial.val = 0;
1061 if (offs)
1063 BYTE buf[2048];
1064 RTL_OSVERSIONINFOEXW ovi;
1065 int i;
1067 lseek(dev, offs, SEEK_SET);
1068 read(dev, buf, 2048);
1070 * OK, another braindead one... argh. Just believe it.
1071 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
1072 * It's true and nobody will ever be able to change it.
1074 ovi.dwOSVersionInfoSize = sizeof(ovi);
1075 RtlGetVersion(&ovi);
1076 if ((ovi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (ovi.dwMajorVersion >= 4))
1078 b0 = 3; b1 = 2; b2 = 1; b3 = 0;
1080 for (i = 0; i < 2048; i += 4)
1082 /* DON'T optimize this into DWORD !! (breaks overflow) */
1083 serial.p[b0] += buf[i+b0];
1084 serial.p[b1] += buf[i+b1];
1085 serial.p[b2] += buf[i+b2];
1086 serial.p[b3] += buf[i+b3];
1089 close(dev);
1090 return serial.val;
1093 /**************************************************************************
1094 * CDROM_GetSerial [internal]
1096 static DWORD CDROM_GetSerial(int drive)
1098 DWORD serial = 0;
1099 HANDLE h;
1100 CDROM_DISK_DATA cdd;
1101 DWORD br;
1102 BOOL r;
1104 TRACE("%d\n", drive);
1106 h = CDROM_Open(drive);
1107 if( !h )
1108 return 0;
1109 r = DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL,
1110 0, &cdd, sizeof(cdd), &br, 0);
1111 if (!r)
1113 CloseHandle(h);
1114 return 0;
1117 switch (cdd.DiskData & 0x03)
1119 case CDROM_DISK_DATA_TRACK:
1120 /* hopefully a data CD */
1121 serial = CDROM_Data_GetSerial(drive);
1122 break;
1123 case CDROM_DISK_AUDIO_TRACK:
1124 /* fall thru */
1125 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
1126 serial = CDROM_Audio_GetSerial(h);
1127 break;
1128 case 0:
1129 break;
1132 if (serial)
1133 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial), LOWORD(serial));
1135 CloseHandle(h);
1137 return serial;
1140 /***********************************************************************
1141 * DRIVE_GetSerialNumber
1143 DWORD DRIVE_GetSerialNumber( int drive )
1145 DWORD serial = 0;
1146 char buff[DRIVE_SUPER];
1148 TRACE("drive %d, type = %d\n", drive, DOSDrives[drive].type);
1150 if (!DRIVE_IsValid( drive )) return 0;
1152 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
1154 switch(DOSDrives[drive].type)
1156 case DRIVE_REMOVABLE:
1157 case DRIVE_FIXED:
1158 if (DRIVE_ReadSuperblock(drive,(char *) buff))
1159 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
1160 " Maybe not FAT?\n" ,
1161 DOSDrives[drive].device, 'A'+drive);
1162 else
1163 serial = *((DWORD*)(buff+0x27));
1164 break;
1165 case DRIVE_CDROM:
1166 serial = CDROM_GetSerial(drive);
1167 break;
1168 default:
1169 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
1173 return (serial) ? serial : DOSDrives[drive].serial_conf;
1177 /***********************************************************************
1178 * DRIVE_SetSerialNumber
1180 int DRIVE_SetSerialNumber( int drive, DWORD serial )
1182 char buff[DRIVE_SUPER];
1184 if (!DRIVE_IsValid( drive )) return 0;
1186 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
1188 if ((DOSDrives[drive].type != DRIVE_REMOVABLE) &&
1189 (DOSDrives[drive].type != DRIVE_FIXED)) return 0;
1190 /* check, if the drive has a FAT filesystem */
1191 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
1192 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
1193 return 1;
1196 if (DOSDrives[drive].type == DRIVE_CDROM) return 0;
1197 DOSDrives[drive].serial_conf = serial;
1198 return 1;
1202 /***********************************************************************
1203 * DRIVE_GetType
1205 static UINT DRIVE_GetType( int drive )
1207 if (!DRIVE_IsValid( drive )) return DRIVE_NO_ROOT_DIR;
1208 return DOSDrives[drive].type;
1212 /***********************************************************************
1213 * DRIVE_GetFlags
1215 UINT DRIVE_GetFlags( int drive )
1217 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
1218 return DOSDrives[drive].flags;
1221 /***********************************************************************
1222 * DRIVE_GetCodepage
1224 UINT DRIVE_GetCodepage( int drive )
1226 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
1227 return DOSDrives[drive].codepage;
1231 /***********************************************************************
1232 * DRIVE_Chdir
1234 int DRIVE_Chdir( int drive, LPCWSTR path )
1236 DOS_FULL_NAME full_name;
1237 WCHAR buffer[MAX_PATHNAME_LEN];
1238 LPSTR unix_cwd;
1239 BY_HANDLE_FILE_INFORMATION info;
1240 TDB *pTask = GlobalLock16(GetCurrentTask());
1242 buffer[0] = 'A' + drive;
1243 buffer[1] = ':';
1244 buffer[2] = 0;
1245 TRACE("(%s,%s)\n", debugstr_w(buffer), debugstr_w(path) );
1246 strncpyW( buffer + 2, path, MAX_PATHNAME_LEN - 2 );
1247 buffer[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1249 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
1250 if (!FILE_Stat( full_name.long_name, &info, NULL )) return 0;
1251 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1253 SetLastError( ERROR_FILE_NOT_FOUND );
1254 return 0;
1256 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
1257 while (*unix_cwd == '/') unix_cwd++;
1259 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
1260 'A' + drive, unix_cwd, debugstr_w(full_name.short_name + 3) );
1262 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
1263 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
1264 DOSDrives[drive].dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name.short_name) - 2) * sizeof(WCHAR));
1265 strcpyW(DOSDrives[drive].dos_cwd, full_name.short_name + 3);
1266 DOSDrives[drive].unix_cwd = heap_strdup( unix_cwd );
1268 if (drive == DRIVE_CurDrive)
1270 UNICODE_STRING dirW;
1272 RtlInitUnicodeString( &dirW, full_name.short_name );
1273 RtlSetCurrentDirectory_U( &dirW );
1276 if (pTask && (pTask->curdrive & 0x80) &&
1277 ((pTask->curdrive & ~0x80) == drive))
1279 WideCharToMultiByte(CP_ACP, 0, full_name.short_name + 2, -1,
1280 pTask->curdir, sizeof(pTask->curdir), NULL, NULL);
1281 DRIVE_LastTask = GetCurrentTask();
1283 return 1;
1287 /***********************************************************************
1288 * DRIVE_Disable
1290 int DRIVE_Disable( int drive )
1292 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1294 SetLastError( ERROR_INVALID_DRIVE );
1295 return 0;
1297 DOSDrives[drive].flags |= DRIVE_DISABLED;
1298 return 1;
1302 /***********************************************************************
1303 * DRIVE_Enable
1305 int DRIVE_Enable( int drive )
1307 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1309 SetLastError( ERROR_INVALID_DRIVE );
1310 return 0;
1312 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
1313 return 1;
1317 /***********************************************************************
1318 * DRIVE_SetLogicalMapping
1320 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
1322 /* If new_drive is already valid, do nothing and return 0
1323 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1325 DOSDRIVE *old, *new;
1327 old = DOSDrives + existing_drive;
1328 new = DOSDrives + new_drive;
1330 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
1331 !old->root ||
1332 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
1334 SetLastError( ERROR_INVALID_DRIVE );
1335 return 0;
1338 if ( new->root )
1340 TRACE("Can't map drive %c: to already existing drive %c:\n",
1341 'A' + existing_drive, 'A' + new_drive );
1342 /* it is already mapped there, so return success */
1343 if (!strcmp(old->root,new->root))
1344 return 1;
1345 return 0;
1348 new->root = heap_strdup( old->root );
1349 new->dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(old->dos_cwd) + 1) * sizeof(WCHAR));
1350 strcpyW(new->dos_cwd, old->dos_cwd);
1351 new->unix_cwd = heap_strdup( old->unix_cwd );
1352 new->device = heap_strdup( old->device );
1353 memcpy ( new->label_conf, old->label_conf, 12 );
1354 memcpy ( new->label_read, old->label_read, 12 );
1355 new->serial_conf = old->serial_conf;
1356 new->type = old->type;
1357 new->flags = old->flags;
1358 new->dev = old->dev;
1359 new->ino = old->ino;
1361 TRACE("Drive %c: is now equal to drive %c:\n",
1362 'A' + new_drive, 'A' + existing_drive );
1364 return 1;
1368 /***********************************************************************
1369 * DRIVE_OpenDevice
1371 * Open the drive raw device and return a Unix fd (or -1 on error).
1373 int DRIVE_OpenDevice( int drive, int flags )
1375 if (!DRIVE_IsValid( drive )) return -1;
1376 return open( DOSDrives[drive].device, flags );
1380 /***********************************************************************
1381 * DRIVE_GetFreeSpace
1383 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
1384 PULARGE_INTEGER available )
1386 struct statfs info;
1388 if (!DRIVE_IsValid(drive))
1390 SetLastError( ERROR_PATH_NOT_FOUND );
1391 return 0;
1394 /* FIXME: add autoconf check for this */
1395 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1396 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
1397 #else
1398 if (statfs( DOSDrives[drive].root, &info) < 0)
1399 #endif
1401 FILE_SetDosError();
1402 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
1403 return 0;
1406 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bsize, info.f_blocks );
1407 #ifdef HAVE_STRUCT_STATFS_F_BAVAIL
1408 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bavail, info.f_bsize );
1409 #else
1410 # ifdef HAVE_STRUCT_STATFS_F_BFREE
1411 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bfree, info.f_bsize );
1412 # else
1413 # error "statfs has no bfree/bavail member!"
1414 # endif
1415 #endif
1416 if (DOSDrives[drive].type == DRIVE_CDROM)
1417 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1418 available->QuadPart = 0;
1420 return 1;
1423 /***********************************************************************
1424 * DRIVE_GetCurrentDirectory
1425 * Returns "X:\\path\\etc\\".
1427 * Despite the API description, return required length including the
1428 * terminating null when buffer too small. This is the real behaviour.
1430 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPWSTR buf )
1432 UINT ret;
1433 LPCWSTR dos_cwd = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1434 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1436 ret = strlenW(dos_cwd) + 3; /* length of WHOLE current directory */
1437 if (ret >= buflen) return ret + 1;
1439 strcpyW( buf, driveA_rootW );
1440 buf[0] += DRIVE_GetCurrentDrive();
1441 strcatW( buf, dos_cwd );
1442 return ret;
1446 /***********************************************************************
1447 * DRIVE_BuildEnv
1449 * Build the environment array containing the drives' current directories.
1450 * Resulting pointer must be freed with HeapFree.
1452 char *DRIVE_BuildEnv(void)
1454 int i, length = 0;
1455 LPCWSTR cwd[MAX_DOS_DRIVES];
1456 char *env, *p;
1458 for (i = 0; i < MAX_DOS_DRIVES; i++)
1460 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0])
1461 length += WideCharToMultiByte(DRIVE_GetCodepage(i), 0,
1462 cwd[i], -1, NULL, 0, NULL, NULL) + 7;
1464 if (!(env = HeapAlloc( GetProcessHeap(), 0, length+1 ))) return NULL;
1465 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
1467 if (cwd[i] && cwd[i][0])
1469 *p++ = '='; *p++ = 'A' + i; *p++ = ':';
1470 *p++ = '='; *p++ = 'A' + i; *p++ = ':'; *p++ = '\\';
1471 WideCharToMultiByte(DRIVE_GetCodepage(i), 0, cwd[i], -1, p, 0x7fffffff, NULL, NULL);
1472 p += strlen(p) + 1;
1475 *p = 0;
1476 return env;
1480 /***********************************************************************
1481 * GetDiskFreeSpace (KERNEL.422)
1483 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
1484 LPDWORD sector_bytes, LPDWORD free_clusters,
1485 LPDWORD total_clusters )
1487 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
1488 free_clusters, total_clusters );
1492 /***********************************************************************
1493 * GetDiskFreeSpaceW (KERNEL32.@)
1495 * Fails if expression resulting from current drive's dir and "root"
1496 * is not a root dir of the target drive.
1498 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1499 * if the corresponding info is unneeded.
1501 * FIXME: needs to support UNC names from Win95 OSR2 on.
1503 * Behaviour under Win95a:
1504 * CurrDir root result
1505 * "E:\\TEST" "E:" FALSE
1506 * "E:\\" "E:" TRUE
1507 * "E:\\" "E" FALSE
1508 * "E:\\" "\\" TRUE
1509 * "E:\\TEST" "\\" TRUE
1510 * "E:\\TEST" ":\\" FALSE
1511 * "E:\\TEST" "E:\\" TRUE
1512 * "E:\\TEST" "" FALSE
1513 * "E:\\" "" FALSE (!)
1514 * "E:\\" 0x0 TRUE
1515 * "E:\\TEST" 0x0 TRUE (!)
1516 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1517 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1519 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1520 LPDWORD sector_bytes, LPDWORD free_clusters,
1521 LPDWORD total_clusters )
1523 int drive, sec_size;
1524 ULARGE_INTEGER size,available;
1525 LPCWSTR path;
1526 DWORD cluster_sec;
1528 TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root), cluster_sectors, sector_bytes,
1529 free_clusters, total_clusters);
1531 if (!root || root[0] == '\\' || root[0] == '/')
1532 drive = DRIVE_GetCurrentDrive();
1533 else
1534 if (root[0] && root[1] == ':') /* root contains drive tag */
1536 drive = toupperW(root[0]) - 'A';
1537 path = &root[2];
1538 if (path[0] == '\0')
1540 path = DRIVE_GetDosCwd(drive);
1541 if (!path)
1543 SetLastError(ERROR_PATH_NOT_FOUND);
1544 return FALSE;
1547 else
1548 if (path[0] == '\\')
1549 path++;
1551 if (path[0]) /* oops, we are in a subdir */
1553 SetLastError(ERROR_INVALID_NAME);
1554 return FALSE;
1557 else
1559 if (!root[0])
1560 SetLastError(ERROR_PATH_NOT_FOUND);
1561 else
1562 SetLastError(ERROR_INVALID_NAME);
1563 return FALSE;
1566 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1568 /* Cap the size and available at 2GB as per specs. */
1569 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1571 size.s.HighPart = 0;
1572 size.s.LowPart = 0x7fffffff;
1574 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1576 available.s.HighPart =0;
1577 available.s.LowPart = 0x7fffffff;
1579 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
1580 size.s.LowPart /= sec_size;
1581 available.s.LowPart /= sec_size;
1582 /* FIXME: probably have to adjust those variables too for CDFS */
1583 cluster_sec = 1;
1584 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1586 if (cluster_sectors)
1587 *cluster_sectors = cluster_sec;
1588 if (sector_bytes)
1589 *sector_bytes = sec_size;
1590 if (free_clusters)
1591 *free_clusters = available.s.LowPart / cluster_sec;
1592 if (total_clusters)
1593 *total_clusters = size.s.LowPart / cluster_sec;
1594 return TRUE;
1598 /***********************************************************************
1599 * GetDiskFreeSpaceA (KERNEL32.@)
1601 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1602 LPDWORD sector_bytes, LPDWORD free_clusters,
1603 LPDWORD total_clusters )
1605 UNICODE_STRING rootW;
1606 BOOL ret = FALSE;
1608 if (root)
1610 if(!RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1612 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1613 return FALSE;
1616 else
1617 rootW.Buffer = NULL;
1619 ret = GetDiskFreeSpaceW(rootW.Buffer, cluster_sectors, sector_bytes,
1620 free_clusters, total_clusters );
1621 RtlFreeUnicodeString(&rootW);
1623 return ret;
1627 /***********************************************************************
1628 * GetDiskFreeSpaceExW (KERNEL32.@)
1630 * This function is used to acquire the size of the available and
1631 * total space on a logical volume.
1633 * RETURNS
1635 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1636 * detailed error information.
1639 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root,
1640 PULARGE_INTEGER avail,
1641 PULARGE_INTEGER total,
1642 PULARGE_INTEGER totalfree)
1644 int drive;
1645 ULARGE_INTEGER size,available;
1647 if (!root) drive = DRIVE_GetCurrentDrive();
1648 else
1649 { /* C: always works for GetDiskFreeSpaceEx */
1650 if ((root[1]) && ((root[1] != ':') || (root[2] && root[2] != '\\')))
1652 FIXME("there are valid root names which are not supported yet\n");
1653 /* ..like UNC names, for instance. */
1655 WARN("invalid root '%s'\n", debugstr_w(root));
1656 return FALSE;
1658 drive = toupperW(root[0]) - 'A';
1661 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1663 if (total)
1665 total->s.HighPart = size.s.HighPart;
1666 total->s.LowPart = size.s.LowPart;
1669 if (totalfree)
1671 totalfree->s.HighPart = available.s.HighPart;
1672 totalfree->s.LowPart = available.s.LowPart;
1675 if (avail)
1677 if (FIXME_ON(dosfs))
1679 /* On Windows2000, we need to check the disk quota
1680 allocated for the user owning the calling process. We
1681 don't want to be more obtrusive than necessary with the
1682 FIXME messages, so don't print the FIXME unless Wine is
1683 actually masquerading as Windows2000. */
1685 RTL_OSVERSIONINFOEXW ovi;
1686 ovi.dwOSVersionInfoSize = sizeof(ovi);
1687 if (RtlGetVersion(&ovi))
1689 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1690 FIXME("no per-user quota support yet\n");
1694 /* Quick hack, should eventually be fixed to work 100% with
1695 Windows2000 (see comment above). */
1696 avail->s.HighPart = available.s.HighPart;
1697 avail->s.LowPart = available.s.LowPart;
1700 return TRUE;
1703 /***********************************************************************
1704 * GetDiskFreeSpaceExA (KERNEL32.@)
1706 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root, PULARGE_INTEGER avail,
1707 PULARGE_INTEGER total,
1708 PULARGE_INTEGER totalfree)
1710 UNICODE_STRING rootW;
1711 BOOL ret;
1713 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
1714 else rootW.Buffer = NULL;
1716 ret = GetDiskFreeSpaceExW( rootW.Buffer, avail, total, totalfree);
1718 RtlFreeUnicodeString(&rootW);
1719 return ret;
1722 /***********************************************************************
1723 * GetDriveType (KERNEL.136)
1724 * This function returns the type of a drive in Win16.
1725 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1726 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1727 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1728 * do any pseudo-clever changes.
1730 * RETURNS
1731 * drivetype DRIVE_xxx
1733 UINT16 WINAPI GetDriveType16( UINT16 drive ) /* [in] number (NOT letter) of drive */
1735 UINT type = DRIVE_GetType(drive);
1736 TRACE("(%c:)\n", 'A' + drive );
1737 if (type == DRIVE_CDROM) type = DRIVE_REMOTE;
1738 return type;
1742 /***********************************************************************
1743 * GetDriveTypeW (KERNEL32.@)
1745 * Returns the type of the disk drive specified. If root is NULL the
1746 * root of the current directory is used.
1748 * RETURNS
1750 * Type of drive (from Win32 SDK):
1752 * DRIVE_UNKNOWN unable to find out anything about the drive
1753 * DRIVE_NO_ROOT_DIR nonexistent root dir
1754 * DRIVE_REMOVABLE the disk can be removed from the machine
1755 * DRIVE_FIXED the disk can not be removed from the machine
1756 * DRIVE_REMOTE network disk
1757 * DRIVE_CDROM CDROM drive
1758 * DRIVE_RAMDISK virtual disk in RAM
1760 UINT WINAPI GetDriveTypeW(LPCWSTR root) /* [in] String describing drive */
1762 int drive;
1763 TRACE("(%s)\n", debugstr_w(root));
1765 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1766 else
1768 if ((root[1]) && (root[1] != ':'))
1770 WARN("invalid root %s\n", debugstr_w(root));
1771 return DRIVE_NO_ROOT_DIR;
1773 drive = toupperW(root[0]) - 'A';
1775 return DRIVE_GetType(drive);
1779 /***********************************************************************
1780 * GetDriveTypeA (KERNEL32.@)
1782 UINT WINAPI GetDriveTypeA( LPCSTR root )
1784 UNICODE_STRING rootW;
1785 UINT ret = 0;
1787 if (root)
1789 if( !RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1791 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1792 return 0;
1795 else
1796 rootW.Buffer = NULL;
1798 ret = GetDriveTypeW(rootW.Buffer);
1800 RtlFreeUnicodeString(&rootW);
1801 return ret;
1806 /***********************************************************************
1807 * GetCurrentDirectory (KERNEL.411)
1809 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1811 WCHAR cur_dirW[MAX_PATH];
1813 DRIVE_GetCurrentDirectory(MAX_PATH, cur_dirW);
1814 return (UINT16)WideCharToMultiByte(CP_ACP, 0, cur_dirW, -1, buf, buflen, NULL, NULL);
1818 /***********************************************************************
1819 * GetCurrentDirectoryW (KERNEL32.@)
1821 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1823 UINT ret;
1824 WCHAR longname[MAX_PATHNAME_LEN];
1825 WCHAR shortname[MAX_PATHNAME_LEN];
1827 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1828 if ( ret > MAX_PATHNAME_LEN ) {
1829 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1830 return ret;
1832 GetLongPathNameW(shortname, longname, MAX_PATHNAME_LEN);
1833 ret = strlenW( longname ) + 1;
1834 if (ret > buflen) return ret;
1835 strcpyW(buf, longname);
1836 return ret - 1;
1839 /***********************************************************************
1840 * GetCurrentDirectoryA (KERNEL32.@)
1842 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1844 WCHAR bufferW[MAX_PATH];
1845 DWORD ret, retW;
1847 retW = GetCurrentDirectoryW(MAX_PATH, bufferW);
1849 if (!retW)
1850 ret = 0;
1851 else if (retW > MAX_PATH)
1853 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1854 ret = 0;
1856 else
1858 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1859 if (buflen >= ret)
1861 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buf, buflen, NULL, NULL);
1862 ret--; /* length without 0 */
1865 return ret;
1869 /***********************************************************************
1870 * SetCurrentDirectory (KERNEL.412)
1872 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1874 return SetCurrentDirectoryA( dir );
1878 /***********************************************************************
1879 * SetCurrentDirectoryW (KERNEL32.@)
1881 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dir )
1883 int drive, olddrive = DRIVE_GetCurrentDrive();
1885 if (!dir)
1887 SetLastError(ERROR_INVALID_PARAMETER);
1888 return FALSE;
1890 if (dir[0] && (dir[1]==':'))
1892 drive = toupperW( *dir ) - 'A';
1893 dir += 2;
1895 else
1896 drive = olddrive;
1898 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1899 sets pTask->curdir only if pTask->curdrive is drive */
1900 if (!(DRIVE_SetCurrentDrive( drive )))
1901 return FALSE;
1903 /* FIXME: what about empty strings? Add a \\ ? */
1904 if (!DRIVE_Chdir( drive, dir )) {
1905 DRIVE_SetCurrentDrive(olddrive);
1906 return FALSE;
1908 return TRUE;
1912 /***********************************************************************
1913 * SetCurrentDirectoryA (KERNEL32.@)
1915 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1917 UNICODE_STRING dirW;
1918 BOOL ret = FALSE;
1920 if (!dir)
1922 SetLastError(ERROR_INVALID_PARAMETER);
1923 return FALSE;
1926 if (RtlCreateUnicodeStringFromAsciiz(&dirW, dir))
1928 ret = SetCurrentDirectoryW(dirW.Buffer);
1929 RtlFreeUnicodeString(&dirW);
1931 else
1932 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1933 return ret;
1937 /***********************************************************************
1938 * GetLogicalDriveStringsA (KERNEL32.@)
1940 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1942 int drive, count;
1944 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1945 if (DRIVE_IsValid(drive)) count++;
1946 if ((count * 4) + 1 <= len)
1948 LPSTR p = buffer;
1949 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1950 if (DRIVE_IsValid(drive))
1952 *p++ = 'a' + drive;
1953 *p++ = ':';
1954 *p++ = '\\';
1955 *p++ = '\0';
1957 *p = '\0';
1958 return count * 4;
1960 else
1961 return (count * 4) + 1; /* account for terminating null */
1962 /* The API tells about these different return values */
1966 /***********************************************************************
1967 * GetLogicalDriveStringsW (KERNEL32.@)
1969 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1971 int drive, count;
1973 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1974 if (DRIVE_IsValid(drive)) count++;
1975 if (count * 4 * sizeof(WCHAR) <= len)
1977 LPWSTR p = buffer;
1978 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1979 if (DRIVE_IsValid(drive))
1981 *p++ = (WCHAR)('a' + drive);
1982 *p++ = (WCHAR)':';
1983 *p++ = (WCHAR)'\\';
1984 *p++ = (WCHAR)'\0';
1986 *p = (WCHAR)'\0';
1988 return count * 4 * sizeof(WCHAR);
1992 /***********************************************************************
1993 * GetLogicalDrives (KERNEL32.@)
1995 DWORD WINAPI GetLogicalDrives(void)
1997 DWORD ret = 0;
1998 int drive;
2000 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
2002 if ( (DRIVE_IsValid(drive)) ||
2003 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
2004 ret |= (1 << drive);
2006 return ret;
2010 /***********************************************************************
2011 * GetVolumeInformationW (KERNEL32.@)
2013 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
2014 DWORD label_len, DWORD *serial,
2015 DWORD *filename_len, DWORD *flags,
2016 LPWSTR fsname, DWORD fsname_len )
2018 int drive;
2019 LPWSTR cp;
2021 /* FIXME, SetLastError()s missing */
2023 if (!root) drive = DRIVE_GetCurrentDrive();
2024 else
2026 if (root[0] && root[1] != ':')
2028 WARN("invalid root %s\n", debugstr_w(root));
2029 return FALSE;
2031 drive = toupperW(root[0]) - 'A';
2033 if (!DRIVE_IsValid( drive )) return FALSE;
2034 if (label && label_len)
2036 strncpyW( label, DRIVE_GetLabel(drive), label_len );
2037 label[label_len - 1] = 0; /* ensure 0 termination */
2038 cp = label + strlenW(label);
2039 while (cp != label && *(cp-1) == ' ') cp--;
2040 *cp = '\0';
2042 if (serial) *serial = DRIVE_GetSerialNumber(drive);
2044 /* Set the filesystem information */
2045 /* Note: we only emulate a FAT fs at present */
2047 if (filename_len) {
2048 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
2049 *filename_len = 12;
2050 else
2051 *filename_len = 255;
2053 if (flags)
2055 *flags=0;
2056 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
2057 *flags|=FS_CASE_SENSITIVE;
2058 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
2059 *flags|=FS_CASE_IS_PRESERVED;
2061 if (fsname && fsname_len)
2063 /* Diablo checks that return code ... */
2064 if (DOSDrives[drive].type == DRIVE_CDROM)
2066 static const WCHAR cdfsW[] = {'C','D','F','S',0};
2067 strncpyW( fsname, cdfsW, fsname_len );
2069 else
2071 static const WCHAR fatW[] = {'F','A','T',0};
2072 strncpyW( fsname, fatW, fsname_len );
2074 fsname[fsname_len - 1] = 0; /* ensure 0 termination */
2076 return TRUE;
2080 /***********************************************************************
2081 * GetVolumeInformationA (KERNEL32.@)
2083 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
2084 DWORD label_len, DWORD *serial,
2085 DWORD *filename_len, DWORD *flags,
2086 LPSTR fsname, DWORD fsname_len )
2088 UNICODE_STRING rootW;
2089 LPWSTR labelW, fsnameW;
2090 BOOL ret;
2092 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
2093 else rootW.Buffer = NULL;
2094 labelW = label ? HeapAlloc(GetProcessHeap(), 0, label_len * sizeof(WCHAR)) : NULL;
2095 fsnameW = fsname ? HeapAlloc(GetProcessHeap(), 0, fsname_len * sizeof(WCHAR)) : NULL;
2097 if ((ret = GetVolumeInformationW(rootW.Buffer, labelW, label_len, serial,
2098 filename_len, flags, fsnameW, fsname_len)))
2100 if (label) WideCharToMultiByte(CP_ACP, 0, labelW, -1, label, label_len, NULL, NULL);
2101 if (fsname) WideCharToMultiByte(CP_ACP, 0, fsnameW, -1, fsname, fsname_len, NULL, NULL);
2104 RtlFreeUnicodeString(&rootW);
2105 if (labelW) HeapFree( GetProcessHeap(), 0, labelW );
2106 if (fsnameW) HeapFree( GetProcessHeap(), 0, fsnameW );
2107 return ret;
2110 /***********************************************************************
2111 * SetVolumeLabelW (KERNEL32.@)
2113 BOOL WINAPI SetVolumeLabelW( LPCWSTR root, LPCWSTR volname )
2115 int drive;
2117 /* FIXME, SetLastErrors missing */
2119 if (!root) drive = DRIVE_GetCurrentDrive();
2120 else
2122 if ((root[1]) && (root[1] != ':'))
2124 WARN("invalid root %s\n", debugstr_w(root));
2125 return FALSE;
2127 drive = toupperW(root[0]) - 'A';
2129 if (!DRIVE_IsValid( drive )) return FALSE;
2131 /* some copy protection stuff check this */
2132 if (DOSDrives[drive].type == DRIVE_CDROM) return FALSE;
2134 strncpyW(DOSDrives[drive].label_conf, volname, 12);
2135 DOSDrives[drive].label_conf[12 - 1] = 0; /* ensure 0 termination */
2136 return TRUE;
2139 /***********************************************************************
2140 * SetVolumeLabelA (KERNEL32.@)
2142 BOOL WINAPI SetVolumeLabelA(LPCSTR root, LPCSTR volname)
2144 UNICODE_STRING rootW, volnameW;
2145 BOOL ret;
2147 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
2148 else rootW.Buffer = NULL;
2149 if (volname) RtlCreateUnicodeStringFromAsciiz(&volnameW, volname);
2150 else volnameW.Buffer = NULL;
2152 ret = SetVolumeLabelW( rootW.Buffer, volnameW.Buffer );
2154 RtlFreeUnicodeString(&rootW);
2155 RtlFreeUnicodeString(&volnameW);
2156 return ret;
2159 /***********************************************************************
2160 * GetVolumeNameForVolumeMountPointW (KERNEL32.@)
2162 DWORD WINAPI GetVolumeNameForVolumeMountPointW(LPWSTR str, DWORD a, DWORD b)
2164 FIXME("(%s, %lx, %lx): stub\n", debugstr_w(str), a, b);
2165 return 0;