Use statvfs instead of statfs, and provide a default implementation in
[wine/multimedia.git] / files / drive.c
blobaf8f70c4e716ea86aa3680eb07b899c89c5785f1
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 HAVE_SYS_STATVFS_H
47 # include <sys/statvfs.h>
48 #endif
49 #ifdef STATFS_DEFINED_BY_SYS_VFS
50 # include <sys/vfs.h>
51 #else
52 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
53 # include <sys/mount.h>
54 # else
55 # ifdef STATFS_DEFINED_BY_SYS_STATFS
56 # include <sys/statfs.h>
57 # endif
58 # endif
59 #endif
61 #define NONAMELESSUNION
62 #define NONAMELESSSTRUCT
63 #include "ntstatus.h"
64 #include "windef.h"
65 #include "winbase.h"
66 #include "winreg.h"
67 #include "winternl.h"
68 #include "wine/winbase16.h" /* for GetCurrentTask */
69 #include "winerror.h"
70 #include "winioctl.h"
71 #include "ntddstor.h"
72 #include "ntddcdrm.h"
73 #include "drive.h"
74 #include "file.h"
75 #include "wine/unicode.h"
76 #include "wine/library.h"
77 #include "wine/server.h"
78 #include "wine/debug.h"
80 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
81 WINE_DECLARE_DEBUG_CHANNEL(file);
83 typedef struct
85 char *root; /* root dir in Unix format without trailing / */
86 LPWSTR dos_cwd; /* cwd in DOS format without leading or trailing \ */
87 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
88 char *device; /* raw device path */
89 WCHAR label_conf[12]; /* drive label as cfg'd in wine config */
90 WCHAR label_read[12]; /* drive label as read from device */
91 DWORD serial_conf; /* drive serial number as cfg'd in wine config */
92 UINT type; /* drive type */
93 UINT flags; /* drive flags */
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 LabelW[] = {'L','a','b','e','l',0};
203 static const WCHAR SerialW[] = {'S','e','r','i','a','l',0};
204 static const WCHAR TypeW[] = {'T','y','p','e',0};
205 static const WCHAR FilesystemW[] = {'F','i','l','e','s','y','s','t','e','m',0};
206 static const WCHAR DeviceW[] = {'D','e','v','i','c','e',0};
207 static const WCHAR ReadVolInfoW[] = {'R','e','a','d','V','o','l','I','n','f','o',0};
208 static const WCHAR FailReadOnlyW[] = {'F','a','i','l','R','e','a','d','O','n','l','y',0};
209 static const WCHAR driveC_labelW[] = {'D','r','i','v','e',' ','C',' ',' ',' ',' ',0};
212 attr.Length = sizeof(attr);
213 attr.RootDirectory = 0;
214 attr.ObjectName = &nameW;
215 attr.Attributes = 0;
216 attr.SecurityDescriptor = NULL;
217 attr.SecurityQualityOfService = NULL;
219 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
221 RtlInitUnicodeString( &nameW, driveW );
222 nameW.Buffer[(nameW.Length / sizeof(WCHAR)) - 1] = 'A' + i;
223 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ) != STATUS_SUCCESS) continue;
225 /* Get the root path */
226 RtlInitUnicodeString( &nameW, PathW );
227 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
229 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
230 ExpandEnvironmentStringsW( data, path, sizeof(path)/sizeof(WCHAR) );
232 p = path + strlenW(path) - 1;
233 while ((p > path) && (*p == '/')) *p-- = '\0';
235 if (path[0] == '/')
237 len = WideCharToMultiByte(CP_UNIXCP, 0, path, -1, NULL, 0, NULL, NULL);
238 drive->root = HeapAlloc(GetProcessHeap(), 0, len);
239 WideCharToMultiByte(CP_UNIXCP, 0, path, -1, drive->root, len, NULL, NULL);
241 else
243 /* relative paths are relative to config dir */
244 const char *config = wine_get_config_dir();
245 len = strlen(config);
246 len += WideCharToMultiByte(CP_UNIXCP, 0, path, -1, NULL, 0, NULL, NULL) + 2;
247 drive->root = HeapAlloc( GetProcessHeap(), 0, len );
248 len -= sprintf( drive->root, "%s/", config );
249 WideCharToMultiByte(CP_UNIXCP, 0, path, -1,
250 drive->root + strlen(drive->root), len, NULL, NULL);
253 if (stat( drive->root, &drive_stat_buffer ))
255 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
256 drive->root, strerror(errno), 'A' + i);
257 HeapFree( GetProcessHeap(), 0, drive->root );
258 drive->root = NULL;
259 goto next;
261 if (!S_ISDIR(drive_stat_buffer.st_mode))
263 MESSAGE("%s is not a directory, ignoring drive %c:\n",
264 drive->root, 'A' + i );
265 HeapFree( GetProcessHeap(), 0, drive->root );
266 drive->root = NULL;
267 goto next;
270 drive->dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(drive->dos_cwd[0]));
271 drive->unix_cwd = heap_strdup( "" );
272 drive->device = NULL;
273 drive->flags = 0;
274 drive->dev = drive_stat_buffer.st_dev;
275 drive->ino = drive_stat_buffer.st_ino;
277 /* Get the drive type */
278 RtlInitUnicodeString( &nameW, TypeW );
279 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
281 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
282 drive->type = DRIVE_GetDriveType( i, data );
284 else drive->type = DRIVE_FIXED;
286 /* Get the drive label */
287 RtlInitUnicodeString( &nameW, LabelW );
288 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
290 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
291 lstrcpynW( drive->label_conf, data, 12 );
293 if ((len = strlenW(drive->label_conf)) < 11)
295 /* Pad label with spaces */
296 while(len < 11) drive->label_conf[len++] = ' ';
297 drive->label_conf[11] = '\0';
300 /* Get the serial number */
301 RtlInitUnicodeString( &nameW, SerialW );
302 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
304 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
305 drive->serial_conf = strtoulW( data, NULL, 16 );
307 else drive->serial_conf = 12345678;
309 /* Get the filesystem type */
310 RtlInitUnicodeString( &nameW, FilesystemW );
311 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
313 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
314 drive->flags = DRIVE_GetFSFlags( i, data );
316 else drive->flags = DRIVE_CASE_PRESERVING;
318 /* Get the device */
319 RtlInitUnicodeString( &nameW, DeviceW );
320 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
322 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
323 len = WideCharToMultiByte(CP_UNIXCP, 0, data, -1, NULL, 0, NULL, NULL);
324 drive->device = HeapAlloc(GetProcessHeap(), 0, len);
325 WideCharToMultiByte(CP_UNIXCP, 0, data, -1, drive->device, len, NULL, NULL);
327 RtlInitUnicodeString( &nameW, ReadVolInfoW );
328 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
330 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
331 if (IS_OPTION_TRUE(data[0])) drive->flags |= DRIVE_READ_VOL_INFO;
333 else drive->flags |= DRIVE_READ_VOL_INFO;
335 if (drive->type == DRIVE_CDROM)
337 int cd_fd;
338 if ((cd_fd = open(drive->device, O_RDONLY|O_NONBLOCK)) != -1)
340 CDROM_InitRegistry(cd_fd);
341 close(cd_fd);
346 /* Get the FailReadOnly flag */
347 RtlInitUnicodeString( &nameW, FailReadOnlyW );
348 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
350 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
351 if (IS_OPTION_TRUE(data[0])) drive->flags |= DRIVE_FAIL_READ_ONLY;
354 /* Make the first hard disk the current drive */
355 if ((DRIVE_CurDrive == -1) && (drive->type == DRIVE_FIXED))
356 DRIVE_CurDrive = i;
358 count++;
359 TRACE("Drive %c: path=%s type=%s label=%s serial=%08lx "
360 "flags=%08x dev=%x ino=%x\n",
361 'A' + i, drive->root, debugstr_w(DRIVE_Types[drive->type]),
362 debugstr_w(drive->label_conf), drive->serial_conf, drive->flags,
363 (int)drive->dev, (int)drive->ino );
366 next:
367 NtClose( hkey );
370 if (!count)
372 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
373 /* Create a C drive pointing to Unix root dir */
374 DOSDrives[2].root = heap_strdup( "/" );
375 DOSDrives[2].dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DOSDrives[2].dos_cwd[0]));
376 DOSDrives[2].unix_cwd = heap_strdup( "" );
377 strcpyW( DOSDrives[2].label_conf, driveC_labelW );
378 DOSDrives[2].serial_conf = 12345678;
379 DOSDrives[2].type = DRIVE_FIXED;
380 DOSDrives[2].device = NULL;
381 DOSDrives[2].flags = 0;
382 DRIVE_CurDrive = 2;
385 /* Make sure the current drive is valid */
386 if (DRIVE_CurDrive == -1)
388 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
390 if (drive->root && !(drive->flags & DRIVE_DISABLED))
392 DRIVE_CurDrive = i;
393 break;
398 /* get current working directory info for all drives */
399 for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
401 if (!GetEnvironmentVariableW(drive_env, path, MAX_PATHNAME_LEN)) continue;
402 /* sanity check */
403 if (toupperW(path[0]) != drive_env[1] || path[1] != ':') continue;
404 DRIVE_Chdir( i, path + 2 );
406 return 1;
410 /***********************************************************************
411 * DRIVE_IsValid
413 int DRIVE_IsValid( int drive )
415 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
416 return (DOSDrives[drive].root &&
417 !(DOSDrives[drive].flags & DRIVE_DISABLED));
421 /***********************************************************************
422 * DRIVE_GetCurrentDrive
424 int DRIVE_GetCurrentDrive(void)
426 TDB *pTask = GlobalLock16(GetCurrentTask());
427 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
428 return DRIVE_CurDrive;
432 /***********************************************************************
433 * DRIVE_SetCurrentDrive
435 int DRIVE_SetCurrentDrive( int drive )
437 TDB *pTask = GlobalLock16(GetCurrentTask());
438 if (!DRIVE_IsValid( drive ))
440 SetLastError( ERROR_INVALID_DRIVE );
441 return 0;
443 TRACE("%c:\n", 'A' + drive );
444 DRIVE_CurDrive = drive;
445 if (pTask) pTask->curdrive = drive | 0x80;
446 return 1;
450 /***********************************************************************
451 * DRIVE_FindDriveRoot
453 * Find a drive for which the root matches the beginning of the given path.
454 * This can be used to translate a Unix path into a drive + DOS path.
455 * Return value is the drive, or -1 on error. On success, path is modified
456 * to point to the beginning of the DOS path.
458 * Note: path must be in the encoding of the underlying Unix file system.
460 int DRIVE_FindDriveRoot( const char **path )
462 /* Starting with the full path, check if the device and inode match any of
463 * the wine 'drives'. If not then remove the last path component and try
464 * again. If the last component was a '..' then skip a normal component
465 * since it's a directory that's ascended back out of.
467 int drive, level, len;
468 char buffer[MAX_PATHNAME_LEN];
469 char *p;
470 struct stat st;
472 strcpy( buffer, *path );
473 for (p = buffer; *p; p++) if (*p == '\\') *p = '/';
474 len = p - buffer;
476 /* strip off trailing slashes */
477 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
479 for (;;)
481 /* Find the drive */
482 if (stat( buffer, &st ) == 0 && S_ISDIR( st.st_mode ))
484 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
486 if (!DOSDrives[drive].root ||
487 (DOSDrives[drive].flags & DRIVE_DISABLED))
488 continue;
490 if ((DOSDrives[drive].dev == st.st_dev) &&
491 (DOSDrives[drive].ino == st.st_ino))
493 if (len == 1) len = 0; /* preserve root slash in returned path */
494 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
495 *path, 'A' + drive, buffer, *path + len);
496 *path += len;
497 if (!**path) *path = "\\";
498 return drive;
502 if (len <= 1) return -1; /* reached root */
504 level = 0;
505 while (level < 1)
507 /* find start of the last path component */
508 while (len > 1 && buffer[len - 1] != '/') len--;
509 if (!buffer[len]) break; /* empty component -> reached root */
510 /* does removing it take us up a level? */
511 if (strcmp( buffer + len, "." ) != 0)
512 level += strcmp( buffer + len, ".." ) ? 1 : -1;
513 buffer[len] = 0;
514 /* strip off trailing slashes */
515 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
521 /***********************************************************************
522 * DRIVE_FindDriveRootW
524 * Unicode version of DRIVE_FindDriveRoot.
526 int DRIVE_FindDriveRootW( LPCWSTR *path )
528 int drive, level, len;
529 WCHAR buffer[MAX_PATHNAME_LEN];
530 WCHAR *p;
531 struct stat st;
533 strcpyW( buffer, *path );
534 for (p = buffer; *p; p++) if (*p == '\\') *p = '/';
535 len = p - buffer;
537 /* strip off trailing slashes */
538 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
540 for (;;)
542 char buffA[MAX_PATHNAME_LEN];
544 WideCharToMultiByte( CP_UNIXCP, 0, buffer, -1, buffA, sizeof(buffA), NULL, NULL );
545 if (stat( buffA, &st ) == 0 && S_ISDIR( st.st_mode ))
547 /* Find the drive */
548 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
550 if (!DOSDrives[drive].root ||
551 (DOSDrives[drive].flags & DRIVE_DISABLED))
552 continue;
554 if ((DOSDrives[drive].dev == st.st_dev) &&
555 (DOSDrives[drive].ino == st.st_ino))
557 static const WCHAR rootW[] = {'\\',0};
559 if (len == 1) len = 0; /* preserve root slash in returned path */
560 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
561 debugstr_w(*path), 'A' + drive, debugstr_w(buffer), debugstr_w(*path + len));
562 *path += len;
563 if (!**path) *path = rootW;
564 return drive;
568 if (len <= 1) return -1; /* reached root */
570 level = 0;
571 while (level < 1)
573 static const WCHAR dotW[] = {'.',0};
574 static const WCHAR dotdotW[] = {'.','.',0};
576 /* find start of the last path component */
577 while (len > 1 && buffer[len - 1] != '/') len--;
578 if (!buffer[len]) break; /* empty component -> reached root */
579 /* does removing it take us up a level? */
580 if (strcmpW( buffer + len, dotW ) != 0)
581 level += strcmpW( buffer + len, dotdotW ) ? 1 : -1;
582 buffer[len] = 0;
583 /* strip off trailing slashes */
584 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
590 /***********************************************************************
591 * DRIVE_GetRoot
593 const char * DRIVE_GetRoot( int drive )
595 if (!DRIVE_IsValid( drive )) return NULL;
596 return DOSDrives[drive].root;
600 /***********************************************************************
601 * DRIVE_GetDosCwd
603 LPCWSTR DRIVE_GetDosCwd( int drive )
605 TDB *pTask = GlobalLock16(GetCurrentTask());
606 if (!DRIVE_IsValid( drive )) return NULL;
608 /* Check if we need to change the directory to the new task. */
609 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
610 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
611 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
613 static const WCHAR rootW[] = {'\\',0};
614 WCHAR curdirW[MAX_PATH];
615 MultiByteToWideChar(CP_ACP, 0, pTask->curdir, -1, curdirW, MAX_PATH);
616 /* Perform the task-switch */
617 if (!DRIVE_Chdir( drive, curdirW )) DRIVE_Chdir( drive, rootW );
618 DRIVE_LastTask = GetCurrentTask();
620 return DOSDrives[drive].dos_cwd;
624 /***********************************************************************
625 * DRIVE_GetUnixCwd
627 const char * DRIVE_GetUnixCwd( int drive )
629 TDB *pTask = GlobalLock16(GetCurrentTask());
630 if (!DRIVE_IsValid( drive )) return NULL;
632 /* Check if we need to change the directory to the new task. */
633 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
634 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
635 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
637 static const WCHAR rootW[] = {'\\',0};
638 WCHAR curdirW[MAX_PATH];
639 MultiByteToWideChar(CP_ACP, 0, pTask->curdir, -1, curdirW, MAX_PATH);
640 /* Perform the task-switch */
641 if (!DRIVE_Chdir( drive, curdirW )) DRIVE_Chdir( drive, rootW );
642 DRIVE_LastTask = GetCurrentTask();
644 return DOSDrives[drive].unix_cwd;
648 /***********************************************************************
649 * DRIVE_GetDevice
651 const char * DRIVE_GetDevice( int drive )
653 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
656 /******************************************************************
657 * static WORD CDROM_Data_FindBestVoldesc
661 static WORD CDROM_Data_FindBestVoldesc(int fd)
663 BYTE cur_vd_type, max_vd_type = 0;
664 unsigned int offs, best_offs = 0, extra_offs = 0;
665 char sig[3];
667 for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
669 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
670 * the volume label is displaced forward by 8
672 lseek(fd, offs + 11, SEEK_SET); /* check for non-ISO9660 signature */
673 read(fd, &sig, 3);
674 if ((sig[0] == 'R') && (sig[1] == 'O') && (sig[2]=='M'))
676 extra_offs = 8;
678 lseek(fd, offs + extra_offs, SEEK_SET);
679 read(fd, &cur_vd_type, 1);
680 if (cur_vd_type == 0xff) /* voldesc set terminator */
681 break;
682 if (cur_vd_type > max_vd_type)
684 max_vd_type = cur_vd_type;
685 best_offs = offs + extra_offs;
688 return best_offs;
691 /***********************************************************************
692 * DRIVE_ReadSuperblock
694 * NOTE
695 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
696 * to check, that they are writing on a FAT filesystem !
698 int DRIVE_ReadSuperblock (int drive, char * buff)
700 #define DRIVE_SUPER 96
701 int fd;
702 off_t offs;
703 int ret = 0;
704 struct stat stat_buf;
706 memset(buff, 0, DRIVE_SUPER);
707 /* O_NONBLOCK in case we're opening FIFO; will be reset later */
708 if ((fd = open(DOSDrives[drive].device, O_RDONLY|O_NOCTTY|O_NONBLOCK)) != -1) {
709 if (fstat(fd, &stat_buf) < 0) { /* shouldn't happen since we just opened that file */
710 ERR("fstat() failed for opened device '%s' (drive %c:) ! IT SHOULDN'T HAPPEN !!!\n",
711 DOSDrives[drive].device, 'A'+drive);
712 ret = -1;
713 } else if (!S_ISBLK(stat_buf.st_mode)) {
714 ERR("Device '%s' (drive %c:) is not a block device - check your config\n",
715 DOSDrives[drive].device, 'A'+drive);
716 ret = -1;
717 /* reset O_NONBLOCK */
718 } else if (fcntl(fd, F_SETFL, 0) < 0 || fcntl(fd, F_GETFL) & O_NONBLOCK) {
719 ERR("fcntl() failed to reset O_NONBLOCK for device '%s' (drive %c:)\n",
720 DOSDrives[drive].device, 'A'+drive);
721 ret = -1;
723 if (ret) {
724 close(fd);
725 fd = -1;
727 } else {
728 if (!DOSDrives[drive].device)
729 ERR("No device configured for drive %c: !\n", 'A'+drive);
730 else
731 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
732 (stat(DOSDrives[drive].device, &stat_buf)) ?
733 "not available or symlink not valid ?" : "no permission");
735 if (fd == -1) {
736 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
737 return -1;
740 switch(DOSDrives[drive].type)
742 case DRIVE_REMOVABLE:
743 case DRIVE_FIXED:
744 offs = 0;
745 break;
746 case DRIVE_CDROM:
747 offs = CDROM_Data_FindBestVoldesc(fd);
748 break;
749 default:
750 offs = 0;
751 break;
754 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs))
756 ret = -4;
757 goto the_end;
759 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER)
761 ret = -2;
762 goto the_end;
765 switch(DOSDrives[drive].type)
767 case DRIVE_REMOVABLE:
768 case DRIVE_FIXED:
769 if ((buff[0x26]!=0x29) || /* Check for FAT present */
770 /* FIXME: do really all FAT have their name beginning with
771 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
772 memcmp( buff+0x36,"FAT",3))
774 ERR("The filesystem is not FAT !! (device=%s)\n",
775 DOSDrives[drive].device);
776 ret = -3;
777 goto the_end;
779 break;
780 case DRIVE_CDROM:
781 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
783 ret = -3;
784 goto the_end;
786 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
787 break;
788 default:
789 ret = -3;
790 goto the_end;
793 return close(fd);
794 the_end:
795 close(fd);
796 return ret;
800 /***********************************************************************
801 * DRIVE_WriteSuperblockEntry
803 * NOTE
804 * We are writing as little as possible (ie. not the whole SuperBlock)
805 * not to interfere with kernel. The drive can be mounted !
807 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
809 int fd;
811 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
813 ERR("Cannot open the device %s (for writing)\n",
814 DOSDrives[drive].device);
815 return -1;
817 if (lseek(fd,ofs,SEEK_SET)!=ofs)
819 ERR("lseek failed on device %s !\n",
820 DOSDrives[drive].device);
821 close(fd);
822 return -2;
824 if (write(fd,buff,len)!=len)
826 close(fd);
827 ERR("Cannot write on %s !\n",
828 DOSDrives[drive].device);
829 return -3;
831 return close (fd);
834 /******************************************************************
835 * static HANDLE CDROM_Open
839 static HANDLE CDROM_Open(int drive)
841 WCHAR root[] = {'\\','\\','.','\\','A',':',0};
842 root[4] += drive;
843 return CreateFileW(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
846 /**************************************************************************
847 * CDROM_Data_GetLabel [internal]
849 DWORD CDROM_Data_GetLabel(int drive, WCHAR *label)
851 #define LABEL_LEN 32+1
852 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
853 WORD offs = CDROM_Data_FindBestVoldesc(dev);
854 WCHAR label_read[LABEL_LEN]; /* Unicode possible, too */
855 DWORD unicode_id = 0;
857 if (offs)
859 if ((lseek(dev, offs+0x58, SEEK_SET) == offs+0x58)
860 && (read(dev, &unicode_id, 3) == 3))
862 int ver = (unicode_id & 0xff0000) >> 16;
864 if ((lseek(dev, offs+0x28, SEEK_SET) != offs+0x28)
865 || (read(dev, &label_read, LABEL_LEN) != LABEL_LEN))
866 goto failure;
868 close(dev);
869 if ((LOWORD(unicode_id) == 0x2f25) /* Unicode ID */
870 && ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
871 { /* yippee, unicode */
872 int i;
873 WORD ch;
874 for (i=0; i<LABEL_LEN;i++)
875 { /* Motorola -> Intel Unicode conversion :-\ */
876 ch = label_read[i];
877 label_read[i] = (ch << 8) | (ch >> 8);
879 strncpyW(label, label_read, 11);
880 label[11] = 0;
882 else
884 MultiByteToWideChar(CP_UNIXCP, 0, (LPSTR)label_read, -1, label, 11);
885 label[11] = '\0';
887 return 1;
890 failure:
891 close(dev);
892 ERR("error reading label !\n");
893 return 0;
896 /**************************************************************************
897 * CDROM_GetLabel [internal]
899 static DWORD CDROM_GetLabel(int drive, WCHAR *label)
901 HANDLE h;
902 CDROM_DISK_DATA cdd;
903 DWORD br, ret = 1;
904 BOOL r;
906 h = CDROM_Open(drive);
907 if( !h )
908 return 0;
909 r = DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL,
910 0, &cdd, sizeof(cdd), &br, 0);
911 CloseHandle( h );
912 if( !r )
913 return 0;
915 switch (cdd.DiskData & 0x03)
917 case CDROM_DISK_DATA_TRACK:
918 if (!CDROM_Data_GetLabel(drive, label))
919 ret = 0;
920 break;
921 case CDROM_DISK_AUDIO_TRACK:
923 static const WCHAR audioCD[] = {'A','u','d','i','o',' ','C','D',' ',' ',' ',0};
924 strcpyW(label, audioCD);
925 break;
927 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
928 FIXME("Need to get the label of a mixed mode CD!\n");
929 /* This assumes that the first track is a data track! */
930 /* I guess the correct way would be to enumerate all data tracks
931 and check each for the title */
932 if (!CDROM_Data_GetLabel(drive, label))
933 ret = 0;
934 break;
935 case 0:
936 ret = 0;
937 break;
939 TRACE("CD: label is %s\n", debugstr_w(label));
941 return ret;
943 /***********************************************************************
944 * DRIVE_GetLabel
946 LPCWSTR DRIVE_GetLabel( int drive )
948 int read = 0;
949 char buff[DRIVE_SUPER];
950 int offs = -1;
952 if (!DRIVE_IsValid( drive )) return NULL;
953 if (DOSDrives[drive].type == DRIVE_CDROM)
955 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
957 else
958 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
960 if (DRIVE_ReadSuperblock(drive,(char *) buff))
961 ERR("Invalid or unreadable superblock on %s (%c:).\n",
962 DOSDrives[drive].device, (char)(drive+'A'));
963 else {
964 if (DOSDrives[drive].type == DRIVE_REMOVABLE ||
965 DOSDrives[drive].type == DRIVE_FIXED)
966 offs = 0x2b;
968 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
969 if (offs != -1)
970 MultiByteToWideChar(CP_UNIXCP, 0, buff+offs, 11,
971 DOSDrives[drive].label_read, 11);
972 DOSDrives[drive].label_read[11]='\0';
973 read = 1;
977 return (read) ?
978 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
981 #define CDFRAMES_PERSEC 75
982 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
983 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
984 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
986 /**************************************************************************
987 * CDROM_Audio_GetSerial [internal]
989 static DWORD CDROM_Audio_GetSerial(HANDLE h)
991 unsigned long serial = 0;
992 int i;
993 WORD wMagic;
994 DWORD dwStart, dwEnd, br;
995 CDROM_TOC toc;
997 if (!DeviceIoControl(h, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0))
998 return 0;
1001 * wMagic collects the wFrames from track 1
1002 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
1003 * frames.
1004 * There it is collected for correcting the serial when there are less than
1005 * 3 tracks.
1007 wMagic = toc.TrackData[0].Address[2];
1008 dwStart = FRAME_OF_TOC(toc, toc.FirstTrack);
1010 for (i = 0; i <= toc.LastTrack - toc.FirstTrack; i++) {
1011 serial += (toc.TrackData[i].Address[0] << 16) |
1012 (toc.TrackData[i].Address[1] << 8) | toc.TrackData[i].Address[2];
1014 dwEnd = FRAME_OF_TOC(toc, toc.LastTrack + 1);
1016 if (toc.LastTrack - toc.FirstTrack + 1 < 3)
1017 serial += wMagic + (dwEnd - dwStart);
1019 return serial;
1022 /**************************************************************************
1023 * CDROM_Data_GetSerial [internal]
1025 static DWORD CDROM_Data_GetSerial(int drive)
1027 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
1028 WORD offs;
1029 union {
1030 unsigned long val;
1031 unsigned char p[4];
1032 } serial;
1033 BYTE b0 = 0, b1 = 1, b2 = 2, b3 = 3;
1036 if (dev == -1) return 0;
1037 offs = CDROM_Data_FindBestVoldesc(dev);
1039 serial.val = 0;
1040 if (offs)
1042 BYTE buf[2048];
1043 RTL_OSVERSIONINFOEXW ovi;
1044 int i;
1046 lseek(dev, offs, SEEK_SET);
1047 read(dev, buf, 2048);
1049 * OK, another braindead one... argh. Just believe it.
1050 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
1051 * It's true and nobody will ever be able to change it.
1053 ovi.dwOSVersionInfoSize = sizeof(ovi);
1054 RtlGetVersion(&ovi);
1055 if ((ovi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (ovi.dwMajorVersion >= 4))
1057 b0 = 3; b1 = 2; b2 = 1; b3 = 0;
1059 for (i = 0; i < 2048; i += 4)
1061 /* DON'T optimize this into DWORD !! (breaks overflow) */
1062 serial.p[b0] += buf[i+b0];
1063 serial.p[b1] += buf[i+b1];
1064 serial.p[b2] += buf[i+b2];
1065 serial.p[b3] += buf[i+b3];
1068 close(dev);
1069 return serial.val;
1072 /**************************************************************************
1073 * CDROM_GetSerial [internal]
1075 static DWORD CDROM_GetSerial(int drive)
1077 DWORD serial = 0;
1078 HANDLE h;
1079 CDROM_DISK_DATA cdd;
1080 DWORD br;
1081 BOOL r;
1083 TRACE("%d\n", drive);
1085 h = CDROM_Open(drive);
1086 if( !h )
1087 return 0;
1088 r = DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL,
1089 0, &cdd, sizeof(cdd), &br, 0);
1090 if (!r)
1092 CloseHandle(h);
1093 return 0;
1096 switch (cdd.DiskData & 0x03)
1098 case CDROM_DISK_DATA_TRACK:
1099 /* hopefully a data CD */
1100 serial = CDROM_Data_GetSerial(drive);
1101 break;
1102 case CDROM_DISK_AUDIO_TRACK:
1103 /* fall thru */
1104 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
1105 serial = CDROM_Audio_GetSerial(h);
1106 break;
1107 case 0:
1108 break;
1111 if (serial)
1112 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial), LOWORD(serial));
1114 CloseHandle(h);
1116 return serial;
1119 /***********************************************************************
1120 * DRIVE_GetSerialNumber
1122 DWORD DRIVE_GetSerialNumber( int drive )
1124 DWORD serial = 0;
1125 char buff[DRIVE_SUPER];
1127 TRACE("drive %d, type = %d\n", drive, DOSDrives[drive].type);
1129 if (!DRIVE_IsValid( drive )) return 0;
1131 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
1133 switch(DOSDrives[drive].type)
1135 case DRIVE_REMOVABLE:
1136 case DRIVE_FIXED:
1137 if (DRIVE_ReadSuperblock(drive,(char *) buff))
1138 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
1139 " Maybe not FAT?\n" ,
1140 DOSDrives[drive].device, 'A'+drive);
1141 else
1142 serial = *((DWORD*)(buff+0x27));
1143 break;
1144 case DRIVE_CDROM:
1145 serial = CDROM_GetSerial(drive);
1146 break;
1147 default:
1148 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
1152 return (serial) ? serial : DOSDrives[drive].serial_conf;
1156 /***********************************************************************
1157 * DRIVE_SetSerialNumber
1159 int DRIVE_SetSerialNumber( int drive, DWORD serial )
1161 char buff[DRIVE_SUPER];
1163 if (!DRIVE_IsValid( drive )) return 0;
1165 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
1167 if ((DOSDrives[drive].type != DRIVE_REMOVABLE) &&
1168 (DOSDrives[drive].type != DRIVE_FIXED)) return 0;
1169 /* check, if the drive has a FAT filesystem */
1170 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
1171 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
1172 return 1;
1175 if (DOSDrives[drive].type == DRIVE_CDROM) return 0;
1176 DOSDrives[drive].serial_conf = serial;
1177 return 1;
1181 /***********************************************************************
1182 * DRIVE_GetType
1184 static UINT DRIVE_GetType( int drive )
1186 if (!DRIVE_IsValid( drive )) return DRIVE_NO_ROOT_DIR;
1187 return DOSDrives[drive].type;
1191 /***********************************************************************
1192 * DRIVE_GetFlags
1194 UINT DRIVE_GetFlags( int drive )
1196 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
1197 return DOSDrives[drive].flags;
1200 /***********************************************************************
1201 * DRIVE_Chdir
1203 int DRIVE_Chdir( int drive, LPCWSTR path )
1205 DOS_FULL_NAME full_name;
1206 WCHAR buffer[MAX_PATHNAME_LEN];
1207 LPSTR unix_cwd;
1208 BY_HANDLE_FILE_INFORMATION info;
1209 TDB *pTask = GlobalLock16(GetCurrentTask());
1211 buffer[0] = 'A' + drive;
1212 buffer[1] = ':';
1213 buffer[2] = 0;
1214 TRACE("(%s,%s)\n", debugstr_w(buffer), debugstr_w(path) );
1215 strncpyW( buffer + 2, path, MAX_PATHNAME_LEN - 2 );
1216 buffer[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1218 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
1219 if (!FILE_Stat( full_name.long_name, &info, NULL )) return 0;
1220 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1222 SetLastError( ERROR_FILE_NOT_FOUND );
1223 return 0;
1225 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
1226 while (*unix_cwd == '/') unix_cwd++;
1228 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
1229 'A' + drive, unix_cwd, debugstr_w(full_name.short_name + 3) );
1231 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
1232 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
1233 DOSDrives[drive].dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name.short_name) - 2) * sizeof(WCHAR));
1234 strcpyW(DOSDrives[drive].dos_cwd, full_name.short_name + 3);
1235 DOSDrives[drive].unix_cwd = heap_strdup( unix_cwd );
1237 if (drive == DRIVE_CurDrive)
1239 UNICODE_STRING dirW;
1241 RtlInitUnicodeString( &dirW, full_name.short_name );
1242 RtlSetCurrentDirectory_U( &dirW );
1245 if (pTask && (pTask->curdrive & 0x80) &&
1246 ((pTask->curdrive & ~0x80) == drive))
1248 WideCharToMultiByte(CP_ACP, 0, full_name.short_name + 2, -1,
1249 pTask->curdir, sizeof(pTask->curdir), NULL, NULL);
1250 DRIVE_LastTask = GetCurrentTask();
1252 return 1;
1256 /***********************************************************************
1257 * DRIVE_Disable
1259 int DRIVE_Disable( int drive )
1261 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1263 SetLastError( ERROR_INVALID_DRIVE );
1264 return 0;
1266 DOSDrives[drive].flags |= DRIVE_DISABLED;
1267 return 1;
1271 /***********************************************************************
1272 * DRIVE_Enable
1274 int DRIVE_Enable( int drive )
1276 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1278 SetLastError( ERROR_INVALID_DRIVE );
1279 return 0;
1281 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
1282 return 1;
1286 /***********************************************************************
1287 * DefineDosDeviceA (KERNEL32.@)
1289 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath)
1291 UNICODE_STRING d, t;
1292 BOOL ret;
1294 if (!RtlCreateUnicodeStringFromAsciiz(&d, devname))
1296 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1297 return FALSE;
1299 if (!RtlCreateUnicodeStringFromAsciiz(&t, targetpath))
1301 RtlFreeUnicodeString(&d);
1302 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1303 return FALSE;
1305 ret = DefineDosDeviceW(flags, d.Buffer, t.Buffer);
1306 RtlFreeUnicodeString(&d);
1307 RtlFreeUnicodeString(&t);
1308 return ret;
1312 /***********************************************************************
1313 * DefineDosDeviceA (KERNEL32.@)
1315 BOOL WINAPI DefineDosDeviceW(DWORD flags,LPCWSTR devname,LPCWSTR targetpath)
1317 DOSDRIVE *old, *new;
1319 /* this is a temporary hack for int21 support. better implementation has to be done */
1320 if (flags != DDD_RAW_TARGET_PATH ||
1321 !(toupperW(devname[0]) >= 'A' && toupperW(devname[0]) <= 'Z') ||
1322 devname[1] != ':' || devname[2] != 0 ||
1323 !(toupperW(targetpath[0]) >= 'A' && toupperW(targetpath[0]) <= 'Z') ||
1324 targetpath[1] != ':' || targetpath[2] != '\\' || targetpath[3] != 0)
1326 FIXME("(0x%08lx,%s,%s),stub!\n", flags, debugstr_w(devname), debugstr_w(targetpath));
1327 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1328 return FALSE;
1331 old = DOSDrives + devname[0] - 'A';
1332 new = DOSDrives + targetpath[0] - 'A';
1334 if (!old->root)
1336 SetLastError( ERROR_INVALID_DRIVE );
1337 return 0;
1340 if ( new->root )
1342 TRACE("Can't map drive %c: to already existing drive %c:\n",
1343 devname[0], targetpath[0] );
1344 /* it is already mapped there, so return success */
1345 if (!strcmp(old->root,new->root))
1346 return 1;
1347 return 0;
1350 new->root = heap_strdup( old->root );
1351 new->dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(old->dos_cwd) + 1) * sizeof(WCHAR));
1352 strcpyW(new->dos_cwd, old->dos_cwd);
1353 new->unix_cwd = heap_strdup( old->unix_cwd );
1354 new->device = heap_strdup( old->device );
1355 memcpy ( new->label_conf, old->label_conf, 12 );
1356 memcpy ( new->label_read, old->label_read, 12 );
1357 new->serial_conf = old->serial_conf;
1358 new->type = old->type;
1359 new->flags = old->flags;
1360 new->dev = old->dev;
1361 new->ino = old->ino;
1363 TRACE("Drive %c: is now equal to drive %c:\n",
1364 targetpath[0], devname[0] );
1366 return 1;
1370 /***********************************************************************
1371 * DRIVE_GetFreeSpace
1373 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
1374 PULARGE_INTEGER available )
1376 struct statvfs info;
1378 if (!DRIVE_IsValid(drive))
1380 SetLastError( ERROR_PATH_NOT_FOUND );
1381 return 0;
1384 if (statvfs( DOSDrives[drive].root, &info ) < 0)
1386 FILE_SetDosError();
1387 WARN("cannot do statvfs(%s)\n", DOSDrives[drive].root);
1388 return 0;
1390 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_frsize, info.f_blocks );
1391 if (DOSDrives[drive].type == DRIVE_CDROM)
1392 available->QuadPart = 0; /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1393 else
1394 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_frsize, info.f_bavail );
1396 return 1;
1399 /***********************************************************************
1400 * DRIVE_GetCurrentDirectory
1401 * Returns "X:\\path\\etc\\".
1403 * Despite the API description, return required length including the
1404 * terminating null when buffer too small. This is the real behaviour.
1406 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPWSTR buf )
1408 UINT ret;
1409 LPCWSTR dos_cwd = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1410 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1412 ret = strlenW(dos_cwd) + 3; /* length of WHOLE current directory */
1413 if (ret >= buflen) return ret + 1;
1415 strcpyW( buf, driveA_rootW );
1416 buf[0] += DRIVE_GetCurrentDrive();
1417 strcatW( buf, dos_cwd );
1418 return ret;
1422 /***********************************************************************
1423 * DRIVE_BuildEnv
1425 * Build the environment array containing the drives' current directories.
1426 * Resulting pointer must be freed with HeapFree.
1428 WCHAR *DRIVE_BuildEnv(void)
1430 int i, length = 0;
1431 LPCWSTR cwd[MAX_DOS_DRIVES];
1432 WCHAR *env, *p;
1434 for (i = 0; i < MAX_DOS_DRIVES; i++)
1436 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0])
1437 length += strlenW(cwd[i]) + 8;
1439 if (!(env = HeapAlloc( GetProcessHeap(), 0, (length+1) * sizeof(WCHAR) ))) return NULL;
1440 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
1442 if (cwd[i] && cwd[i][0])
1444 *p++ = '='; *p++ = 'A' + i; *p++ = ':';
1445 *p++ = '='; *p++ = 'A' + i; *p++ = ':'; *p++ = '\\';
1446 strcpyW( p, cwd[i] );
1447 p += strlenW(p) + 1;
1450 *p = 0;
1451 return env;
1455 /***********************************************************************
1456 * GetDiskFreeSpace (KERNEL.422)
1458 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
1459 LPDWORD sector_bytes, LPDWORD free_clusters,
1460 LPDWORD total_clusters )
1462 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
1463 free_clusters, total_clusters );
1467 /***********************************************************************
1468 * GetDiskFreeSpaceW (KERNEL32.@)
1470 * Fails if expression resulting from current drive's dir and "root"
1471 * is not a root dir of the target drive.
1473 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1474 * if the corresponding info is unneeded.
1476 * FIXME: needs to support UNC names from Win95 OSR2 on.
1478 * Behaviour under Win95a:
1479 * CurrDir root result
1480 * "E:\\TEST" "E:" FALSE
1481 * "E:\\" "E:" TRUE
1482 * "E:\\" "E" FALSE
1483 * "E:\\" "\\" TRUE
1484 * "E:\\TEST" "\\" TRUE
1485 * "E:\\TEST" ":\\" FALSE
1486 * "E:\\TEST" "E:\\" TRUE
1487 * "E:\\TEST" "" FALSE
1488 * "E:\\" "" FALSE (!)
1489 * "E:\\" 0x0 TRUE
1490 * "E:\\TEST" 0x0 TRUE (!)
1491 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1492 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1494 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1495 LPDWORD sector_bytes, LPDWORD free_clusters,
1496 LPDWORD total_clusters )
1498 int drive, sec_size;
1499 ULARGE_INTEGER size,available;
1500 LPCWSTR path;
1501 DWORD cluster_sec;
1503 TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root), cluster_sectors, sector_bytes,
1504 free_clusters, total_clusters);
1506 if (!root || root[0] == '\\' || root[0] == '/')
1507 drive = DRIVE_GetCurrentDrive();
1508 else
1509 if (root[0] && root[1] == ':') /* root contains drive tag */
1511 drive = toupperW(root[0]) - 'A';
1512 path = &root[2];
1513 if (path[0] == '\0')
1515 path = DRIVE_GetDosCwd(drive);
1516 if (!path)
1518 SetLastError(ERROR_PATH_NOT_FOUND);
1519 return FALSE;
1522 else
1523 if (path[0] == '\\')
1524 path++;
1526 if (path[0]) /* oops, we are in a subdir */
1528 SetLastError(ERROR_INVALID_NAME);
1529 return FALSE;
1532 else
1534 if (!root[0])
1535 SetLastError(ERROR_PATH_NOT_FOUND);
1536 else
1537 SetLastError(ERROR_INVALID_NAME);
1538 return FALSE;
1541 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1543 /* Cap the size and available at 2GB as per specs. */
1544 if ((size.u.HighPart) ||(size.u.LowPart > 0x7fffffff))
1546 size.u.HighPart = 0;
1547 size.u.LowPart = 0x7fffffff;
1549 if ((available.u.HighPart) ||(available.u.LowPart > 0x7fffffff))
1551 available.u.HighPart =0;
1552 available.u.LowPart = 0x7fffffff;
1554 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
1555 size.u.LowPart /= sec_size;
1556 available.u.LowPart /= sec_size;
1557 /* FIXME: probably have to adjust those variables too for CDFS */
1558 cluster_sec = 1;
1559 while (cluster_sec * 65536 < size.u.LowPart) cluster_sec *= 2;
1561 if (cluster_sectors)
1562 *cluster_sectors = cluster_sec;
1563 if (sector_bytes)
1564 *sector_bytes = sec_size;
1565 if (free_clusters)
1566 *free_clusters = available.u.LowPart / cluster_sec;
1567 if (total_clusters)
1568 *total_clusters = size.u.LowPart / cluster_sec;
1569 return TRUE;
1573 /***********************************************************************
1574 * GetDiskFreeSpaceA (KERNEL32.@)
1576 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1577 LPDWORD sector_bytes, LPDWORD free_clusters,
1578 LPDWORD total_clusters )
1580 UNICODE_STRING rootW;
1581 BOOL ret = FALSE;
1583 if (root)
1585 if(!RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1587 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1588 return FALSE;
1591 else
1592 rootW.Buffer = NULL;
1594 ret = GetDiskFreeSpaceW(rootW.Buffer, cluster_sectors, sector_bytes,
1595 free_clusters, total_clusters );
1596 RtlFreeUnicodeString(&rootW);
1598 return ret;
1602 /***********************************************************************
1603 * GetDiskFreeSpaceExW (KERNEL32.@)
1605 * This function is used to acquire the size of the available and
1606 * total space on a logical volume.
1608 * RETURNS
1610 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1611 * detailed error information.
1614 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root,
1615 PULARGE_INTEGER avail,
1616 PULARGE_INTEGER total,
1617 PULARGE_INTEGER totalfree)
1619 int drive;
1620 ULARGE_INTEGER size,available;
1622 if (!root) drive = DRIVE_GetCurrentDrive();
1623 else
1624 { /* C: always works for GetDiskFreeSpaceEx */
1625 if ((root[1]) && ((root[1] != ':') || (root[2] && root[2] != '\\')))
1627 FIXME("there are valid root names which are not supported yet\n");
1628 /* ..like UNC names, for instance. */
1630 WARN("invalid root '%s'\n", debugstr_w(root));
1631 return FALSE;
1633 drive = toupperW(root[0]) - 'A';
1636 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1638 if (total)
1640 total->u.HighPart = size.u.HighPart;
1641 total->u.LowPart = size.u.LowPart;
1644 if (totalfree)
1646 totalfree->u.HighPart = available.u.HighPart;
1647 totalfree->u.LowPart = available.u.LowPart;
1650 if (avail)
1652 if (FIXME_ON(dosfs))
1654 /* On Windows2000, we need to check the disk quota
1655 allocated for the user owning the calling process. We
1656 don't want to be more obtrusive than necessary with the
1657 FIXME messages, so don't print the FIXME unless Wine is
1658 actually masquerading as Windows2000. */
1660 RTL_OSVERSIONINFOEXW ovi;
1661 ovi.dwOSVersionInfoSize = sizeof(ovi);
1662 if (RtlGetVersion(&ovi))
1664 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1665 FIXME("no per-user quota support yet\n");
1669 /* Quick hack, should eventually be fixed to work 100% with
1670 Windows2000 (see comment above). */
1671 avail->u.HighPart = available.u.HighPart;
1672 avail->u.LowPart = available.u.LowPart;
1675 return TRUE;
1678 /***********************************************************************
1679 * GetDiskFreeSpaceExA (KERNEL32.@)
1681 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root, PULARGE_INTEGER avail,
1682 PULARGE_INTEGER total,
1683 PULARGE_INTEGER totalfree)
1685 UNICODE_STRING rootW;
1686 BOOL ret;
1688 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
1689 else rootW.Buffer = NULL;
1691 ret = GetDiskFreeSpaceExW( rootW.Buffer, avail, total, totalfree);
1693 RtlFreeUnicodeString(&rootW);
1694 return ret;
1697 /***********************************************************************
1698 * GetDriveType (KERNEL.136)
1699 * This function returns the type of a drive in Win16.
1700 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1701 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1702 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1703 * do any pseudo-clever changes.
1705 * RETURNS
1706 * drivetype DRIVE_xxx
1708 UINT16 WINAPI GetDriveType16( UINT16 drive ) /* [in] number (NOT letter) of drive */
1710 UINT type = DRIVE_GetType(drive);
1711 TRACE("(%c:)\n", 'A' + drive );
1712 if (type == DRIVE_CDROM) type = DRIVE_REMOTE;
1713 return type;
1717 /***********************************************************************
1718 * GetDriveTypeW (KERNEL32.@)
1720 * Returns the type of the disk drive specified. If root is NULL the
1721 * root of the current directory is used.
1723 * RETURNS
1725 * Type of drive (from Win32 SDK):
1727 * DRIVE_UNKNOWN unable to find out anything about the drive
1728 * DRIVE_NO_ROOT_DIR nonexistent root dir
1729 * DRIVE_REMOVABLE the disk can be removed from the machine
1730 * DRIVE_FIXED the disk can not be removed from the machine
1731 * DRIVE_REMOTE network disk
1732 * DRIVE_CDROM CDROM drive
1733 * DRIVE_RAMDISK virtual disk in RAM
1735 UINT WINAPI GetDriveTypeW(LPCWSTR root) /* [in] String describing drive */
1737 int drive;
1738 TRACE("(%s)\n", debugstr_w(root));
1740 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1741 else
1743 if ((root[1]) && (root[1] != ':'))
1745 WARN("invalid root %s\n", debugstr_w(root));
1746 return DRIVE_NO_ROOT_DIR;
1748 drive = toupperW(root[0]) - 'A';
1750 return DRIVE_GetType(drive);
1754 /***********************************************************************
1755 * GetDriveTypeA (KERNEL32.@)
1757 UINT WINAPI GetDriveTypeA( LPCSTR root )
1759 UNICODE_STRING rootW;
1760 UINT ret = 0;
1762 if (root)
1764 if( !RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1766 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1767 return 0;
1770 else
1771 rootW.Buffer = NULL;
1773 ret = GetDriveTypeW(rootW.Buffer);
1775 RtlFreeUnicodeString(&rootW);
1776 return ret;
1781 /***********************************************************************
1782 * GetCurrentDirectory (KERNEL.411)
1784 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1786 WCHAR cur_dirW[MAX_PATH];
1788 DRIVE_GetCurrentDirectory(MAX_PATH, cur_dirW);
1789 return (UINT16)WideCharToMultiByte(CP_ACP, 0, cur_dirW, -1, buf, buflen, NULL, NULL);
1793 /***********************************************************************
1794 * GetCurrentDirectoryW (KERNEL32.@)
1796 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1798 UINT ret;
1799 WCHAR longname[MAX_PATHNAME_LEN];
1800 WCHAR shortname[MAX_PATHNAME_LEN];
1802 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1803 if ( ret > MAX_PATHNAME_LEN ) {
1804 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1805 return ret;
1807 GetLongPathNameW(shortname, longname, MAX_PATHNAME_LEN);
1808 ret = strlenW( longname ) + 1;
1809 if (ret > buflen) return ret;
1810 strcpyW(buf, longname);
1811 return ret - 1;
1814 /***********************************************************************
1815 * GetCurrentDirectoryA (KERNEL32.@)
1817 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1819 WCHAR bufferW[MAX_PATH];
1820 DWORD ret, retW;
1822 retW = GetCurrentDirectoryW(MAX_PATH, bufferW);
1824 if (!retW)
1825 ret = 0;
1826 else if (retW > MAX_PATH)
1828 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1829 ret = 0;
1831 else
1833 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1834 if (buflen >= ret)
1836 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buf, buflen, NULL, NULL);
1837 ret--; /* length without 0 */
1840 return ret;
1844 /***********************************************************************
1845 * SetCurrentDirectory (KERNEL.412)
1847 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1849 return SetCurrentDirectoryA( dir );
1853 /***********************************************************************
1854 * SetCurrentDirectoryW (KERNEL32.@)
1856 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dir )
1858 int drive, olddrive = DRIVE_GetCurrentDrive();
1860 if (!dir)
1862 SetLastError(ERROR_INVALID_PARAMETER);
1863 return FALSE;
1865 if (dir[0] && (dir[1]==':'))
1867 drive = toupperW( *dir ) - 'A';
1868 dir += 2;
1870 else
1871 drive = olddrive;
1873 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1874 sets pTask->curdir only if pTask->curdrive is drive */
1875 if (!(DRIVE_SetCurrentDrive( drive )))
1876 return FALSE;
1878 /* FIXME: what about empty strings? Add a \\ ? */
1879 if (!DRIVE_Chdir( drive, dir )) {
1880 DRIVE_SetCurrentDrive(olddrive);
1881 return FALSE;
1883 return TRUE;
1887 /***********************************************************************
1888 * SetCurrentDirectoryA (KERNEL32.@)
1890 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1892 UNICODE_STRING dirW;
1893 BOOL ret = FALSE;
1895 if (!dir)
1897 SetLastError(ERROR_INVALID_PARAMETER);
1898 return FALSE;
1901 if (RtlCreateUnicodeStringFromAsciiz(&dirW, dir))
1903 ret = SetCurrentDirectoryW(dirW.Buffer);
1904 RtlFreeUnicodeString(&dirW);
1906 else
1907 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1908 return ret;
1912 /***********************************************************************
1913 * GetLogicalDriveStringsA (KERNEL32.@)
1915 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1917 int drive, count;
1919 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1920 if (DRIVE_IsValid(drive)) count++;
1921 if ((count * 4) + 1 <= len)
1923 LPSTR p = buffer;
1924 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1925 if (DRIVE_IsValid(drive))
1927 *p++ = 'a' + drive;
1928 *p++ = ':';
1929 *p++ = '\\';
1930 *p++ = '\0';
1932 *p = '\0';
1933 return count * 4;
1935 else
1936 return (count * 4) + 1; /* account for terminating null */
1937 /* The API tells about these different return values */
1941 /***********************************************************************
1942 * GetLogicalDriveStringsW (KERNEL32.@)
1944 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1946 int drive, count;
1948 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1949 if (DRIVE_IsValid(drive)) count++;
1950 if (count * 4 * sizeof(WCHAR) <= len)
1952 LPWSTR p = buffer;
1953 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1954 if (DRIVE_IsValid(drive))
1956 *p++ = (WCHAR)('a' + drive);
1957 *p++ = (WCHAR)':';
1958 *p++ = (WCHAR)'\\';
1959 *p++ = (WCHAR)'\0';
1961 *p = (WCHAR)'\0';
1963 return count * 4 * sizeof(WCHAR);
1967 /***********************************************************************
1968 * GetLogicalDrives (KERNEL32.@)
1970 DWORD WINAPI GetLogicalDrives(void)
1972 DWORD ret = 0;
1973 int drive;
1975 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1977 if ( (DRIVE_IsValid(drive)) ||
1978 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1979 ret |= (1 << drive);
1981 return ret;
1985 /***********************************************************************
1986 * GetVolumeInformationW (KERNEL32.@)
1988 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1989 DWORD label_len, DWORD *serial,
1990 DWORD *filename_len, DWORD *flags,
1991 LPWSTR fsname, DWORD fsname_len )
1993 int drive;
1994 LPWSTR cp;
1996 /* FIXME, SetLastError()s missing */
1998 if (!root) drive = DRIVE_GetCurrentDrive();
1999 else
2001 if (root[0] && root[1] != ':')
2003 WARN("invalid root %s\n", debugstr_w(root));
2004 return FALSE;
2006 drive = toupperW(root[0]) - 'A';
2008 if (!DRIVE_IsValid( drive )) return FALSE;
2009 if (label && label_len)
2011 strncpyW( label, DRIVE_GetLabel(drive), label_len );
2012 label[label_len - 1] = 0; /* ensure 0 termination */
2013 cp = label + strlenW(label);
2014 while (cp != label && *(cp-1) == ' ') cp--;
2015 *cp = '\0';
2017 if (serial) *serial = DRIVE_GetSerialNumber(drive);
2019 /* Set the filesystem information */
2020 /* Note: we only emulate a FAT fs at present */
2022 if (filename_len) {
2023 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
2024 *filename_len = 12;
2025 else
2026 *filename_len = 255;
2028 if (flags)
2030 *flags=0;
2031 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
2032 *flags|=FS_CASE_SENSITIVE;
2033 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
2034 *flags|=FS_CASE_IS_PRESERVED;
2036 if (fsname && fsname_len)
2038 /* Diablo checks that return code ... */
2039 if (DOSDrives[drive].type == DRIVE_CDROM)
2041 static const WCHAR cdfsW[] = {'C','D','F','S',0};
2042 strncpyW( fsname, cdfsW, fsname_len );
2044 else
2046 static const WCHAR fatW[] = {'F','A','T',0};
2047 strncpyW( fsname, fatW, fsname_len );
2049 fsname[fsname_len - 1] = 0; /* ensure 0 termination */
2051 return TRUE;
2055 /***********************************************************************
2056 * GetVolumeInformationA (KERNEL32.@)
2058 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
2059 DWORD label_len, DWORD *serial,
2060 DWORD *filename_len, DWORD *flags,
2061 LPSTR fsname, DWORD fsname_len )
2063 UNICODE_STRING rootW;
2064 LPWSTR labelW, fsnameW;
2065 BOOL ret;
2067 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
2068 else rootW.Buffer = NULL;
2069 labelW = label ? HeapAlloc(GetProcessHeap(), 0, label_len * sizeof(WCHAR)) : NULL;
2070 fsnameW = fsname ? HeapAlloc(GetProcessHeap(), 0, fsname_len * sizeof(WCHAR)) : NULL;
2072 if ((ret = GetVolumeInformationW(rootW.Buffer, labelW, label_len, serial,
2073 filename_len, flags, fsnameW, fsname_len)))
2075 if (label) WideCharToMultiByte(CP_ACP, 0, labelW, -1, label, label_len, NULL, NULL);
2076 if (fsname) WideCharToMultiByte(CP_ACP, 0, fsnameW, -1, fsname, fsname_len, NULL, NULL);
2079 RtlFreeUnicodeString(&rootW);
2080 if (labelW) HeapFree( GetProcessHeap(), 0, labelW );
2081 if (fsnameW) HeapFree( GetProcessHeap(), 0, fsnameW );
2082 return ret;
2085 /***********************************************************************
2086 * SetVolumeLabelW (KERNEL32.@)
2088 BOOL WINAPI SetVolumeLabelW( LPCWSTR root, LPCWSTR volname )
2090 int drive;
2092 /* FIXME, SetLastErrors missing */
2094 if (!root) drive = DRIVE_GetCurrentDrive();
2095 else
2097 if ((root[1]) && (root[1] != ':'))
2099 WARN("invalid root %s\n", debugstr_w(root));
2100 return FALSE;
2102 drive = toupperW(root[0]) - 'A';
2104 if (!DRIVE_IsValid( drive )) return FALSE;
2106 /* some copy protection stuff check this */
2107 if (DOSDrives[drive].type == DRIVE_CDROM) return FALSE;
2109 strncpyW(DOSDrives[drive].label_conf, volname, 12);
2110 DOSDrives[drive].label_conf[12 - 1] = 0; /* ensure 0 termination */
2111 return TRUE;
2114 /***********************************************************************
2115 * SetVolumeLabelA (KERNEL32.@)
2117 BOOL WINAPI SetVolumeLabelA(LPCSTR root, LPCSTR volname)
2119 UNICODE_STRING rootW, volnameW;
2120 BOOL ret;
2122 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
2123 else rootW.Buffer = NULL;
2124 if (volname) RtlCreateUnicodeStringFromAsciiz(&volnameW, volname);
2125 else volnameW.Buffer = NULL;
2127 ret = SetVolumeLabelW( rootW.Buffer, volnameW.Buffer );
2129 RtlFreeUnicodeString(&rootW);
2130 RtlFreeUnicodeString(&volnameW);
2131 return ret;
2134 /***********************************************************************
2135 * GetVolumeNameForVolumeMountPointW (KERNEL32.@)
2137 BOOL WINAPI GetVolumeNameForVolumeMountPointW(LPCWSTR str, LPWSTR dst, DWORD size)
2139 FIXME("(%s, %p, %lx): stub\n", debugstr_w(str), dst, size);
2140 return 0;