Added graphical feedback.
[wine.git] / files / drive.c
blobe2ff7f27f2fcd1ead41cc22bdb5251fa5dcabad3
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 "wine/unicode.h"
73 #include "wine/library.h"
74 #include "wine/server.h"
75 #include "wine/debug.h"
77 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
78 WINE_DECLARE_DEBUG_CHANNEL(file);
80 typedef struct
82 char *root; /* root dir in Unix format without trailing / */
83 LPWSTR dos_cwd; /* cwd in DOS format without leading or trailing \ */
84 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
85 char *device; /* raw device path */
86 WCHAR label_conf[12]; /* drive label as cfg'd in wine config */
87 WCHAR label_read[12]; /* drive label as read from device */
88 DWORD serial_conf; /* drive serial number as cfg'd in wine config */
89 UINT type; /* drive type */
90 UINT flags; /* drive flags */
91 dev_t dev; /* unix device number */
92 ino_t ino; /* unix inode number */
93 } DOSDRIVE;
96 static const WCHAR DRIVE_Types[][8] =
98 { 0 }, /* DRIVE_UNKNOWN */
99 { 0 }, /* DRIVE_NO_ROOT_DIR */
100 {'f','l','o','p','p','y',0}, /* DRIVE_REMOVABLE */
101 {'h','d',0}, /* DRIVE_FIXED */
102 {'n','e','t','w','o','r','k',0}, /* DRIVE_REMOTE */
103 {'c','d','r','o','m',0}, /* DRIVE_CDROM */
104 {'r','a','m','d','i','s','k',0} /* DRIVE_RAMDISK */
108 /* Known filesystem types */
110 typedef struct
112 const WCHAR name[6];
113 UINT flags;
114 } FS_DESCR;
116 static const FS_DESCR DRIVE_Filesystems[] =
118 { {'u','n','i','x',0}, DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
119 { {'m','s','d','o','s',0}, DRIVE_SHORT_NAMES },
120 { {'d','o','s',0}, DRIVE_SHORT_NAMES },
121 { {'f','a','t',0}, DRIVE_SHORT_NAMES },
122 { {'v','f','a','t',0}, DRIVE_CASE_PRESERVING },
123 { {'w','i','n','9','5',0}, DRIVE_CASE_PRESERVING },
124 { { 0 }, 0 }
128 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
129 static int DRIVE_CurDrive = -1;
131 static HTASK16 DRIVE_LastTask = 0;
133 /* strdup on the process heap */
134 inline static char *heap_strdup( const char *str )
136 INT len = strlen(str) + 1;
137 LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
138 if (p) memcpy( p, str, len );
139 return p;
142 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
144 extern void CDROM_InitRegistry(int dev, int id, const char *device);
146 /***********************************************************************
147 * DRIVE_GetDriveType
149 static inline UINT DRIVE_GetDriveType( INT drive, LPCWSTR value )
151 int i;
153 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
155 if (!strcmpiW( value, DRIVE_Types[i] )) return i;
157 MESSAGE("Drive %c: unknown drive type %s, defaulting to 'hd'.\n",
158 'A' + drive, debugstr_w(value) );
159 return DRIVE_FIXED;
163 /***********************************************************************
164 * DRIVE_GetFSFlags
166 static UINT DRIVE_GetFSFlags( INT drive, LPCWSTR value )
168 const FS_DESCR *descr;
170 for (descr = DRIVE_Filesystems; *descr->name; descr++)
171 if (!strcmpiW( value, descr->name )) return descr->flags;
172 MESSAGE("Drive %c: unknown filesystem type %s, defaulting to 'win95'.\n",
173 'A' + drive, debugstr_w(value) );
174 return DRIVE_CASE_PRESERVING;
178 /***********************************************************************
179 * DRIVE_Init
181 int DRIVE_Init(void)
183 int i, len, count = 0;
184 WCHAR driveW[] = {'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
185 'W','i','n','e','\\','W','i','n','e','\\',
186 'C','o','n','f','i','g','\\','D','r','i','v','e',' ','A',0};
187 WCHAR drive_env[] = {'=','A',':',0};
188 WCHAR path[MAX_PATHNAME_LEN];
189 char tmp[MAX_PATHNAME_LEN*sizeof(WCHAR) + sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
190 struct stat drive_stat_buffer;
191 WCHAR *p;
192 DOSDRIVE *drive;
193 HKEY hkey;
194 DWORD dummy;
195 OBJECT_ATTRIBUTES attr;
196 UNICODE_STRING nameW;
198 static const WCHAR PathW[] = {'P','a','t','h',0};
199 static const WCHAR LabelW[] = {'L','a','b','e','l',0};
200 static const WCHAR SerialW[] = {'S','e','r','i','a','l',0};
201 static const WCHAR TypeW[] = {'T','y','p','e',0};
202 static const WCHAR FilesystemW[] = {'F','i','l','e','s','y','s','t','e','m',0};
203 static const WCHAR DeviceW[] = {'D','e','v','i','c','e',0};
204 static const WCHAR ReadVolInfoW[] = {'R','e','a','d','V','o','l','I','n','f','o',0};
205 static const WCHAR FailReadOnlyW[] = {'F','a','i','l','R','e','a','d','O','n','l','y',0};
206 static const WCHAR driveC_labelW[] = {'D','r','i','v','e',' ','C',' ',' ',' ',' ',0};
209 attr.Length = sizeof(attr);
210 attr.RootDirectory = 0;
211 attr.ObjectName = &nameW;
212 attr.Attributes = 0;
213 attr.SecurityDescriptor = NULL;
214 attr.SecurityQualityOfService = NULL;
216 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
218 RtlInitUnicodeString( &nameW, driveW );
219 nameW.Buffer[(nameW.Length / sizeof(WCHAR)) - 1] = 'A' + i;
220 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ) != STATUS_SUCCESS) continue;
222 /* Get the root path */
223 RtlInitUnicodeString( &nameW, PathW );
224 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
226 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
227 ExpandEnvironmentStringsW( data, path, sizeof(path)/sizeof(WCHAR) );
229 p = path + strlenW(path) - 1;
230 while ((p > path) && (*p == '/')) *p-- = '\0';
232 if (path[0] == '/')
234 len = WideCharToMultiByte(CP_UNIXCP, 0, path, -1, NULL, 0, NULL, NULL);
235 drive->root = HeapAlloc(GetProcessHeap(), 0, len);
236 WideCharToMultiByte(CP_UNIXCP, 0, path, -1, drive->root, len, NULL, NULL);
238 else
240 /* relative paths are relative to config dir */
241 const char *config = wine_get_config_dir();
242 len = strlen(config);
243 len += WideCharToMultiByte(CP_UNIXCP, 0, path, -1, NULL, 0, NULL, NULL) + 2;
244 drive->root = HeapAlloc( GetProcessHeap(), 0, len );
245 len -= sprintf( drive->root, "%s/", config );
246 WideCharToMultiByte(CP_UNIXCP, 0, path, -1,
247 drive->root + strlen(drive->root), len, NULL, NULL);
250 if (stat( drive->root, &drive_stat_buffer ))
252 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
253 drive->root, strerror(errno), 'A' + i);
254 HeapFree( GetProcessHeap(), 0, drive->root );
255 drive->root = NULL;
256 goto next;
258 if (!S_ISDIR(drive_stat_buffer.st_mode))
260 MESSAGE("%s is not a directory, ignoring drive %c:\n",
261 drive->root, 'A' + i );
262 HeapFree( GetProcessHeap(), 0, drive->root );
263 drive->root = NULL;
264 goto next;
267 drive->dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(drive->dos_cwd[0]));
268 drive->unix_cwd = heap_strdup( "" );
269 drive->device = NULL;
270 drive->flags = 0;
271 drive->dev = drive_stat_buffer.st_dev;
272 drive->ino = drive_stat_buffer.st_ino;
274 /* Get the drive type */
275 RtlInitUnicodeString( &nameW, TypeW );
276 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
278 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
279 drive->type = DRIVE_GetDriveType( i, data );
281 else drive->type = DRIVE_FIXED;
283 /* Get the drive label */
284 RtlInitUnicodeString( &nameW, LabelW );
285 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
287 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
288 lstrcpynW( drive->label_conf, data, 12 );
290 if ((len = strlenW(drive->label_conf)) < 11)
292 /* Pad label with spaces */
293 while(len < 11) drive->label_conf[len++] = ' ';
294 drive->label_conf[11] = '\0';
297 /* Get the serial number */
298 RtlInitUnicodeString( &nameW, SerialW );
299 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
301 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
302 drive->serial_conf = strtoulW( data, NULL, 16 );
304 else drive->serial_conf = 12345678;
306 /* Get the filesystem type */
307 RtlInitUnicodeString( &nameW, FilesystemW );
308 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
310 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
311 drive->flags = DRIVE_GetFSFlags( i, data );
313 else drive->flags = DRIVE_CASE_PRESERVING;
315 /* Get the device */
316 RtlInitUnicodeString( &nameW, DeviceW );
317 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
319 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
320 len = WideCharToMultiByte(CP_UNIXCP, 0, data, -1, NULL, 0, NULL, NULL);
321 drive->device = HeapAlloc(GetProcessHeap(), 0, len);
322 WideCharToMultiByte(CP_UNIXCP, 0, data, -1, drive->device, len, NULL, NULL);
324 RtlInitUnicodeString( &nameW, ReadVolInfoW );
325 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
327 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
328 if (IS_OPTION_TRUE(data[0])) drive->flags |= DRIVE_READ_VOL_INFO;
330 else drive->flags |= DRIVE_READ_VOL_INFO;
332 if (drive->type == DRIVE_CDROM)
334 int cd_fd;
335 if ((cd_fd = open(drive->device, O_RDONLY|O_NONBLOCK)) != -1)
337 CDROM_InitRegistry(cd_fd, i, drive->device);
338 close(cd_fd);
343 /* Get the FailReadOnly flag */
344 RtlInitUnicodeString( &nameW, FailReadOnlyW );
345 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
347 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
348 if (IS_OPTION_TRUE(data[0])) drive->flags |= DRIVE_FAIL_READ_ONLY;
351 /* Make the first hard disk the current drive */
352 if ((DRIVE_CurDrive == -1) && (drive->type == DRIVE_FIXED))
353 DRIVE_CurDrive = i;
355 count++;
356 TRACE("Drive %c: path=%s type=%s label=%s serial=%08lx "
357 "flags=%08x dev=%x ino=%x\n",
358 'A' + i, drive->root, debugstr_w(DRIVE_Types[drive->type]),
359 debugstr_w(drive->label_conf), drive->serial_conf, drive->flags,
360 (int)drive->dev, (int)drive->ino );
363 next:
364 NtClose( hkey );
367 if (!count)
369 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
370 /* Create a C drive pointing to Unix root dir */
371 DOSDrives[2].root = heap_strdup( "/" );
372 DOSDrives[2].dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DOSDrives[2].dos_cwd[0]));
373 DOSDrives[2].unix_cwd = heap_strdup( "" );
374 strcpyW( DOSDrives[2].label_conf, driveC_labelW );
375 DOSDrives[2].serial_conf = 12345678;
376 DOSDrives[2].type = DRIVE_FIXED;
377 DOSDrives[2].device = NULL;
378 DOSDrives[2].flags = 0;
379 DRIVE_CurDrive = 2;
382 /* Make sure the current drive is valid */
383 if (DRIVE_CurDrive == -1)
385 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
387 if (drive->root && !(drive->flags & DRIVE_DISABLED))
389 DRIVE_CurDrive = i;
390 break;
395 /* get current working directory info for all drives */
396 for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
398 if (!GetEnvironmentVariableW(drive_env, path, MAX_PATHNAME_LEN)) continue;
399 /* sanity check */
400 if (toupperW(path[0]) != drive_env[1] || path[1] != ':') continue;
401 DRIVE_Chdir( i, path + 2 );
403 return 1;
407 /***********************************************************************
408 * DRIVE_IsValid
410 int DRIVE_IsValid( int drive )
412 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
413 return (DOSDrives[drive].root &&
414 !(DOSDrives[drive].flags & DRIVE_DISABLED));
418 /***********************************************************************
419 * DRIVE_GetCurrentDrive
421 int DRIVE_GetCurrentDrive(void)
423 TDB *pTask = GlobalLock16(GetCurrentTask());
424 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
425 return DRIVE_CurDrive;
429 /***********************************************************************
430 * DRIVE_SetCurrentDrive
432 int DRIVE_SetCurrentDrive( int drive )
434 TDB *pTask = GlobalLock16(GetCurrentTask());
435 if (!DRIVE_IsValid( drive ))
437 SetLastError( ERROR_INVALID_DRIVE );
438 return 0;
440 TRACE("%c:\n", 'A' + drive );
441 DRIVE_CurDrive = drive;
442 if (pTask) pTask->curdrive = drive | 0x80;
443 return 1;
447 /***********************************************************************
448 * DRIVE_FindDriveRoot
450 * Find a drive for which the root matches the beginning of the given path.
451 * This can be used to translate a Unix path into a drive + DOS path.
452 * Return value is the drive, or -1 on error. On success, path is modified
453 * to point to the beginning of the DOS path.
455 * Note: path must be in the encoding of the underlying Unix file system.
457 int DRIVE_FindDriveRoot( const char **path )
459 /* Starting with the full path, check if the device and inode match any of
460 * the wine 'drives'. If not then remove the last path component and try
461 * again. If the last component was a '..' then skip a normal component
462 * since it's a directory that's ascended back out of.
464 int drive, level, len;
465 char buffer[MAX_PATHNAME_LEN];
466 char *p;
467 struct stat st;
469 strcpy( buffer, *path );
470 for (p = buffer; *p; p++) if (*p == '\\') *p = '/';
471 len = p - buffer;
473 /* strip off trailing slashes */
474 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
476 for (;;)
478 /* Find the drive */
479 if (stat( buffer, &st ) == 0 && S_ISDIR( st.st_mode ))
481 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
483 if (!DOSDrives[drive].root ||
484 (DOSDrives[drive].flags & DRIVE_DISABLED))
485 continue;
487 if ((DOSDrives[drive].dev == st.st_dev) &&
488 (DOSDrives[drive].ino == st.st_ino))
490 if (len == 1) len = 0; /* preserve root slash in returned path */
491 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
492 *path, 'A' + drive, buffer, *path + len);
493 *path += len;
494 if (!**path) *path = "\\";
495 return drive;
499 if (len <= 1) return -1; /* reached root */
501 level = 0;
502 while (level < 1)
504 /* find start of the last path component */
505 while (len > 1 && buffer[len - 1] != '/') len--;
506 if (!buffer[len]) break; /* empty component -> reached root */
507 /* does removing it take us up a level? */
508 if (strcmp( buffer + len, "." ) != 0)
509 level += strcmp( buffer + len, ".." ) ? 1 : -1;
510 buffer[len] = 0;
511 /* strip off trailing slashes */
512 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
518 /***********************************************************************
519 * DRIVE_FindDriveRootW
521 * Unicode version of DRIVE_FindDriveRoot.
523 int DRIVE_FindDriveRootW( LPCWSTR *path )
525 int drive, level, len;
526 WCHAR buffer[MAX_PATHNAME_LEN];
527 WCHAR *p;
528 struct stat st;
530 strcpyW( buffer, *path );
531 for (p = buffer; *p; p++) if (*p == '\\') *p = '/';
532 len = p - buffer;
534 /* strip off trailing slashes */
535 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
537 for (;;)
539 char buffA[MAX_PATHNAME_LEN];
541 WideCharToMultiByte( CP_UNIXCP, 0, buffer, -1, buffA, sizeof(buffA), NULL, NULL );
542 if (stat( buffA, &st ) == 0 && S_ISDIR( st.st_mode ))
544 /* Find the drive */
545 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
547 if (!DOSDrives[drive].root ||
548 (DOSDrives[drive].flags & DRIVE_DISABLED))
549 continue;
551 if ((DOSDrives[drive].dev == st.st_dev) &&
552 (DOSDrives[drive].ino == st.st_ino))
554 static const WCHAR rootW[] = {'\\',0};
556 if (len == 1) len = 0; /* preserve root slash in returned path */
557 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
558 debugstr_w(*path), 'A' + drive, debugstr_w(buffer), debugstr_w(*path + len));
559 *path += len;
560 if (!**path) *path = rootW;
561 return drive;
565 if (len <= 1) return -1; /* reached root */
567 level = 0;
568 while (level < 1)
570 static const WCHAR dotW[] = {'.',0};
571 static const WCHAR dotdotW[] = {'.','.',0};
573 /* find start of the last path component */
574 while (len > 1 && buffer[len - 1] != '/') len--;
575 if (!buffer[len]) break; /* empty component -> reached root */
576 /* does removing it take us up a level? */
577 if (strcmpW( buffer + len, dotW ) != 0)
578 level += strcmpW( buffer + len, dotdotW ) ? 1 : -1;
579 buffer[len] = 0;
580 /* strip off trailing slashes */
581 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
587 /***********************************************************************
588 * DRIVE_GetRoot
590 const char * DRIVE_GetRoot( int drive )
592 if (!DRIVE_IsValid( drive )) return NULL;
593 return DOSDrives[drive].root;
597 /***********************************************************************
598 * DRIVE_GetDosCwd
600 LPCWSTR DRIVE_GetDosCwd( int drive )
602 TDB *pTask = GlobalLock16(GetCurrentTask());
603 if (!DRIVE_IsValid( drive )) return NULL;
605 /* Check if we need to change the directory to the new task. */
606 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
607 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
608 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
610 static const WCHAR rootW[] = {'\\',0};
611 WCHAR curdirW[MAX_PATH];
612 MultiByteToWideChar(CP_ACP, 0, pTask->curdir, -1, curdirW, MAX_PATH);
613 /* Perform the task-switch */
614 if (!DRIVE_Chdir( drive, curdirW )) DRIVE_Chdir( drive, rootW );
615 DRIVE_LastTask = GetCurrentTask();
617 return DOSDrives[drive].dos_cwd;
621 /***********************************************************************
622 * DRIVE_GetUnixCwd
624 const char * DRIVE_GetUnixCwd( 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].unix_cwd;
645 /***********************************************************************
646 * DRIVE_GetDevice
648 const char * DRIVE_GetDevice( int drive )
650 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
653 /******************************************************************
654 * static WORD CDROM_Data_FindBestVoldesc
658 static WORD CDROM_Data_FindBestVoldesc(int fd)
660 BYTE cur_vd_type, max_vd_type = 0;
661 unsigned int offs, best_offs = 0, extra_offs = 0;
662 char sig[3];
664 for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
666 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
667 * the volume label is displaced forward by 8
669 lseek(fd, offs + 11, SEEK_SET); /* check for non-ISO9660 signature */
670 read(fd, &sig, 3);
671 if ((sig[0] == 'R') && (sig[1] == 'O') && (sig[2]=='M'))
673 extra_offs = 8;
675 lseek(fd, offs + extra_offs, SEEK_SET);
676 read(fd, &cur_vd_type, 1);
677 if (cur_vd_type == 0xff) /* voldesc set terminator */
678 break;
679 if (cur_vd_type > max_vd_type)
681 max_vd_type = cur_vd_type;
682 best_offs = offs + extra_offs;
685 return best_offs;
688 /***********************************************************************
689 * DRIVE_ReadSuperblock
691 * NOTE
692 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
693 * to check, that they are writing on a FAT filesystem !
695 int DRIVE_ReadSuperblock (int drive, char * buff)
697 #define DRIVE_SUPER 96
698 int fd;
699 off_t offs;
700 int ret = 0;
701 struct stat stat_buf;
703 memset(buff, 0, DRIVE_SUPER);
704 /* O_NONBLOCK in case we're opening FIFO; will be reset later */
705 if ((fd = open(DOSDrives[drive].device, O_RDONLY|O_NOCTTY|O_NONBLOCK)) != -1) {
706 if (fstat(fd, &stat_buf) < 0) { /* shouldn't happen since we just opened that file */
707 ERR("fstat() failed for opened device '%s' (drive %c:) ! IT SHOULDN'T HAPPEN !!!\n",
708 DOSDrives[drive].device, 'A'+drive);
709 ret = -1;
710 } else if (!S_ISBLK(stat_buf.st_mode)) {
711 ERR("Device '%s' (drive %c:) is not a block device - check your config\n",
712 DOSDrives[drive].device, 'A'+drive);
713 ret = -1;
714 /* reset O_NONBLOCK */
715 } else if (fcntl(fd, F_SETFL, 0) < 0 || fcntl(fd, F_GETFL) & O_NONBLOCK) {
716 ERR("fcntl() failed to reset O_NONBLOCK for device '%s' (drive %c:)\n",
717 DOSDrives[drive].device, 'A'+drive);
718 ret = -1;
720 if (ret) {
721 close(fd);
722 fd = -1;
724 } else {
725 if (!DOSDrives[drive].device)
726 ERR("No device configured for drive %c: !\n", 'A'+drive);
727 else
728 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
729 (stat(DOSDrives[drive].device, &stat_buf)) ?
730 "not available or symlink not valid ?" : "no permission");
732 if (fd == -1) {
733 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
734 return -1;
737 switch(DOSDrives[drive].type)
739 case DRIVE_REMOVABLE:
740 case DRIVE_FIXED:
741 offs = 0;
742 break;
743 case DRIVE_CDROM:
744 offs = CDROM_Data_FindBestVoldesc(fd);
745 break;
746 default:
747 offs = 0;
748 break;
751 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs))
753 ret = -4;
754 goto the_end;
756 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER)
758 ret = -2;
759 goto the_end;
762 switch(DOSDrives[drive].type)
764 case DRIVE_REMOVABLE:
765 case DRIVE_FIXED:
766 if ((buff[0x26]!=0x29) || /* Check for FAT present */
767 /* FIXME: do really all FAT have their name beginning with
768 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
769 memcmp( buff+0x36,"FAT",3))
771 ERR("The filesystem is not FAT !! (device=%s)\n",
772 DOSDrives[drive].device);
773 ret = -3;
774 goto the_end;
776 break;
777 case DRIVE_CDROM:
778 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
780 ret = -3;
781 goto the_end;
783 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
784 break;
785 default:
786 ret = -3;
787 goto the_end;
790 return close(fd);
791 the_end:
792 close(fd);
793 return ret;
797 /***********************************************************************
798 * DRIVE_WriteSuperblockEntry
800 * NOTE
801 * We are writing as little as possible (ie. not the whole SuperBlock)
802 * not to interfere with kernel. The drive can be mounted !
804 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
806 int fd;
808 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
810 ERR("Cannot open the device %s (for writing)\n",
811 DOSDrives[drive].device);
812 return -1;
814 if (lseek(fd,ofs,SEEK_SET)!=ofs)
816 ERR("lseek failed on device %s !\n",
817 DOSDrives[drive].device);
818 close(fd);
819 return -2;
821 if (write(fd,buff,len)!=len)
823 close(fd);
824 ERR("Cannot write on %s !\n",
825 DOSDrives[drive].device);
826 return -3;
828 return close (fd);
831 /******************************************************************
832 * static HANDLE CDROM_Open
836 static HANDLE CDROM_Open(int drive)
838 WCHAR root[] = {'\\','\\','.','\\','A',':',0};
839 root[4] += drive;
840 return CreateFileW(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
843 /**************************************************************************
844 * CDROM_Data_GetLabel [internal]
846 DWORD CDROM_Data_GetLabel(int drive, WCHAR *label)
848 #define LABEL_LEN 32+1
849 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
850 WORD offs = CDROM_Data_FindBestVoldesc(dev);
851 WCHAR label_read[LABEL_LEN]; /* Unicode possible, too */
852 DWORD unicode_id = 0;
854 if (offs)
856 if ((lseek(dev, offs+0x58, SEEK_SET) == offs+0x58)
857 && (read(dev, &unicode_id, 3) == 3))
859 int ver = (unicode_id & 0xff0000) >> 16;
861 if ((lseek(dev, offs+0x28, SEEK_SET) != offs+0x28)
862 || (read(dev, &label_read, LABEL_LEN) != LABEL_LEN))
863 goto failure;
865 close(dev);
866 if ((LOWORD(unicode_id) == 0x2f25) /* Unicode ID */
867 && ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
868 { /* yippee, unicode */
869 int i;
870 WORD ch;
871 for (i=0; i<LABEL_LEN;i++)
872 { /* Motorola -> Intel Unicode conversion :-\ */
873 ch = label_read[i];
874 label_read[i] = (ch << 8) | (ch >> 8);
876 strncpyW(label, label_read, 11);
877 label[11] = 0;
879 else
881 MultiByteToWideChar(CP_UNIXCP, 0, (LPSTR)label_read, -1, label, 11);
882 label[11] = '\0';
884 return 1;
887 failure:
888 close(dev);
889 ERR("error reading label !\n");
890 return 0;
893 /**************************************************************************
894 * CDROM_GetLabel [internal]
896 static DWORD CDROM_GetLabel(int drive, WCHAR *label)
898 HANDLE h;
899 CDROM_DISK_DATA cdd;
900 DWORD br, ret = 1;
901 BOOL r;
903 h = CDROM_Open(drive);
904 if( !h )
905 return 0;
906 r = DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL,
907 0, &cdd, sizeof(cdd), &br, 0);
908 CloseHandle( h );
909 if( !r )
910 return 0;
912 switch (cdd.DiskData & 0x03)
914 case CDROM_DISK_DATA_TRACK:
915 if (!CDROM_Data_GetLabel(drive, label))
916 ret = 0;
917 break;
918 case CDROM_DISK_AUDIO_TRACK:
920 static const WCHAR audioCD[] = {'A','u','d','i','o',' ','C','D',' ',' ',' ',0};
921 strcpyW(label, audioCD);
922 break;
924 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
925 FIXME("Need to get the label of a mixed mode CD!\n");
926 /* This assumes that the first track is a data track! */
927 /* I guess the correct way would be to enumerate all data tracks
928 and check each for the title */
929 if (!CDROM_Data_GetLabel(drive, label))
930 ret = 0;
931 break;
932 case 0:
933 ret = 0;
934 break;
936 TRACE("CD: label is %s\n", debugstr_w(label));
938 return ret;
940 /***********************************************************************
941 * DRIVE_GetLabel
943 LPCWSTR DRIVE_GetLabel( int drive )
945 int read = 0;
946 char buff[DRIVE_SUPER];
947 int offs = -1;
949 if (!DRIVE_IsValid( drive )) return NULL;
950 if (DOSDrives[drive].type == DRIVE_CDROM)
952 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
954 else
955 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
957 if (DRIVE_ReadSuperblock(drive,(char *) buff))
958 ERR("Invalid or unreadable superblock on %s (%c:).\n",
959 DOSDrives[drive].device, (char)(drive+'A'));
960 else {
961 if (DOSDrives[drive].type == DRIVE_REMOVABLE ||
962 DOSDrives[drive].type == DRIVE_FIXED)
963 offs = 0x2b;
965 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
966 if (offs != -1)
967 MultiByteToWideChar(CP_UNIXCP, 0, buff+offs, 11,
968 DOSDrives[drive].label_read, 11);
969 DOSDrives[drive].label_read[11]='\0';
970 read = 1;
974 return (read) ?
975 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
978 #define CDFRAMES_PERSEC 75
979 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
980 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
981 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
983 /**************************************************************************
984 * CDROM_Audio_GetSerial [internal]
986 static DWORD CDROM_Audio_GetSerial(HANDLE h)
988 unsigned long serial = 0;
989 int i;
990 WORD wMagic;
991 DWORD dwStart, dwEnd, br;
992 CDROM_TOC toc;
994 if (!DeviceIoControl(h, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0))
995 return 0;
998 * wMagic collects the wFrames from track 1
999 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
1000 * frames.
1001 * There it is collected for correcting the serial when there are less than
1002 * 3 tracks.
1004 wMagic = toc.TrackData[0].Address[2];
1005 dwStart = FRAME_OF_TOC(toc, toc.FirstTrack);
1007 for (i = 0; i <= toc.LastTrack - toc.FirstTrack; i++) {
1008 serial += (toc.TrackData[i].Address[0] << 16) |
1009 (toc.TrackData[i].Address[1] << 8) | toc.TrackData[i].Address[2];
1011 dwEnd = FRAME_OF_TOC(toc, toc.LastTrack + 1);
1013 if (toc.LastTrack - toc.FirstTrack + 1 < 3)
1014 serial += wMagic + (dwEnd - dwStart);
1016 return serial;
1019 /**************************************************************************
1020 * CDROM_Data_GetSerial [internal]
1022 static DWORD CDROM_Data_GetSerial(int drive)
1024 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
1025 WORD offs;
1026 union {
1027 unsigned long val;
1028 unsigned char p[4];
1029 } serial;
1030 BYTE b0 = 0, b1 = 1, b2 = 2, b3 = 3;
1033 if (dev == -1) return 0;
1034 offs = CDROM_Data_FindBestVoldesc(dev);
1036 serial.val = 0;
1037 if (offs)
1039 BYTE buf[2048];
1040 RTL_OSVERSIONINFOEXW ovi;
1041 int i;
1043 lseek(dev, offs, SEEK_SET);
1044 read(dev, buf, 2048);
1046 * OK, another braindead one... argh. Just believe it.
1047 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
1048 * It's true and nobody will ever be able to change it.
1050 ovi.dwOSVersionInfoSize = sizeof(ovi);
1051 RtlGetVersion(&ovi);
1052 if ((ovi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (ovi.dwMajorVersion >= 4))
1054 b0 = 3; b1 = 2; b2 = 1; b3 = 0;
1056 for (i = 0; i < 2048; i += 4)
1058 /* DON'T optimize this into DWORD !! (breaks overflow) */
1059 serial.p[b0] += buf[i+b0];
1060 serial.p[b1] += buf[i+b1];
1061 serial.p[b2] += buf[i+b2];
1062 serial.p[b3] += buf[i+b3];
1065 close(dev);
1066 return serial.val;
1069 /**************************************************************************
1070 * CDROM_GetSerial [internal]
1072 static DWORD CDROM_GetSerial(int drive)
1074 DWORD serial = 0;
1075 HANDLE h;
1076 CDROM_DISK_DATA cdd;
1077 DWORD br;
1078 BOOL r;
1080 TRACE("%d\n", drive);
1082 h = CDROM_Open(drive);
1083 if( !h )
1084 return 0;
1085 r = DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL,
1086 0, &cdd, sizeof(cdd), &br, 0);
1087 if (!r)
1089 CloseHandle(h);
1090 return 0;
1093 switch (cdd.DiskData & 0x03)
1095 case CDROM_DISK_DATA_TRACK:
1096 /* hopefully a data CD */
1097 serial = CDROM_Data_GetSerial(drive);
1098 break;
1099 case CDROM_DISK_AUDIO_TRACK:
1100 /* fall thru */
1101 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
1102 serial = CDROM_Audio_GetSerial(h);
1103 break;
1104 case 0:
1105 break;
1108 if (serial)
1109 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial), LOWORD(serial));
1111 CloseHandle(h);
1113 return serial;
1116 /***********************************************************************
1117 * DRIVE_GetSerialNumber
1119 DWORD DRIVE_GetSerialNumber( int drive )
1121 DWORD serial = 0;
1122 char buff[DRIVE_SUPER];
1124 TRACE("drive %d, type = %d\n", drive, DOSDrives[drive].type);
1126 if (!DRIVE_IsValid( drive )) return 0;
1128 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
1130 switch(DOSDrives[drive].type)
1132 case DRIVE_REMOVABLE:
1133 case DRIVE_FIXED:
1134 if (DRIVE_ReadSuperblock(drive,(char *) buff))
1135 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
1136 " Maybe not FAT?\n" ,
1137 DOSDrives[drive].device, 'A'+drive);
1138 else
1139 serial = *((DWORD*)(buff+0x27));
1140 break;
1141 case DRIVE_CDROM:
1142 serial = CDROM_GetSerial(drive);
1143 break;
1144 default:
1145 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
1149 return (serial) ? serial : DOSDrives[drive].serial_conf;
1153 /***********************************************************************
1154 * DRIVE_SetSerialNumber
1156 int DRIVE_SetSerialNumber( int drive, DWORD serial )
1158 char buff[DRIVE_SUPER];
1160 if (!DRIVE_IsValid( drive )) return 0;
1162 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
1164 if ((DOSDrives[drive].type != DRIVE_REMOVABLE) &&
1165 (DOSDrives[drive].type != DRIVE_FIXED)) return 0;
1166 /* check, if the drive has a FAT filesystem */
1167 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
1168 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
1169 return 1;
1172 if (DOSDrives[drive].type == DRIVE_CDROM) return 0;
1173 DOSDrives[drive].serial_conf = serial;
1174 return 1;
1178 /***********************************************************************
1179 * DRIVE_GetType
1181 static UINT DRIVE_GetType( int drive )
1183 if (!DRIVE_IsValid( drive )) return DRIVE_NO_ROOT_DIR;
1184 return DOSDrives[drive].type;
1188 /***********************************************************************
1189 * DRIVE_GetFlags
1191 UINT DRIVE_GetFlags( int drive )
1193 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
1194 return DOSDrives[drive].flags;
1197 /***********************************************************************
1198 * DRIVE_Chdir
1200 int DRIVE_Chdir( int drive, LPCWSTR path )
1202 DOS_FULL_NAME full_name;
1203 WCHAR buffer[MAX_PATHNAME_LEN];
1204 LPSTR unix_cwd;
1205 BY_HANDLE_FILE_INFORMATION info;
1206 TDB *pTask = GlobalLock16(GetCurrentTask());
1208 buffer[0] = 'A' + drive;
1209 buffer[1] = ':';
1210 buffer[2] = 0;
1211 TRACE("(%s,%s)\n", debugstr_w(buffer), debugstr_w(path) );
1212 strncpyW( buffer + 2, path, MAX_PATHNAME_LEN - 2 );
1213 buffer[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1215 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
1216 if (!FILE_Stat( full_name.long_name, &info, NULL )) return 0;
1217 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1219 SetLastError( ERROR_FILE_NOT_FOUND );
1220 return 0;
1222 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
1223 while (*unix_cwd == '/') unix_cwd++;
1225 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
1226 'A' + drive, unix_cwd, debugstr_w(full_name.short_name + 3) );
1228 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
1229 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
1230 DOSDrives[drive].dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name.short_name) - 2) * sizeof(WCHAR));
1231 strcpyW(DOSDrives[drive].dos_cwd, full_name.short_name + 3);
1232 DOSDrives[drive].unix_cwd = heap_strdup( unix_cwd );
1234 if (drive == DRIVE_CurDrive)
1236 UNICODE_STRING dirW;
1238 RtlInitUnicodeString( &dirW, full_name.short_name );
1239 RtlSetCurrentDirectory_U( &dirW );
1242 if (pTask && (pTask->curdrive & 0x80) &&
1243 ((pTask->curdrive & ~0x80) == drive))
1245 WideCharToMultiByte(CP_ACP, 0, full_name.short_name + 2, -1,
1246 pTask->curdir, sizeof(pTask->curdir), NULL, NULL);
1247 DRIVE_LastTask = GetCurrentTask();
1249 return 1;
1253 /***********************************************************************
1254 * DRIVE_Disable
1256 int DRIVE_Disable( int drive )
1258 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1260 SetLastError( ERROR_INVALID_DRIVE );
1261 return 0;
1263 DOSDrives[drive].flags |= DRIVE_DISABLED;
1264 return 1;
1268 /***********************************************************************
1269 * DRIVE_Enable
1271 int DRIVE_Enable( int drive )
1273 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1275 SetLastError( ERROR_INVALID_DRIVE );
1276 return 0;
1278 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
1279 return 1;
1283 /***********************************************************************
1284 * DefineDosDeviceA (KERNEL32.@)
1286 BOOL WINAPI DefineDosDeviceA(DWORD flags,LPCSTR devname,LPCSTR targetpath)
1288 UNICODE_STRING d, t;
1289 BOOL ret;
1291 if (!RtlCreateUnicodeStringFromAsciiz(&d, devname))
1293 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1294 return FALSE;
1296 if (!RtlCreateUnicodeStringFromAsciiz(&t, targetpath))
1298 RtlFreeUnicodeString(&d);
1299 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1300 return FALSE;
1302 ret = DefineDosDeviceW(flags, d.Buffer, t.Buffer);
1303 RtlFreeUnicodeString(&d);
1304 RtlFreeUnicodeString(&t);
1305 return ret;
1309 /***********************************************************************
1310 * DefineDosDeviceA (KERNEL32.@)
1312 BOOL WINAPI DefineDosDeviceW(DWORD flags,LPCWSTR devname,LPCWSTR targetpath)
1314 DOSDRIVE *old, *new;
1316 /* this is a temporary hack for int21 support. better implementation has to be done */
1317 if (flags != DDD_RAW_TARGET_PATH ||
1318 !(toupperW(devname[0]) >= 'A' && toupperW(devname[0]) <= 'Z') ||
1319 devname[1] != ':' || devname[2] != 0 ||
1320 !(toupperW(targetpath[0]) >= 'A' && toupperW(targetpath[0]) <= 'Z') ||
1321 targetpath[1] != ':' || targetpath[2] != '\\' || targetpath[3] != 0)
1323 FIXME("(0x%08lx,%s,%s),stub!\n", flags, debugstr_w(devname), debugstr_w(targetpath));
1324 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1325 return FALSE;
1328 old = DOSDrives + devname[0] - 'A';
1329 new = DOSDrives + targetpath[0] - 'A';
1331 if (!old->root)
1333 SetLastError( ERROR_INVALID_DRIVE );
1334 return 0;
1337 if ( new->root )
1339 TRACE("Can't map drive %c: to already existing drive %c:\n",
1340 devname[0], targetpath[0] );
1341 /* it is already mapped there, so return success */
1342 if (!strcmp(old->root,new->root))
1343 return 1;
1344 return 0;
1347 new->root = heap_strdup( old->root );
1348 new->dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(old->dos_cwd) + 1) * sizeof(WCHAR));
1349 strcpyW(new->dos_cwd, old->dos_cwd);
1350 new->unix_cwd = heap_strdup( old->unix_cwd );
1351 new->device = heap_strdup( old->device );
1352 memcpy ( new->label_conf, old->label_conf, 12 );
1353 memcpy ( new->label_read, old->label_read, 12 );
1354 new->serial_conf = old->serial_conf;
1355 new->type = old->type;
1356 new->flags = old->flags;
1357 new->dev = old->dev;
1358 new->ino = old->ino;
1360 TRACE("Drive %c: is now equal to drive %c:\n",
1361 targetpath[0], devname[0] );
1363 return 1;
1367 /***********************************************************************
1368 * DRIVE_GetFreeSpace
1370 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
1371 PULARGE_INTEGER available )
1373 struct statfs info;
1375 if (!DRIVE_IsValid(drive))
1377 SetLastError( ERROR_PATH_NOT_FOUND );
1378 return 0;
1381 /* FIXME: add autoconf check for this */
1382 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1383 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
1384 #else
1385 if (statfs( DOSDrives[drive].root, &info) < 0)
1386 #endif
1388 FILE_SetDosError();
1389 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
1390 return 0;
1393 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bsize, info.f_blocks );
1394 #ifdef HAVE_STRUCT_STATFS_F_BAVAIL
1395 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bavail, info.f_bsize );
1396 #else
1397 # ifdef HAVE_STRUCT_STATFS_F_BFREE
1398 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bfree, info.f_bsize );
1399 # else
1400 # error "statfs has no bfree/bavail member!"
1401 # endif
1402 #endif
1403 if (DOSDrives[drive].type == DRIVE_CDROM)
1404 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1405 available->QuadPart = 0;
1407 return 1;
1410 /***********************************************************************
1411 * DRIVE_GetCurrentDirectory
1412 * Returns "X:\\path\\etc\\".
1414 * Despite the API description, return required length including the
1415 * terminating null when buffer too small. This is the real behaviour.
1417 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPWSTR buf )
1419 UINT ret;
1420 LPCWSTR dos_cwd = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1421 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1423 ret = strlenW(dos_cwd) + 3; /* length of WHOLE current directory */
1424 if (ret >= buflen) return ret + 1;
1426 strcpyW( buf, driveA_rootW );
1427 buf[0] += DRIVE_GetCurrentDrive();
1428 strcatW( buf, dos_cwd );
1429 return ret;
1433 /***********************************************************************
1434 * DRIVE_BuildEnv
1436 * Build the environment array containing the drives' current directories.
1437 * Resulting pointer must be freed with HeapFree.
1439 WCHAR *DRIVE_BuildEnv(void)
1441 int i, length = 0;
1442 LPCWSTR cwd[MAX_DOS_DRIVES];
1443 WCHAR *env, *p;
1445 for (i = 0; i < MAX_DOS_DRIVES; i++)
1447 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0])
1448 length += strlenW(cwd[i]) + 8;
1450 if (!(env = HeapAlloc( GetProcessHeap(), 0, (length+1) * sizeof(WCHAR) ))) return NULL;
1451 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
1453 if (cwd[i] && cwd[i][0])
1455 *p++ = '='; *p++ = 'A' + i; *p++ = ':';
1456 *p++ = '='; *p++ = 'A' + i; *p++ = ':'; *p++ = '\\';
1457 strcpyW( p, cwd[i] );
1458 p += strlenW(p) + 1;
1461 *p = 0;
1462 return env;
1466 /***********************************************************************
1467 * GetDiskFreeSpace (KERNEL.422)
1469 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
1470 LPDWORD sector_bytes, LPDWORD free_clusters,
1471 LPDWORD total_clusters )
1473 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
1474 free_clusters, total_clusters );
1478 /***********************************************************************
1479 * GetDiskFreeSpaceW (KERNEL32.@)
1481 * Fails if expression resulting from current drive's dir and "root"
1482 * is not a root dir of the target drive.
1484 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1485 * if the corresponding info is unneeded.
1487 * FIXME: needs to support UNC names from Win95 OSR2 on.
1489 * Behaviour under Win95a:
1490 * CurrDir root result
1491 * "E:\\TEST" "E:" FALSE
1492 * "E:\\" "E:" TRUE
1493 * "E:\\" "E" FALSE
1494 * "E:\\" "\\" TRUE
1495 * "E:\\TEST" "\\" TRUE
1496 * "E:\\TEST" ":\\" FALSE
1497 * "E:\\TEST" "E:\\" TRUE
1498 * "E:\\TEST" "" FALSE
1499 * "E:\\" "" FALSE (!)
1500 * "E:\\" 0x0 TRUE
1501 * "E:\\TEST" 0x0 TRUE (!)
1502 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1503 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1505 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1506 LPDWORD sector_bytes, LPDWORD free_clusters,
1507 LPDWORD total_clusters )
1509 int drive, sec_size;
1510 ULARGE_INTEGER size,available;
1511 LPCWSTR path;
1512 DWORD cluster_sec;
1514 TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root), cluster_sectors, sector_bytes,
1515 free_clusters, total_clusters);
1517 if (!root || root[0] == '\\' || root[0] == '/')
1518 drive = DRIVE_GetCurrentDrive();
1519 else
1520 if (root[0] && root[1] == ':') /* root contains drive tag */
1522 drive = toupperW(root[0]) - 'A';
1523 path = &root[2];
1524 if (path[0] == '\0')
1526 path = DRIVE_GetDosCwd(drive);
1527 if (!path)
1529 SetLastError(ERROR_PATH_NOT_FOUND);
1530 return FALSE;
1533 else
1534 if (path[0] == '\\')
1535 path++;
1537 if (path[0]) /* oops, we are in a subdir */
1539 SetLastError(ERROR_INVALID_NAME);
1540 return FALSE;
1543 else
1545 if (!root[0])
1546 SetLastError(ERROR_PATH_NOT_FOUND);
1547 else
1548 SetLastError(ERROR_INVALID_NAME);
1549 return FALSE;
1552 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1554 /* Cap the size and available at 2GB as per specs. */
1555 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1557 size.s.HighPart = 0;
1558 size.s.LowPart = 0x7fffffff;
1560 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1562 available.s.HighPart =0;
1563 available.s.LowPart = 0x7fffffff;
1565 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
1566 size.s.LowPart /= sec_size;
1567 available.s.LowPart /= sec_size;
1568 /* FIXME: probably have to adjust those variables too for CDFS */
1569 cluster_sec = 1;
1570 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1572 if (cluster_sectors)
1573 *cluster_sectors = cluster_sec;
1574 if (sector_bytes)
1575 *sector_bytes = sec_size;
1576 if (free_clusters)
1577 *free_clusters = available.s.LowPart / cluster_sec;
1578 if (total_clusters)
1579 *total_clusters = size.s.LowPart / cluster_sec;
1580 return TRUE;
1584 /***********************************************************************
1585 * GetDiskFreeSpaceA (KERNEL32.@)
1587 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1588 LPDWORD sector_bytes, LPDWORD free_clusters,
1589 LPDWORD total_clusters )
1591 UNICODE_STRING rootW;
1592 BOOL ret = FALSE;
1594 if (root)
1596 if(!RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1598 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1599 return FALSE;
1602 else
1603 rootW.Buffer = NULL;
1605 ret = GetDiskFreeSpaceW(rootW.Buffer, cluster_sectors, sector_bytes,
1606 free_clusters, total_clusters );
1607 RtlFreeUnicodeString(&rootW);
1609 return ret;
1613 /***********************************************************************
1614 * GetDiskFreeSpaceExW (KERNEL32.@)
1616 * This function is used to acquire the size of the available and
1617 * total space on a logical volume.
1619 * RETURNS
1621 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1622 * detailed error information.
1625 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root,
1626 PULARGE_INTEGER avail,
1627 PULARGE_INTEGER total,
1628 PULARGE_INTEGER totalfree)
1630 int drive;
1631 ULARGE_INTEGER size,available;
1633 if (!root) drive = DRIVE_GetCurrentDrive();
1634 else
1635 { /* C: always works for GetDiskFreeSpaceEx */
1636 if ((root[1]) && ((root[1] != ':') || (root[2] && root[2] != '\\')))
1638 FIXME("there are valid root names which are not supported yet\n");
1639 /* ..like UNC names, for instance. */
1641 WARN("invalid root '%s'\n", debugstr_w(root));
1642 return FALSE;
1644 drive = toupperW(root[0]) - 'A';
1647 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1649 if (total)
1651 total->s.HighPart = size.s.HighPart;
1652 total->s.LowPart = size.s.LowPart;
1655 if (totalfree)
1657 totalfree->s.HighPart = available.s.HighPart;
1658 totalfree->s.LowPart = available.s.LowPart;
1661 if (avail)
1663 if (FIXME_ON(dosfs))
1665 /* On Windows2000, we need to check the disk quota
1666 allocated for the user owning the calling process. We
1667 don't want to be more obtrusive than necessary with the
1668 FIXME messages, so don't print the FIXME unless Wine is
1669 actually masquerading as Windows2000. */
1671 RTL_OSVERSIONINFOEXW ovi;
1672 ovi.dwOSVersionInfoSize = sizeof(ovi);
1673 if (RtlGetVersion(&ovi))
1675 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1676 FIXME("no per-user quota support yet\n");
1680 /* Quick hack, should eventually be fixed to work 100% with
1681 Windows2000 (see comment above). */
1682 avail->s.HighPart = available.s.HighPart;
1683 avail->s.LowPart = available.s.LowPart;
1686 return TRUE;
1689 /***********************************************************************
1690 * GetDiskFreeSpaceExA (KERNEL32.@)
1692 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root, PULARGE_INTEGER avail,
1693 PULARGE_INTEGER total,
1694 PULARGE_INTEGER totalfree)
1696 UNICODE_STRING rootW;
1697 BOOL ret;
1699 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
1700 else rootW.Buffer = NULL;
1702 ret = GetDiskFreeSpaceExW( rootW.Buffer, avail, total, totalfree);
1704 RtlFreeUnicodeString(&rootW);
1705 return ret;
1708 /***********************************************************************
1709 * GetDriveType (KERNEL.136)
1710 * This function returns the type of a drive in Win16.
1711 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1712 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1713 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1714 * do any pseudo-clever changes.
1716 * RETURNS
1717 * drivetype DRIVE_xxx
1719 UINT16 WINAPI GetDriveType16( UINT16 drive ) /* [in] number (NOT letter) of drive */
1721 UINT type = DRIVE_GetType(drive);
1722 TRACE("(%c:)\n", 'A' + drive );
1723 if (type == DRIVE_CDROM) type = DRIVE_REMOTE;
1724 return type;
1728 /***********************************************************************
1729 * GetDriveTypeW (KERNEL32.@)
1731 * Returns the type of the disk drive specified. If root is NULL the
1732 * root of the current directory is used.
1734 * RETURNS
1736 * Type of drive (from Win32 SDK):
1738 * DRIVE_UNKNOWN unable to find out anything about the drive
1739 * DRIVE_NO_ROOT_DIR nonexistent root dir
1740 * DRIVE_REMOVABLE the disk can be removed from the machine
1741 * DRIVE_FIXED the disk can not be removed from the machine
1742 * DRIVE_REMOTE network disk
1743 * DRIVE_CDROM CDROM drive
1744 * DRIVE_RAMDISK virtual disk in RAM
1746 UINT WINAPI GetDriveTypeW(LPCWSTR root) /* [in] String describing drive */
1748 int drive;
1749 TRACE("(%s)\n", debugstr_w(root));
1751 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1752 else
1754 if ((root[1]) && (root[1] != ':'))
1756 WARN("invalid root %s\n", debugstr_w(root));
1757 return DRIVE_NO_ROOT_DIR;
1759 drive = toupperW(root[0]) - 'A';
1761 return DRIVE_GetType(drive);
1765 /***********************************************************************
1766 * GetDriveTypeA (KERNEL32.@)
1768 UINT WINAPI GetDriveTypeA( LPCSTR root )
1770 UNICODE_STRING rootW;
1771 UINT ret = 0;
1773 if (root)
1775 if( !RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1777 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1778 return 0;
1781 else
1782 rootW.Buffer = NULL;
1784 ret = GetDriveTypeW(rootW.Buffer);
1786 RtlFreeUnicodeString(&rootW);
1787 return ret;
1792 /***********************************************************************
1793 * GetCurrentDirectory (KERNEL.411)
1795 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1797 WCHAR cur_dirW[MAX_PATH];
1799 DRIVE_GetCurrentDirectory(MAX_PATH, cur_dirW);
1800 return (UINT16)WideCharToMultiByte(CP_ACP, 0, cur_dirW, -1, buf, buflen, NULL, NULL);
1804 /***********************************************************************
1805 * GetCurrentDirectoryW (KERNEL32.@)
1807 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1809 UINT ret;
1810 WCHAR longname[MAX_PATHNAME_LEN];
1811 WCHAR shortname[MAX_PATHNAME_LEN];
1813 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1814 if ( ret > MAX_PATHNAME_LEN ) {
1815 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1816 return ret;
1818 GetLongPathNameW(shortname, longname, MAX_PATHNAME_LEN);
1819 ret = strlenW( longname ) + 1;
1820 if (ret > buflen) return ret;
1821 strcpyW(buf, longname);
1822 return ret - 1;
1825 /***********************************************************************
1826 * GetCurrentDirectoryA (KERNEL32.@)
1828 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1830 WCHAR bufferW[MAX_PATH];
1831 DWORD ret, retW;
1833 retW = GetCurrentDirectoryW(MAX_PATH, bufferW);
1835 if (!retW)
1836 ret = 0;
1837 else if (retW > MAX_PATH)
1839 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1840 ret = 0;
1842 else
1844 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1845 if (buflen >= ret)
1847 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buf, buflen, NULL, NULL);
1848 ret--; /* length without 0 */
1851 return ret;
1855 /***********************************************************************
1856 * SetCurrentDirectory (KERNEL.412)
1858 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1860 return SetCurrentDirectoryA( dir );
1864 /***********************************************************************
1865 * SetCurrentDirectoryW (KERNEL32.@)
1867 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dir )
1869 int drive, olddrive = DRIVE_GetCurrentDrive();
1871 if (!dir)
1873 SetLastError(ERROR_INVALID_PARAMETER);
1874 return FALSE;
1876 if (dir[0] && (dir[1]==':'))
1878 drive = toupperW( *dir ) - 'A';
1879 dir += 2;
1881 else
1882 drive = olddrive;
1884 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1885 sets pTask->curdir only if pTask->curdrive is drive */
1886 if (!(DRIVE_SetCurrentDrive( drive )))
1887 return FALSE;
1889 /* FIXME: what about empty strings? Add a \\ ? */
1890 if (!DRIVE_Chdir( drive, dir )) {
1891 DRIVE_SetCurrentDrive(olddrive);
1892 return FALSE;
1894 return TRUE;
1898 /***********************************************************************
1899 * SetCurrentDirectoryA (KERNEL32.@)
1901 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1903 UNICODE_STRING dirW;
1904 BOOL ret = FALSE;
1906 if (!dir)
1908 SetLastError(ERROR_INVALID_PARAMETER);
1909 return FALSE;
1912 if (RtlCreateUnicodeStringFromAsciiz(&dirW, dir))
1914 ret = SetCurrentDirectoryW(dirW.Buffer);
1915 RtlFreeUnicodeString(&dirW);
1917 else
1918 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1919 return ret;
1923 /***********************************************************************
1924 * GetLogicalDriveStringsA (KERNEL32.@)
1926 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1928 int drive, count;
1930 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1931 if (DRIVE_IsValid(drive)) count++;
1932 if ((count * 4) + 1 <= len)
1934 LPSTR p = buffer;
1935 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1936 if (DRIVE_IsValid(drive))
1938 *p++ = 'a' + drive;
1939 *p++ = ':';
1940 *p++ = '\\';
1941 *p++ = '\0';
1943 *p = '\0';
1944 return count * 4;
1946 else
1947 return (count * 4) + 1; /* account for terminating null */
1948 /* The API tells about these different return values */
1952 /***********************************************************************
1953 * GetLogicalDriveStringsW (KERNEL32.@)
1955 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1957 int drive, count;
1959 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1960 if (DRIVE_IsValid(drive)) count++;
1961 if (count * 4 * sizeof(WCHAR) <= len)
1963 LPWSTR p = buffer;
1964 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1965 if (DRIVE_IsValid(drive))
1967 *p++ = (WCHAR)('a' + drive);
1968 *p++ = (WCHAR)':';
1969 *p++ = (WCHAR)'\\';
1970 *p++ = (WCHAR)'\0';
1972 *p = (WCHAR)'\0';
1974 return count * 4 * sizeof(WCHAR);
1978 /***********************************************************************
1979 * GetLogicalDrives (KERNEL32.@)
1981 DWORD WINAPI GetLogicalDrives(void)
1983 DWORD ret = 0;
1984 int drive;
1986 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1988 if ( (DRIVE_IsValid(drive)) ||
1989 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1990 ret |= (1 << drive);
1992 return ret;
1996 /***********************************************************************
1997 * GetVolumeInformationW (KERNEL32.@)
1999 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
2000 DWORD label_len, DWORD *serial,
2001 DWORD *filename_len, DWORD *flags,
2002 LPWSTR fsname, DWORD fsname_len )
2004 int drive;
2005 LPWSTR cp;
2007 /* FIXME, SetLastError()s missing */
2009 if (!root) drive = DRIVE_GetCurrentDrive();
2010 else
2012 if (root[0] && root[1] != ':')
2014 WARN("invalid root %s\n", debugstr_w(root));
2015 return FALSE;
2017 drive = toupperW(root[0]) - 'A';
2019 if (!DRIVE_IsValid( drive )) return FALSE;
2020 if (label && label_len)
2022 strncpyW( label, DRIVE_GetLabel(drive), label_len );
2023 label[label_len - 1] = 0; /* ensure 0 termination */
2024 cp = label + strlenW(label);
2025 while (cp != label && *(cp-1) == ' ') cp--;
2026 *cp = '\0';
2028 if (serial) *serial = DRIVE_GetSerialNumber(drive);
2030 /* Set the filesystem information */
2031 /* Note: we only emulate a FAT fs at present */
2033 if (filename_len) {
2034 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
2035 *filename_len = 12;
2036 else
2037 *filename_len = 255;
2039 if (flags)
2041 *flags=0;
2042 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
2043 *flags|=FS_CASE_SENSITIVE;
2044 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
2045 *flags|=FS_CASE_IS_PRESERVED;
2047 if (fsname && fsname_len)
2049 /* Diablo checks that return code ... */
2050 if (DOSDrives[drive].type == DRIVE_CDROM)
2052 static const WCHAR cdfsW[] = {'C','D','F','S',0};
2053 strncpyW( fsname, cdfsW, fsname_len );
2055 else
2057 static const WCHAR fatW[] = {'F','A','T',0};
2058 strncpyW( fsname, fatW, fsname_len );
2060 fsname[fsname_len - 1] = 0; /* ensure 0 termination */
2062 return TRUE;
2066 /***********************************************************************
2067 * GetVolumeInformationA (KERNEL32.@)
2069 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
2070 DWORD label_len, DWORD *serial,
2071 DWORD *filename_len, DWORD *flags,
2072 LPSTR fsname, DWORD fsname_len )
2074 UNICODE_STRING rootW;
2075 LPWSTR labelW, fsnameW;
2076 BOOL ret;
2078 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
2079 else rootW.Buffer = NULL;
2080 labelW = label ? HeapAlloc(GetProcessHeap(), 0, label_len * sizeof(WCHAR)) : NULL;
2081 fsnameW = fsname ? HeapAlloc(GetProcessHeap(), 0, fsname_len * sizeof(WCHAR)) : NULL;
2083 if ((ret = GetVolumeInformationW(rootW.Buffer, labelW, label_len, serial,
2084 filename_len, flags, fsnameW, fsname_len)))
2086 if (label) WideCharToMultiByte(CP_ACP, 0, labelW, -1, label, label_len, NULL, NULL);
2087 if (fsname) WideCharToMultiByte(CP_ACP, 0, fsnameW, -1, fsname, fsname_len, NULL, NULL);
2090 RtlFreeUnicodeString(&rootW);
2091 if (labelW) HeapFree( GetProcessHeap(), 0, labelW );
2092 if (fsnameW) HeapFree( GetProcessHeap(), 0, fsnameW );
2093 return ret;
2096 /***********************************************************************
2097 * SetVolumeLabelW (KERNEL32.@)
2099 BOOL WINAPI SetVolumeLabelW( LPCWSTR root, LPCWSTR volname )
2101 int drive;
2103 /* FIXME, SetLastErrors missing */
2105 if (!root) drive = DRIVE_GetCurrentDrive();
2106 else
2108 if ((root[1]) && (root[1] != ':'))
2110 WARN("invalid root %s\n", debugstr_w(root));
2111 return FALSE;
2113 drive = toupperW(root[0]) - 'A';
2115 if (!DRIVE_IsValid( drive )) return FALSE;
2117 /* some copy protection stuff check this */
2118 if (DOSDrives[drive].type == DRIVE_CDROM) return FALSE;
2120 strncpyW(DOSDrives[drive].label_conf, volname, 12);
2121 DOSDrives[drive].label_conf[12 - 1] = 0; /* ensure 0 termination */
2122 return TRUE;
2125 /***********************************************************************
2126 * SetVolumeLabelA (KERNEL32.@)
2128 BOOL WINAPI SetVolumeLabelA(LPCSTR root, LPCSTR volname)
2130 UNICODE_STRING rootW, volnameW;
2131 BOOL ret;
2133 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
2134 else rootW.Buffer = NULL;
2135 if (volname) RtlCreateUnicodeStringFromAsciiz(&volnameW, volname);
2136 else volnameW.Buffer = NULL;
2138 ret = SetVolumeLabelW( rootW.Buffer, volnameW.Buffer );
2140 RtlFreeUnicodeString(&rootW);
2141 RtlFreeUnicodeString(&volnameW);
2142 return ret;
2145 /***********************************************************************
2146 * GetVolumeNameForVolumeMountPointW (KERNEL32.@)
2148 BOOL WINAPI GetVolumeNameForVolumeMountPointW(LPCWSTR str, LPWSTR dst, DWORD size)
2150 FIXME("(%s, %p, %lx): stub\n", debugstr_w(str), dst, size);
2151 return 0;