- Remove an erroneous check in NdrSendReceive.
[wine.git] / files / drive.c
blob8cf95ac053fb3fd82b5bd6a1877f7d1d619f81c9
1 /*
2 * DOS drives handling functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
7 * Label & serial number read support.
8 * (c) 1999 Petr Tomasek <tomasek@etf.cuni.cz>
9 * (c) 2000 Andreas Mohr (changes)
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "config.h"
27 #include "wine/port.h"
29 #include <assert.h>
30 #include <ctype.h>
31 #include <string.h>
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <errno.h>
39 #ifdef HAVE_UNISTD_H
40 # include <unistd.h>
41 #endif
43 #ifdef HAVE_SYS_PARAM_H
44 # include <sys/param.h>
45 #endif
46 #ifdef STATFS_DEFINED_BY_SYS_VFS
47 # include <sys/vfs.h>
48 #else
49 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
50 # include <sys/mount.h>
51 # else
52 # ifdef STATFS_DEFINED_BY_SYS_STATFS
53 # include <sys/statfs.h>
54 # endif
55 # endif
56 #endif
58 #define NONAMELESSUNION
59 #define NONAMELESSSTRUCT
60 #include "ntstatus.h"
61 #include "windef.h"
62 #include "winbase.h"
63 #include "winreg.h"
64 #include "winternl.h"
65 #include "wine/winbase16.h" /* for GetCurrentTask */
66 #include "winerror.h"
67 #include "winioctl.h"
68 #include "ntddstor.h"
69 #include "ntddcdrm.h"
70 #include "drive.h"
71 #include "file.h"
72 #include "msdos.h"
73 #include "task.h"
74 #include "wine/unicode.h"
75 #include "wine/library.h"
76 #include "wine/server.h"
77 #include "wine/debug.h"
79 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
80 WINE_DECLARE_DEBUG_CHANNEL(file);
82 typedef struct
84 char *root; /* root dir in Unix format without trailing / */
85 LPWSTR dos_cwd; /* cwd in DOS format without leading or trailing \ */
86 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
87 char *device; /* raw device path */
88 WCHAR label_conf[12]; /* drive label as cfg'd in wine config */
89 WCHAR label_read[12]; /* drive label as read from device */
90 DWORD serial_conf; /* drive serial number as cfg'd in wine config */
91 UINT type; /* drive type */
92 UINT flags; /* drive flags */
93 dev_t dev; /* unix device number */
94 ino_t ino; /* unix inode number */
95 } DOSDRIVE;
98 static const WCHAR DRIVE_Types[][8] =
100 { 0 }, /* DRIVE_UNKNOWN */
101 { 0 }, /* DRIVE_NO_ROOT_DIR */
102 {'f','l','o','p','p','y',0}, /* DRIVE_REMOVABLE */
103 {'h','d',0}, /* DRIVE_FIXED */
104 {'n','e','t','w','o','r','k',0}, /* DRIVE_REMOTE */
105 {'c','d','r','o','m',0}, /* DRIVE_CDROM */
106 {'r','a','m','d','i','s','k',0} /* DRIVE_RAMDISK */
110 /* Known filesystem types */
112 typedef struct
114 const WCHAR name[6];
115 UINT flags;
116 } FS_DESCR;
118 static const FS_DESCR DRIVE_Filesystems[] =
120 { {'u','n','i','x',0}, DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
121 { {'m','s','d','o','s',0}, DRIVE_SHORT_NAMES },
122 { {'d','o','s',0}, DRIVE_SHORT_NAMES },
123 { {'f','a','t',0}, DRIVE_SHORT_NAMES },
124 { {'v','f','a','t',0}, DRIVE_CASE_PRESERVING },
125 { {'w','i','n','9','5',0}, DRIVE_CASE_PRESERVING },
126 { { 0 }, 0 }
130 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
131 static int DRIVE_CurDrive = -1;
133 static HTASK16 DRIVE_LastTask = 0;
135 /* strdup on the process heap */
136 inline static char *heap_strdup( const char *str )
138 INT len = strlen(str) + 1;
139 LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
140 if (p) memcpy( p, str, len );
141 return p;
144 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
146 extern void CDROM_InitRegistry(int dev, int id, const char *device);
148 /***********************************************************************
149 * DRIVE_GetDriveType
151 static inline UINT DRIVE_GetDriveType( INT drive, LPCWSTR value )
153 int i;
155 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
157 if (!strcmpiW( value, DRIVE_Types[i] )) return i;
159 MESSAGE("Drive %c: unknown drive type %s, defaulting to 'hd'.\n",
160 'A' + drive, debugstr_w(value) );
161 return DRIVE_FIXED;
165 /***********************************************************************
166 * DRIVE_GetFSFlags
168 static UINT DRIVE_GetFSFlags( INT drive, LPCWSTR value )
170 const FS_DESCR *descr;
172 for (descr = DRIVE_Filesystems; *descr->name; descr++)
173 if (!strcmpiW( value, descr->name )) return descr->flags;
174 MESSAGE("Drive %c: unknown filesystem type %s, defaulting to 'win95'.\n",
175 'A' + drive, debugstr_w(value) );
176 return DRIVE_CASE_PRESERVING;
180 /***********************************************************************
181 * DRIVE_Init
183 int DRIVE_Init(void)
185 int i, len, count = 0;
186 WCHAR driveW[] = {'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
187 'W','i','n','e','\\','W','i','n','e','\\',
188 'C','o','n','f','i','g','\\','D','r','i','v','e',' ','A',0};
189 WCHAR drive_env[] = {'=','A',':',0};
190 WCHAR path[MAX_PATHNAME_LEN];
191 char tmp[MAX_PATHNAME_LEN*sizeof(WCHAR) + sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
192 struct stat drive_stat_buffer;
193 WCHAR *p;
194 DOSDRIVE *drive;
195 HKEY hkey;
196 DWORD dummy;
197 OBJECT_ATTRIBUTES attr;
198 UNICODE_STRING nameW;
200 static const WCHAR PathW[] = {'P','a','t','h',0};
201 static const WCHAR LabelW[] = {'L','a','b','e','l',0};
202 static const WCHAR SerialW[] = {'S','e','r','i','a','l',0};
203 static const WCHAR TypeW[] = {'T','y','p','e',0};
204 static const WCHAR FilesystemW[] = {'F','i','l','e','s','y','s','t','e','m',0};
205 static const WCHAR DeviceW[] = {'D','e','v','i','c','e',0};
206 static const WCHAR ReadVolInfoW[] = {'R','e','a','d','V','o','l','I','n','f','o',0};
207 static const WCHAR FailReadOnlyW[] = {'F','a','i','l','R','e','a','d','O','n','l','y',0};
208 static const WCHAR driveC_labelW[] = {'D','r','i','v','e',' ','C',' ',' ',' ',' ',0};
211 attr.Length = sizeof(attr);
212 attr.RootDirectory = 0;
213 attr.ObjectName = &nameW;
214 attr.Attributes = 0;
215 attr.SecurityDescriptor = NULL;
216 attr.SecurityQualityOfService = NULL;
218 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
220 RtlInitUnicodeString( &nameW, driveW );
221 nameW.Buffer[(nameW.Length / sizeof(WCHAR)) - 1] = 'A' + i;
222 if (NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ) != STATUS_SUCCESS) continue;
224 /* Get the root path */
225 RtlInitUnicodeString( &nameW, PathW );
226 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
228 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
229 ExpandEnvironmentStringsW( data, path, sizeof(path)/sizeof(WCHAR) );
231 p = path + strlenW(path) - 1;
232 while ((p > path) && (*p == '/')) *p-- = '\0';
234 if (path[0] == '/')
236 len = WideCharToMultiByte(CP_UNIXCP, 0, path, -1, NULL, 0, NULL, NULL);
237 drive->root = HeapAlloc(GetProcessHeap(), 0, len);
238 WideCharToMultiByte(CP_UNIXCP, 0, path, -1, drive->root, len, NULL, NULL);
240 else
242 /* relative paths are relative to config dir */
243 const char *config = wine_get_config_dir();
244 len = strlen(config);
245 len += WideCharToMultiByte(CP_UNIXCP, 0, path, -1, NULL, 0, NULL, NULL) + 2;
246 drive->root = HeapAlloc( GetProcessHeap(), 0, len );
247 len -= sprintf( drive->root, "%s/", config );
248 WideCharToMultiByte(CP_UNIXCP, 0, path, -1,
249 drive->root + strlen(drive->root), len, NULL, NULL);
252 if (stat( drive->root, &drive_stat_buffer ))
254 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
255 drive->root, strerror(errno), 'A' + i);
256 HeapFree( GetProcessHeap(), 0, drive->root );
257 drive->root = NULL;
258 goto next;
260 if (!S_ISDIR(drive_stat_buffer.st_mode))
262 MESSAGE("%s is not a directory, ignoring drive %c:\n",
263 drive->root, 'A' + i );
264 HeapFree( GetProcessHeap(), 0, drive->root );
265 drive->root = NULL;
266 goto next;
269 drive->dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(drive->dos_cwd[0]));
270 drive->unix_cwd = heap_strdup( "" );
271 drive->device = NULL;
272 drive->flags = 0;
273 drive->dev = drive_stat_buffer.st_dev;
274 drive->ino = drive_stat_buffer.st_ino;
276 /* Get the drive type */
277 RtlInitUnicodeString( &nameW, TypeW );
278 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
280 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
281 drive->type = DRIVE_GetDriveType( i, data );
283 else drive->type = DRIVE_FIXED;
285 /* Get the drive label */
286 RtlInitUnicodeString( &nameW, LabelW );
287 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
289 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
290 lstrcpynW( drive->label_conf, data, 12 );
292 if ((len = strlenW(drive->label_conf)) < 11)
294 /* Pad label with spaces */
295 while(len < 11) drive->label_conf[len++] = ' ';
296 drive->label_conf[11] = '\0';
299 /* Get the serial number */
300 RtlInitUnicodeString( &nameW, SerialW );
301 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
303 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
304 drive->serial_conf = strtoulW( data, NULL, 16 );
306 else drive->serial_conf = 12345678;
308 /* Get the filesystem type */
309 RtlInitUnicodeString( &nameW, FilesystemW );
310 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
312 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
313 drive->flags = DRIVE_GetFSFlags( i, data );
315 else drive->flags = DRIVE_CASE_PRESERVING;
317 /* Get the device */
318 RtlInitUnicodeString( &nameW, DeviceW );
319 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
321 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
322 len = WideCharToMultiByte(CP_UNIXCP, 0, data, -1, NULL, 0, NULL, NULL);
323 drive->device = HeapAlloc(GetProcessHeap(), 0, len);
324 WideCharToMultiByte(CP_UNIXCP, 0, data, -1, drive->device, len, NULL, NULL);
326 RtlInitUnicodeString( &nameW, ReadVolInfoW );
327 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
329 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
330 if (IS_OPTION_TRUE(data[0])) drive->flags |= DRIVE_READ_VOL_INFO;
332 else drive->flags |= DRIVE_READ_VOL_INFO;
334 if (drive->type == DRIVE_CDROM)
336 int cd_fd;
337 if ((cd_fd = open(drive->device, O_RDONLY|O_NONBLOCK)) != -1)
339 CDROM_InitRegistry(cd_fd, i, drive->device);
340 close(cd_fd);
345 /* Get the FailReadOnly flag */
346 RtlInitUnicodeString( &nameW, FailReadOnlyW );
347 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
349 WCHAR *data = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
350 if (IS_OPTION_TRUE(data[0])) drive->flags |= DRIVE_FAIL_READ_ONLY;
353 /* Make the first hard disk the current drive */
354 if ((DRIVE_CurDrive == -1) && (drive->type == DRIVE_FIXED))
355 DRIVE_CurDrive = i;
357 count++;
358 TRACE("Drive %c: path=%s type=%s label=%s serial=%08lx "
359 "flags=%08x dev=%x ino=%x\n",
360 'A' + i, drive->root, debugstr_w(DRIVE_Types[drive->type]),
361 debugstr_w(drive->label_conf), drive->serial_conf, drive->flags,
362 (int)drive->dev, (int)drive->ino );
365 next:
366 NtClose( hkey );
369 if (!count)
371 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
372 /* Create a C drive pointing to Unix root dir */
373 DOSDrives[2].root = heap_strdup( "/" );
374 DOSDrives[2].dos_cwd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DOSDrives[2].dos_cwd[0]));
375 DOSDrives[2].unix_cwd = heap_strdup( "" );
376 strcpyW( DOSDrives[2].label_conf, driveC_labelW );
377 DOSDrives[2].serial_conf = 12345678;
378 DOSDrives[2].type = DRIVE_FIXED;
379 DOSDrives[2].device = NULL;
380 DOSDrives[2].flags = 0;
381 DRIVE_CurDrive = 2;
384 /* Make sure the current drive is valid */
385 if (DRIVE_CurDrive == -1)
387 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
389 if (drive->root && !(drive->flags & DRIVE_DISABLED))
391 DRIVE_CurDrive = i;
392 break;
397 /* get current working directory info for all drives */
398 for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
400 if (!GetEnvironmentVariableW(drive_env, path, MAX_PATHNAME_LEN)) continue;
401 /* sanity check */
402 if (toupperW(path[0]) != drive_env[1] || path[1] != ':') continue;
403 DRIVE_Chdir( i, path + 2 );
405 return 1;
409 /***********************************************************************
410 * DRIVE_IsValid
412 int DRIVE_IsValid( int drive )
414 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
415 return (DOSDrives[drive].root &&
416 !(DOSDrives[drive].flags & DRIVE_DISABLED));
420 /***********************************************************************
421 * DRIVE_GetCurrentDrive
423 int DRIVE_GetCurrentDrive(void)
425 TDB *pTask = GlobalLock16(GetCurrentTask());
426 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
427 return DRIVE_CurDrive;
431 /***********************************************************************
432 * DRIVE_SetCurrentDrive
434 int DRIVE_SetCurrentDrive( int drive )
436 TDB *pTask = GlobalLock16(GetCurrentTask());
437 if (!DRIVE_IsValid( drive ))
439 SetLastError( ERROR_INVALID_DRIVE );
440 return 0;
442 TRACE("%c:\n", 'A' + drive );
443 DRIVE_CurDrive = drive;
444 if (pTask) pTask->curdrive = drive | 0x80;
445 return 1;
449 /***********************************************************************
450 * DRIVE_FindDriveRoot
452 * Find a drive for which the root matches the beginning of the given path.
453 * This can be used to translate a Unix path into a drive + DOS path.
454 * Return value is the drive, or -1 on error. On success, path is modified
455 * to point to the beginning of the DOS path.
457 * Note: path must be in the encoding of the underlying Unix file system.
459 int DRIVE_FindDriveRoot( const char **path )
461 /* Starting with the full path, check if the device and inode match any of
462 * the wine 'drives'. If not then remove the last path component and try
463 * again. If the last component was a '..' then skip a normal component
464 * since it's a directory that's ascended back out of.
466 int drive, level, len;
467 char buffer[MAX_PATHNAME_LEN];
468 char *p;
469 struct stat st;
471 strcpy( buffer, *path );
472 for (p = buffer; *p; p++) if (*p == '\\') *p = '/';
473 len = p - buffer;
475 /* strip off trailing slashes */
476 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
478 for (;;)
480 /* Find the drive */
481 if (stat( buffer, &st ) == 0 && S_ISDIR( st.st_mode ))
483 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
485 if (!DOSDrives[drive].root ||
486 (DOSDrives[drive].flags & DRIVE_DISABLED))
487 continue;
489 if ((DOSDrives[drive].dev == st.st_dev) &&
490 (DOSDrives[drive].ino == st.st_ino))
492 if (len == 1) len = 0; /* preserve root slash in returned path */
493 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
494 *path, 'A' + drive, buffer, *path + len);
495 *path += len;
496 if (!**path) *path = "\\";
497 return drive;
501 if (len <= 1) return -1; /* reached root */
503 level = 0;
504 while (level < 1)
506 /* find start of the last path component */
507 while (len > 1 && buffer[len - 1] != '/') len--;
508 if (!buffer[len]) break; /* empty component -> reached root */
509 /* does removing it take us up a level? */
510 if (strcmp( buffer + len, "." ) != 0)
511 level += strcmp( buffer + len, ".." ) ? 1 : -1;
512 buffer[len] = 0;
513 /* strip off trailing slashes */
514 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
520 /***********************************************************************
521 * DRIVE_FindDriveRootW
523 * Unicode version of DRIVE_FindDriveRoot.
525 int DRIVE_FindDriveRootW( LPCWSTR *path )
527 int drive, level, len;
528 WCHAR buffer[MAX_PATHNAME_LEN];
529 WCHAR *p;
530 struct stat st;
532 strcpyW( buffer, *path );
533 for (p = buffer; *p; p++) if (*p == '\\') *p = '/';
534 len = p - buffer;
536 /* strip off trailing slashes */
537 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
539 for (;;)
541 char buffA[MAX_PATHNAME_LEN];
543 WideCharToMultiByte( CP_UNIXCP, 0, buffer, -1, buffA, sizeof(buffA), NULL, NULL );
544 if (stat( buffA, &st ) == 0 && S_ISDIR( st.st_mode ))
546 /* Find the drive */
547 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
549 if (!DOSDrives[drive].root ||
550 (DOSDrives[drive].flags & DRIVE_DISABLED))
551 continue;
553 if ((DOSDrives[drive].dev == st.st_dev) &&
554 (DOSDrives[drive].ino == st.st_ino))
556 static const WCHAR rootW[] = {'\\',0};
558 if (len == 1) len = 0; /* preserve root slash in returned path */
559 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
560 debugstr_w(*path), 'A' + drive, debugstr_w(buffer), debugstr_w(*path + len));
561 *path += len;
562 if (!**path) *path = rootW;
563 return drive;
567 if (len <= 1) return -1; /* reached root */
569 level = 0;
570 while (level < 1)
572 static const WCHAR dotW[] = {'.',0};
573 static const WCHAR dotdotW[] = {'.','.',0};
575 /* find start of the last path component */
576 while (len > 1 && buffer[len - 1] != '/') len--;
577 if (!buffer[len]) break; /* empty component -> reached root */
578 /* does removing it take us up a level? */
579 if (strcmpW( buffer + len, dotW ) != 0)
580 level += strcmpW( buffer + len, dotdotW ) ? 1 : -1;
581 buffer[len] = 0;
582 /* strip off trailing slashes */
583 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
589 /***********************************************************************
590 * DRIVE_GetRoot
592 const char * DRIVE_GetRoot( int drive )
594 if (!DRIVE_IsValid( drive )) return NULL;
595 return DOSDrives[drive].root;
599 /***********************************************************************
600 * DRIVE_GetDosCwd
602 LPCWSTR DRIVE_GetDosCwd( int drive )
604 TDB *pTask = GlobalLock16(GetCurrentTask());
605 if (!DRIVE_IsValid( drive )) return NULL;
607 /* Check if we need to change the directory to the new task. */
608 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
609 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
610 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
612 static const WCHAR rootW[] = {'\\',0};
613 WCHAR curdirW[MAX_PATH];
614 MultiByteToWideChar(CP_ACP, 0, pTask->curdir, -1, curdirW, MAX_PATH);
615 /* Perform the task-switch */
616 if (!DRIVE_Chdir( drive, curdirW )) DRIVE_Chdir( drive, rootW );
617 DRIVE_LastTask = GetCurrentTask();
619 return DOSDrives[drive].dos_cwd;
623 /***********************************************************************
624 * DRIVE_GetUnixCwd
626 const char * DRIVE_GetUnixCwd( int drive )
628 TDB *pTask = GlobalLock16(GetCurrentTask());
629 if (!DRIVE_IsValid( drive )) return NULL;
631 /* Check if we need to change the directory to the new task. */
632 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
633 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
634 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
636 static const WCHAR rootW[] = {'\\',0};
637 WCHAR curdirW[MAX_PATH];
638 MultiByteToWideChar(CP_ACP, 0, pTask->curdir, -1, curdirW, MAX_PATH);
639 /* Perform the task-switch */
640 if (!DRIVE_Chdir( drive, curdirW )) DRIVE_Chdir( drive, rootW );
641 DRIVE_LastTask = GetCurrentTask();
643 return DOSDrives[drive].unix_cwd;
647 /***********************************************************************
648 * DRIVE_GetDevice
650 const char * DRIVE_GetDevice( int drive )
652 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
655 /******************************************************************
656 * static WORD CDROM_Data_FindBestVoldesc
660 static WORD CDROM_Data_FindBestVoldesc(int fd)
662 BYTE cur_vd_type, max_vd_type = 0;
663 unsigned int offs, best_offs = 0, extra_offs = 0;
664 char sig[3];
666 for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
668 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
669 * the volume label is displaced forward by 8
671 lseek(fd, offs + 11, SEEK_SET); /* check for non-ISO9660 signature */
672 read(fd, &sig, 3);
673 if ((sig[0] == 'R') && (sig[1] == 'O') && (sig[2]=='M'))
675 extra_offs = 8;
677 lseek(fd, offs + extra_offs, SEEK_SET);
678 read(fd, &cur_vd_type, 1);
679 if (cur_vd_type == 0xff) /* voldesc set terminator */
680 break;
681 if (cur_vd_type > max_vd_type)
683 max_vd_type = cur_vd_type;
684 best_offs = offs + extra_offs;
687 return best_offs;
690 /***********************************************************************
691 * DRIVE_ReadSuperblock
693 * NOTE
694 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
695 * to check, that they are writing on a FAT filesystem !
697 int DRIVE_ReadSuperblock (int drive, char * buff)
699 #define DRIVE_SUPER 96
700 int fd;
701 off_t offs;
702 int ret = 0;
703 struct stat stat_buf;
705 memset(buff, 0, DRIVE_SUPER);
706 /* O_NONBLOCK in case we're opening FIFO; will be reset later */
707 if ((fd = open(DOSDrives[drive].device, O_RDONLY|O_NOCTTY|O_NONBLOCK)) != -1) {
708 if (fstat(fd, &stat_buf) < 0) { /* shouldn't happen since we just opened that file */
709 ERR("fstat() failed for opened device '%s' (drive %c:) ! IT SHOULDN'T HAPPEN !!!\n",
710 DOSDrives[drive].device, 'A'+drive);
711 ret = -1;
712 } else if (!S_ISBLK(stat_buf.st_mode)) {
713 ERR("Device '%s' (drive %c:) is not a block device - check your config\n",
714 DOSDrives[drive].device, 'A'+drive);
715 ret = -1;
716 /* reset O_NONBLOCK */
717 } else if (fcntl(fd, F_SETFL, 0) < 0 || fcntl(fd, F_GETFL) & O_NONBLOCK) {
718 ERR("fcntl() failed to reset O_NONBLOCK for device '%s' (drive %c:)\n",
719 DOSDrives[drive].device, 'A'+drive);
720 ret = -1;
722 if (ret) {
723 close(fd);
724 fd = -1;
726 } else {
727 if (!DOSDrives[drive].device)
728 ERR("No device configured for drive %c: !\n", 'A'+drive);
729 else
730 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
731 (stat(DOSDrives[drive].device, &stat_buf)) ?
732 "not available or symlink not valid ?" : "no permission");
734 if (fd == -1) {
735 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
736 return -1;
739 switch(DOSDrives[drive].type)
741 case DRIVE_REMOVABLE:
742 case DRIVE_FIXED:
743 offs = 0;
744 break;
745 case DRIVE_CDROM:
746 offs = CDROM_Data_FindBestVoldesc(fd);
747 break;
748 default:
749 offs = 0;
750 break;
753 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs))
755 ret = -4;
756 goto the_end;
758 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER)
760 ret = -2;
761 goto the_end;
764 switch(DOSDrives[drive].type)
766 case DRIVE_REMOVABLE:
767 case DRIVE_FIXED:
768 if ((buff[0x26]!=0x29) || /* Check for FAT present */
769 /* FIXME: do really all FAT have their name beginning with
770 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
771 memcmp( buff+0x36,"FAT",3))
773 ERR("The filesystem is not FAT !! (device=%s)\n",
774 DOSDrives[drive].device);
775 ret = -3;
776 goto the_end;
778 break;
779 case DRIVE_CDROM:
780 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
782 ret = -3;
783 goto the_end;
785 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
786 break;
787 default:
788 ret = -3;
789 goto the_end;
792 return close(fd);
793 the_end:
794 close(fd);
795 return ret;
799 /***********************************************************************
800 * DRIVE_WriteSuperblockEntry
802 * NOTE
803 * We are writing as little as possible (ie. not the whole SuperBlock)
804 * not to interfere with kernel. The drive can be mounted !
806 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
808 int fd;
810 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
812 ERR("Cannot open the device %s (for writing)\n",
813 DOSDrives[drive].device);
814 return -1;
816 if (lseek(fd,ofs,SEEK_SET)!=ofs)
818 ERR("lseek failed on device %s !\n",
819 DOSDrives[drive].device);
820 close(fd);
821 return -2;
823 if (write(fd,buff,len)!=len)
825 close(fd);
826 ERR("Cannot write on %s !\n",
827 DOSDrives[drive].device);
828 return -3;
830 return close (fd);
833 /******************************************************************
834 * static HANDLE CDROM_Open
838 static HANDLE CDROM_Open(int drive)
840 WCHAR root[] = {'\\','\\','.','\\','A',':',0};
841 root[4] += drive;
842 return CreateFileW(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
845 /**************************************************************************
846 * CDROM_Data_GetLabel [internal]
848 DWORD CDROM_Data_GetLabel(int drive, WCHAR *label)
850 #define LABEL_LEN 32+1
851 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
852 WORD offs = CDROM_Data_FindBestVoldesc(dev);
853 WCHAR label_read[LABEL_LEN]; /* Unicode possible, too */
854 DWORD unicode_id = 0;
856 if (offs)
858 if ((lseek(dev, offs+0x58, SEEK_SET) == offs+0x58)
859 && (read(dev, &unicode_id, 3) == 3))
861 int ver = (unicode_id & 0xff0000) >> 16;
863 if ((lseek(dev, offs+0x28, SEEK_SET) != offs+0x28)
864 || (read(dev, &label_read, LABEL_LEN) != LABEL_LEN))
865 goto failure;
867 close(dev);
868 if ((LOWORD(unicode_id) == 0x2f25) /* Unicode ID */
869 && ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
870 { /* yippee, unicode */
871 int i;
872 WORD ch;
873 for (i=0; i<LABEL_LEN;i++)
874 { /* Motorola -> Intel Unicode conversion :-\ */
875 ch = label_read[i];
876 label_read[i] = (ch << 8) | (ch >> 8);
878 strncpyW(label, label_read, 11);
879 label[11] = 0;
881 else
883 MultiByteToWideChar(CP_UNIXCP, 0, (LPSTR)label_read, -1, label, 11);
884 label[11] = '\0';
886 return 1;
889 failure:
890 close(dev);
891 ERR("error reading label !\n");
892 return 0;
895 /**************************************************************************
896 * CDROM_GetLabel [internal]
898 static DWORD CDROM_GetLabel(int drive, WCHAR *label)
900 HANDLE h;
901 CDROM_DISK_DATA cdd;
902 DWORD br, ret = 1;
903 BOOL r;
905 h = CDROM_Open(drive);
906 if( !h )
907 return 0;
908 r = DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL,
909 0, &cdd, sizeof(cdd), &br, 0);
910 CloseHandle( h );
911 if( !r )
912 return 0;
914 switch (cdd.DiskData & 0x03)
916 case CDROM_DISK_DATA_TRACK:
917 if (!CDROM_Data_GetLabel(drive, label))
918 ret = 0;
919 break;
920 case CDROM_DISK_AUDIO_TRACK:
922 static const WCHAR audioCD[] = {'A','u','d','i','o',' ','C','D',' ',' ',' ',0};
923 strcpyW(label, audioCD);
924 break;
926 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
927 FIXME("Need to get the label of a mixed mode CD!\n");
928 /* This assumes that the first track is a data track! */
929 /* I guess the correct way would be to enumerate all data tracks
930 and check each for the title */
931 if (!CDROM_Data_GetLabel(drive, label))
932 ret = 0;
933 break;
934 case 0:
935 ret = 0;
936 break;
938 TRACE("CD: label is %s\n", debugstr_w(label));
940 return ret;
942 /***********************************************************************
943 * DRIVE_GetLabel
945 LPCWSTR DRIVE_GetLabel( int drive )
947 int read = 0;
948 char buff[DRIVE_SUPER];
949 int offs = -1;
951 if (!DRIVE_IsValid( drive )) return NULL;
952 if (DOSDrives[drive].type == DRIVE_CDROM)
954 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
956 else
957 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
959 if (DRIVE_ReadSuperblock(drive,(char *) buff))
960 ERR("Invalid or unreadable superblock on %s (%c:).\n",
961 DOSDrives[drive].device, (char)(drive+'A'));
962 else {
963 if (DOSDrives[drive].type == DRIVE_REMOVABLE ||
964 DOSDrives[drive].type == DRIVE_FIXED)
965 offs = 0x2b;
967 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
968 if (offs != -1)
969 MultiByteToWideChar(CP_UNIXCP, 0, buff+offs, 11,
970 DOSDrives[drive].label_read, 11);
971 DOSDrives[drive].label_read[11]='\0';
972 read = 1;
976 return (read) ?
977 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
980 #define CDFRAMES_PERSEC 75
981 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
982 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
983 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
985 /**************************************************************************
986 * CDROM_Audio_GetSerial [internal]
988 static DWORD CDROM_Audio_GetSerial(HANDLE h)
990 unsigned long serial = 0;
991 int i;
992 WORD wMagic;
993 DWORD dwStart, dwEnd, br;
994 CDROM_TOC toc;
996 if (!DeviceIoControl(h, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0))
997 return 0;
1000 * wMagic collects the wFrames from track 1
1001 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
1002 * frames.
1003 * There it is collected for correcting the serial when there are less than
1004 * 3 tracks.
1006 wMagic = toc.TrackData[0].Address[2];
1007 dwStart = FRAME_OF_TOC(toc, toc.FirstTrack);
1009 for (i = 0; i <= toc.LastTrack - toc.FirstTrack; i++) {
1010 serial += (toc.TrackData[i].Address[0] << 16) |
1011 (toc.TrackData[i].Address[1] << 8) | toc.TrackData[i].Address[2];
1013 dwEnd = FRAME_OF_TOC(toc, toc.LastTrack + 1);
1015 if (toc.LastTrack - toc.FirstTrack + 1 < 3)
1016 serial += wMagic + (dwEnd - dwStart);
1018 return serial;
1021 /**************************************************************************
1022 * CDROM_Data_GetSerial [internal]
1024 static DWORD CDROM_Data_GetSerial(int drive)
1026 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
1027 WORD offs;
1028 union {
1029 unsigned long val;
1030 unsigned char p[4];
1031 } serial;
1032 BYTE b0 = 0, b1 = 1, b2 = 2, b3 = 3;
1035 if (dev == -1) return 0;
1036 offs = CDROM_Data_FindBestVoldesc(dev);
1038 serial.val = 0;
1039 if (offs)
1041 BYTE buf[2048];
1042 RTL_OSVERSIONINFOEXW ovi;
1043 int i;
1045 lseek(dev, offs, SEEK_SET);
1046 read(dev, buf, 2048);
1048 * OK, another braindead one... argh. Just believe it.
1049 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
1050 * It's true and nobody will ever be able to change it.
1052 ovi.dwOSVersionInfoSize = sizeof(ovi);
1053 RtlGetVersion(&ovi);
1054 if ((ovi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (ovi.dwMajorVersion >= 4))
1056 b0 = 3; b1 = 2; b2 = 1; b3 = 0;
1058 for (i = 0; i < 2048; i += 4)
1060 /* DON'T optimize this into DWORD !! (breaks overflow) */
1061 serial.p[b0] += buf[i+b0];
1062 serial.p[b1] += buf[i+b1];
1063 serial.p[b2] += buf[i+b2];
1064 serial.p[b3] += buf[i+b3];
1067 close(dev);
1068 return serial.val;
1071 /**************************************************************************
1072 * CDROM_GetSerial [internal]
1074 static DWORD CDROM_GetSerial(int drive)
1076 DWORD serial = 0;
1077 HANDLE h;
1078 CDROM_DISK_DATA cdd;
1079 DWORD br;
1080 BOOL r;
1082 TRACE("%d\n", drive);
1084 h = CDROM_Open(drive);
1085 if( !h )
1086 return 0;
1087 r = DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL,
1088 0, &cdd, sizeof(cdd), &br, 0);
1089 if (!r)
1091 CloseHandle(h);
1092 return 0;
1095 switch (cdd.DiskData & 0x03)
1097 case CDROM_DISK_DATA_TRACK:
1098 /* hopefully a data CD */
1099 serial = CDROM_Data_GetSerial(drive);
1100 break;
1101 case CDROM_DISK_AUDIO_TRACK:
1102 /* fall thru */
1103 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
1104 serial = CDROM_Audio_GetSerial(h);
1105 break;
1106 case 0:
1107 break;
1110 if (serial)
1111 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial), LOWORD(serial));
1113 CloseHandle(h);
1115 return serial;
1118 /***********************************************************************
1119 * DRIVE_GetSerialNumber
1121 DWORD DRIVE_GetSerialNumber( int drive )
1123 DWORD serial = 0;
1124 char buff[DRIVE_SUPER];
1126 TRACE("drive %d, type = %d\n", drive, DOSDrives[drive].type);
1128 if (!DRIVE_IsValid( drive )) return 0;
1130 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
1132 switch(DOSDrives[drive].type)
1134 case DRIVE_REMOVABLE:
1135 case DRIVE_FIXED:
1136 if (DRIVE_ReadSuperblock(drive,(char *) buff))
1137 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
1138 " Maybe not FAT?\n" ,
1139 DOSDrives[drive].device, 'A'+drive);
1140 else
1141 serial = *((DWORD*)(buff+0x27));
1142 break;
1143 case DRIVE_CDROM:
1144 serial = CDROM_GetSerial(drive);
1145 break;
1146 default:
1147 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
1151 return (serial) ? serial : DOSDrives[drive].serial_conf;
1155 /***********************************************************************
1156 * DRIVE_SetSerialNumber
1158 int DRIVE_SetSerialNumber( int drive, DWORD serial )
1160 char buff[DRIVE_SUPER];
1162 if (!DRIVE_IsValid( drive )) return 0;
1164 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
1166 if ((DOSDrives[drive].type != DRIVE_REMOVABLE) &&
1167 (DOSDrives[drive].type != DRIVE_FIXED)) return 0;
1168 /* check, if the drive has a FAT filesystem */
1169 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
1170 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
1171 return 1;
1174 if (DOSDrives[drive].type == DRIVE_CDROM) return 0;
1175 DOSDrives[drive].serial_conf = serial;
1176 return 1;
1180 /***********************************************************************
1181 * DRIVE_GetType
1183 static UINT DRIVE_GetType( int drive )
1185 if (!DRIVE_IsValid( drive )) return DRIVE_NO_ROOT_DIR;
1186 return DOSDrives[drive].type;
1190 /***********************************************************************
1191 * DRIVE_GetFlags
1193 UINT DRIVE_GetFlags( int drive )
1195 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
1196 return DOSDrives[drive].flags;
1199 /***********************************************************************
1200 * DRIVE_Chdir
1202 int DRIVE_Chdir( int drive, LPCWSTR path )
1204 DOS_FULL_NAME full_name;
1205 WCHAR buffer[MAX_PATHNAME_LEN];
1206 LPSTR unix_cwd;
1207 BY_HANDLE_FILE_INFORMATION info;
1208 TDB *pTask = GlobalLock16(GetCurrentTask());
1210 buffer[0] = 'A' + drive;
1211 buffer[1] = ':';
1212 buffer[2] = 0;
1213 TRACE("(%s,%s)\n", debugstr_w(buffer), debugstr_w(path) );
1214 strncpyW( buffer + 2, path, MAX_PATHNAME_LEN - 2 );
1215 buffer[MAX_PATHNAME_LEN - 1] = 0; /* ensure 0 termination */
1217 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
1218 if (!FILE_Stat( full_name.long_name, &info, NULL )) return 0;
1219 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1221 SetLastError( ERROR_FILE_NOT_FOUND );
1222 return 0;
1224 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
1225 while (*unix_cwd == '/') unix_cwd++;
1227 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
1228 'A' + drive, unix_cwd, debugstr_w(full_name.short_name + 3) );
1230 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
1231 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
1232 DOSDrives[drive].dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name.short_name) - 2) * sizeof(WCHAR));
1233 strcpyW(DOSDrives[drive].dos_cwd, full_name.short_name + 3);
1234 DOSDrives[drive].unix_cwd = heap_strdup( unix_cwd );
1236 if (drive == DRIVE_CurDrive)
1238 UNICODE_STRING dirW;
1240 RtlInitUnicodeString( &dirW, full_name.short_name );
1241 RtlSetCurrentDirectory_U( &dirW );
1244 if (pTask && (pTask->curdrive & 0x80) &&
1245 ((pTask->curdrive & ~0x80) == drive))
1247 WideCharToMultiByte(CP_ACP, 0, full_name.short_name + 2, -1,
1248 pTask->curdir, sizeof(pTask->curdir), NULL, NULL);
1249 DRIVE_LastTask = GetCurrentTask();
1251 return 1;
1255 /***********************************************************************
1256 * DRIVE_Disable
1258 int DRIVE_Disable( int drive )
1260 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1262 SetLastError( ERROR_INVALID_DRIVE );
1263 return 0;
1265 DOSDrives[drive].flags |= DRIVE_DISABLED;
1266 return 1;
1270 /***********************************************************************
1271 * DRIVE_Enable
1273 int DRIVE_Enable( int drive )
1275 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1277 SetLastError( ERROR_INVALID_DRIVE );
1278 return 0;
1280 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
1281 return 1;
1285 /***********************************************************************
1286 * DRIVE_SetLogicalMapping
1288 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
1290 /* If new_drive is already valid, do nothing and return 0
1291 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1293 DOSDRIVE *old, *new;
1295 old = DOSDrives + existing_drive;
1296 new = DOSDrives + new_drive;
1298 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
1299 !old->root ||
1300 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
1302 SetLastError( ERROR_INVALID_DRIVE );
1303 return 0;
1306 if ( new->root )
1308 TRACE("Can't map drive %c: to already existing drive %c:\n",
1309 'A' + existing_drive, 'A' + new_drive );
1310 /* it is already mapped there, so return success */
1311 if (!strcmp(old->root,new->root))
1312 return 1;
1313 return 0;
1316 new->root = heap_strdup( old->root );
1317 new->dos_cwd = HeapAlloc(GetProcessHeap(), 0, (strlenW(old->dos_cwd) + 1) * sizeof(WCHAR));
1318 strcpyW(new->dos_cwd, old->dos_cwd);
1319 new->unix_cwd = heap_strdup( old->unix_cwd );
1320 new->device = heap_strdup( old->device );
1321 memcpy ( new->label_conf, old->label_conf, 12 );
1322 memcpy ( new->label_read, old->label_read, 12 );
1323 new->serial_conf = old->serial_conf;
1324 new->type = old->type;
1325 new->flags = old->flags;
1326 new->dev = old->dev;
1327 new->ino = old->ino;
1329 TRACE("Drive %c: is now equal to drive %c:\n",
1330 'A' + new_drive, 'A' + existing_drive );
1332 return 1;
1336 /***********************************************************************
1337 * DRIVE_OpenDevice
1339 * Open the drive raw device and return a Unix fd (or -1 on error).
1341 int DRIVE_OpenDevice( int drive, int flags )
1343 if (!DRIVE_IsValid( drive )) return -1;
1344 return open( DOSDrives[drive].device, flags );
1348 /***********************************************************************
1349 * DRIVE_GetFreeSpace
1351 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
1352 PULARGE_INTEGER available )
1354 struct statfs info;
1356 if (!DRIVE_IsValid(drive))
1358 SetLastError( ERROR_PATH_NOT_FOUND );
1359 return 0;
1362 /* FIXME: add autoconf check for this */
1363 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1364 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
1365 #else
1366 if (statfs( DOSDrives[drive].root, &info) < 0)
1367 #endif
1369 FILE_SetDosError();
1370 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
1371 return 0;
1374 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bsize, info.f_blocks );
1375 #ifdef HAVE_STRUCT_STATFS_F_BAVAIL
1376 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bavail, info.f_bsize );
1377 #else
1378 # ifdef HAVE_STRUCT_STATFS_F_BFREE
1379 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bfree, info.f_bsize );
1380 # else
1381 # error "statfs has no bfree/bavail member!"
1382 # endif
1383 #endif
1384 if (DOSDrives[drive].type == DRIVE_CDROM)
1385 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1386 available->QuadPart = 0;
1388 return 1;
1391 /***********************************************************************
1392 * DRIVE_GetCurrentDirectory
1393 * Returns "X:\\path\\etc\\".
1395 * Despite the API description, return required length including the
1396 * terminating null when buffer too small. This is the real behaviour.
1398 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPWSTR buf )
1400 UINT ret;
1401 LPCWSTR dos_cwd = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1402 static const WCHAR driveA_rootW[] = {'A',':','\\',0};
1404 ret = strlenW(dos_cwd) + 3; /* length of WHOLE current directory */
1405 if (ret >= buflen) return ret + 1;
1407 strcpyW( buf, driveA_rootW );
1408 buf[0] += DRIVE_GetCurrentDrive();
1409 strcatW( buf, dos_cwd );
1410 return ret;
1414 /***********************************************************************
1415 * DRIVE_BuildEnv
1417 * Build the environment array containing the drives' current directories.
1418 * Resulting pointer must be freed with HeapFree.
1420 WCHAR *DRIVE_BuildEnv(void)
1422 int i, length = 0;
1423 LPCWSTR cwd[MAX_DOS_DRIVES];
1424 WCHAR *env, *p;
1426 for (i = 0; i < MAX_DOS_DRIVES; i++)
1428 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0])
1429 length += strlenW(cwd[i]) + 8;
1431 if (!(env = HeapAlloc( GetProcessHeap(), 0, (length+1) * sizeof(WCHAR) ))) return NULL;
1432 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
1434 if (cwd[i] && cwd[i][0])
1436 *p++ = '='; *p++ = 'A' + i; *p++ = ':';
1437 *p++ = '='; *p++ = 'A' + i; *p++ = ':'; *p++ = '\\';
1438 strcpyW( p, cwd[i] );
1439 p += strlenW(p) + 1;
1442 *p = 0;
1443 return env;
1447 /***********************************************************************
1448 * GetDiskFreeSpace (KERNEL.422)
1450 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
1451 LPDWORD sector_bytes, LPDWORD free_clusters,
1452 LPDWORD total_clusters )
1454 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
1455 free_clusters, total_clusters );
1459 /***********************************************************************
1460 * GetDiskFreeSpaceW (KERNEL32.@)
1462 * Fails if expression resulting from current drive's dir and "root"
1463 * is not a root dir of the target drive.
1465 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1466 * if the corresponding info is unneeded.
1468 * FIXME: needs to support UNC names from Win95 OSR2 on.
1470 * Behaviour under Win95a:
1471 * CurrDir root result
1472 * "E:\\TEST" "E:" FALSE
1473 * "E:\\" "E:" TRUE
1474 * "E:\\" "E" FALSE
1475 * "E:\\" "\\" TRUE
1476 * "E:\\TEST" "\\" TRUE
1477 * "E:\\TEST" ":\\" FALSE
1478 * "E:\\TEST" "E:\\" TRUE
1479 * "E:\\TEST" "" FALSE
1480 * "E:\\" "" FALSE (!)
1481 * "E:\\" 0x0 TRUE
1482 * "E:\\TEST" 0x0 TRUE (!)
1483 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1484 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1486 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1487 LPDWORD sector_bytes, LPDWORD free_clusters,
1488 LPDWORD total_clusters )
1490 int drive, sec_size;
1491 ULARGE_INTEGER size,available;
1492 LPCWSTR path;
1493 DWORD cluster_sec;
1495 TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root), cluster_sectors, sector_bytes,
1496 free_clusters, total_clusters);
1498 if (!root || root[0] == '\\' || root[0] == '/')
1499 drive = DRIVE_GetCurrentDrive();
1500 else
1501 if (root[0] && root[1] == ':') /* root contains drive tag */
1503 drive = toupperW(root[0]) - 'A';
1504 path = &root[2];
1505 if (path[0] == '\0')
1507 path = DRIVE_GetDosCwd(drive);
1508 if (!path)
1510 SetLastError(ERROR_PATH_NOT_FOUND);
1511 return FALSE;
1514 else
1515 if (path[0] == '\\')
1516 path++;
1518 if (path[0]) /* oops, we are in a subdir */
1520 SetLastError(ERROR_INVALID_NAME);
1521 return FALSE;
1524 else
1526 if (!root[0])
1527 SetLastError(ERROR_PATH_NOT_FOUND);
1528 else
1529 SetLastError(ERROR_INVALID_NAME);
1530 return FALSE;
1533 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1535 /* Cap the size and available at 2GB as per specs. */
1536 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1538 size.s.HighPart = 0;
1539 size.s.LowPart = 0x7fffffff;
1541 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1543 available.s.HighPart =0;
1544 available.s.LowPart = 0x7fffffff;
1546 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
1547 size.s.LowPart /= sec_size;
1548 available.s.LowPart /= sec_size;
1549 /* FIXME: probably have to adjust those variables too for CDFS */
1550 cluster_sec = 1;
1551 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1553 if (cluster_sectors)
1554 *cluster_sectors = cluster_sec;
1555 if (sector_bytes)
1556 *sector_bytes = sec_size;
1557 if (free_clusters)
1558 *free_clusters = available.s.LowPart / cluster_sec;
1559 if (total_clusters)
1560 *total_clusters = size.s.LowPart / cluster_sec;
1561 return TRUE;
1565 /***********************************************************************
1566 * GetDiskFreeSpaceA (KERNEL32.@)
1568 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1569 LPDWORD sector_bytes, LPDWORD free_clusters,
1570 LPDWORD total_clusters )
1572 UNICODE_STRING rootW;
1573 BOOL ret = FALSE;
1575 if (root)
1577 if(!RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1579 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1580 return FALSE;
1583 else
1584 rootW.Buffer = NULL;
1586 ret = GetDiskFreeSpaceW(rootW.Buffer, cluster_sectors, sector_bytes,
1587 free_clusters, total_clusters );
1588 RtlFreeUnicodeString(&rootW);
1590 return ret;
1594 /***********************************************************************
1595 * GetDiskFreeSpaceExW (KERNEL32.@)
1597 * This function is used to acquire the size of the available and
1598 * total space on a logical volume.
1600 * RETURNS
1602 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1603 * detailed error information.
1606 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root,
1607 PULARGE_INTEGER avail,
1608 PULARGE_INTEGER total,
1609 PULARGE_INTEGER totalfree)
1611 int drive;
1612 ULARGE_INTEGER size,available;
1614 if (!root) drive = DRIVE_GetCurrentDrive();
1615 else
1616 { /* C: always works for GetDiskFreeSpaceEx */
1617 if ((root[1]) && ((root[1] != ':') || (root[2] && root[2] != '\\')))
1619 FIXME("there are valid root names which are not supported yet\n");
1620 /* ..like UNC names, for instance. */
1622 WARN("invalid root '%s'\n", debugstr_w(root));
1623 return FALSE;
1625 drive = toupperW(root[0]) - 'A';
1628 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1630 if (total)
1632 total->s.HighPart = size.s.HighPart;
1633 total->s.LowPart = size.s.LowPart;
1636 if (totalfree)
1638 totalfree->s.HighPart = available.s.HighPart;
1639 totalfree->s.LowPart = available.s.LowPart;
1642 if (avail)
1644 if (FIXME_ON(dosfs))
1646 /* On Windows2000, we need to check the disk quota
1647 allocated for the user owning the calling process. We
1648 don't want to be more obtrusive than necessary with the
1649 FIXME messages, so don't print the FIXME unless Wine is
1650 actually masquerading as Windows2000. */
1652 RTL_OSVERSIONINFOEXW ovi;
1653 ovi.dwOSVersionInfoSize = sizeof(ovi);
1654 if (RtlGetVersion(&ovi))
1656 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1657 FIXME("no per-user quota support yet\n");
1661 /* Quick hack, should eventually be fixed to work 100% with
1662 Windows2000 (see comment above). */
1663 avail->s.HighPart = available.s.HighPart;
1664 avail->s.LowPart = available.s.LowPart;
1667 return TRUE;
1670 /***********************************************************************
1671 * GetDiskFreeSpaceExA (KERNEL32.@)
1673 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root, PULARGE_INTEGER avail,
1674 PULARGE_INTEGER total,
1675 PULARGE_INTEGER totalfree)
1677 UNICODE_STRING rootW;
1678 BOOL ret;
1680 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
1681 else rootW.Buffer = NULL;
1683 ret = GetDiskFreeSpaceExW( rootW.Buffer, avail, total, totalfree);
1685 RtlFreeUnicodeString(&rootW);
1686 return ret;
1689 /***********************************************************************
1690 * GetDriveType (KERNEL.136)
1691 * This function returns the type of a drive in Win16.
1692 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1693 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1694 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1695 * do any pseudo-clever changes.
1697 * RETURNS
1698 * drivetype DRIVE_xxx
1700 UINT16 WINAPI GetDriveType16( UINT16 drive ) /* [in] number (NOT letter) of drive */
1702 UINT type = DRIVE_GetType(drive);
1703 TRACE("(%c:)\n", 'A' + drive );
1704 if (type == DRIVE_CDROM) type = DRIVE_REMOTE;
1705 return type;
1709 /***********************************************************************
1710 * GetDriveTypeW (KERNEL32.@)
1712 * Returns the type of the disk drive specified. If root is NULL the
1713 * root of the current directory is used.
1715 * RETURNS
1717 * Type of drive (from Win32 SDK):
1719 * DRIVE_UNKNOWN unable to find out anything about the drive
1720 * DRIVE_NO_ROOT_DIR nonexistent root dir
1721 * DRIVE_REMOVABLE the disk can be removed from the machine
1722 * DRIVE_FIXED the disk can not be removed from the machine
1723 * DRIVE_REMOTE network disk
1724 * DRIVE_CDROM CDROM drive
1725 * DRIVE_RAMDISK virtual disk in RAM
1727 UINT WINAPI GetDriveTypeW(LPCWSTR root) /* [in] String describing drive */
1729 int drive;
1730 TRACE("(%s)\n", debugstr_w(root));
1732 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1733 else
1735 if ((root[1]) && (root[1] != ':'))
1737 WARN("invalid root %s\n", debugstr_w(root));
1738 return DRIVE_NO_ROOT_DIR;
1740 drive = toupperW(root[0]) - 'A';
1742 return DRIVE_GetType(drive);
1746 /***********************************************************************
1747 * GetDriveTypeA (KERNEL32.@)
1749 UINT WINAPI GetDriveTypeA( LPCSTR root )
1751 UNICODE_STRING rootW;
1752 UINT ret = 0;
1754 if (root)
1756 if( !RtlCreateUnicodeStringFromAsciiz(&rootW, root))
1758 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1759 return 0;
1762 else
1763 rootW.Buffer = NULL;
1765 ret = GetDriveTypeW(rootW.Buffer);
1767 RtlFreeUnicodeString(&rootW);
1768 return ret;
1773 /***********************************************************************
1774 * GetCurrentDirectory (KERNEL.411)
1776 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1778 WCHAR cur_dirW[MAX_PATH];
1780 DRIVE_GetCurrentDirectory(MAX_PATH, cur_dirW);
1781 return (UINT16)WideCharToMultiByte(CP_ACP, 0, cur_dirW, -1, buf, buflen, NULL, NULL);
1785 /***********************************************************************
1786 * GetCurrentDirectoryW (KERNEL32.@)
1788 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1790 UINT ret;
1791 WCHAR longname[MAX_PATHNAME_LEN];
1792 WCHAR shortname[MAX_PATHNAME_LEN];
1794 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1795 if ( ret > MAX_PATHNAME_LEN ) {
1796 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1797 return ret;
1799 GetLongPathNameW(shortname, longname, MAX_PATHNAME_LEN);
1800 ret = strlenW( longname ) + 1;
1801 if (ret > buflen) return ret;
1802 strcpyW(buf, longname);
1803 return ret - 1;
1806 /***********************************************************************
1807 * GetCurrentDirectoryA (KERNEL32.@)
1809 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1811 WCHAR bufferW[MAX_PATH];
1812 DWORD ret, retW;
1814 retW = GetCurrentDirectoryW(MAX_PATH, bufferW);
1816 if (!retW)
1817 ret = 0;
1818 else if (retW > MAX_PATH)
1820 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1821 ret = 0;
1823 else
1825 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
1826 if (buflen >= ret)
1828 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buf, buflen, NULL, NULL);
1829 ret--; /* length without 0 */
1832 return ret;
1836 /***********************************************************************
1837 * SetCurrentDirectory (KERNEL.412)
1839 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1841 return SetCurrentDirectoryA( dir );
1845 /***********************************************************************
1846 * SetCurrentDirectoryW (KERNEL32.@)
1848 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dir )
1850 int drive, olddrive = DRIVE_GetCurrentDrive();
1852 if (!dir)
1854 SetLastError(ERROR_INVALID_PARAMETER);
1855 return FALSE;
1857 if (dir[0] && (dir[1]==':'))
1859 drive = toupperW( *dir ) - 'A';
1860 dir += 2;
1862 else
1863 drive = olddrive;
1865 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1866 sets pTask->curdir only if pTask->curdrive is drive */
1867 if (!(DRIVE_SetCurrentDrive( drive )))
1868 return FALSE;
1870 /* FIXME: what about empty strings? Add a \\ ? */
1871 if (!DRIVE_Chdir( drive, dir )) {
1872 DRIVE_SetCurrentDrive(olddrive);
1873 return FALSE;
1875 return TRUE;
1879 /***********************************************************************
1880 * SetCurrentDirectoryA (KERNEL32.@)
1882 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1884 UNICODE_STRING dirW;
1885 BOOL ret = FALSE;
1887 if (!dir)
1889 SetLastError(ERROR_INVALID_PARAMETER);
1890 return FALSE;
1893 if (RtlCreateUnicodeStringFromAsciiz(&dirW, dir))
1895 ret = SetCurrentDirectoryW(dirW.Buffer);
1896 RtlFreeUnicodeString(&dirW);
1898 else
1899 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1900 return ret;
1904 /***********************************************************************
1905 * GetLogicalDriveStringsA (KERNEL32.@)
1907 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1909 int drive, count;
1911 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1912 if (DRIVE_IsValid(drive)) count++;
1913 if ((count * 4) + 1 <= len)
1915 LPSTR p = buffer;
1916 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1917 if (DRIVE_IsValid(drive))
1919 *p++ = 'a' + drive;
1920 *p++ = ':';
1921 *p++ = '\\';
1922 *p++ = '\0';
1924 *p = '\0';
1925 return count * 4;
1927 else
1928 return (count * 4) + 1; /* account for terminating null */
1929 /* The API tells about these different return values */
1933 /***********************************************************************
1934 * GetLogicalDriveStringsW (KERNEL32.@)
1936 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1938 int drive, count;
1940 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1941 if (DRIVE_IsValid(drive)) count++;
1942 if (count * 4 * sizeof(WCHAR) <= len)
1944 LPWSTR p = buffer;
1945 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1946 if (DRIVE_IsValid(drive))
1948 *p++ = (WCHAR)('a' + drive);
1949 *p++ = (WCHAR)':';
1950 *p++ = (WCHAR)'\\';
1951 *p++ = (WCHAR)'\0';
1953 *p = (WCHAR)'\0';
1955 return count * 4 * sizeof(WCHAR);
1959 /***********************************************************************
1960 * GetLogicalDrives (KERNEL32.@)
1962 DWORD WINAPI GetLogicalDrives(void)
1964 DWORD ret = 0;
1965 int drive;
1967 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1969 if ( (DRIVE_IsValid(drive)) ||
1970 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1971 ret |= (1 << drive);
1973 return ret;
1977 /***********************************************************************
1978 * GetVolumeInformationW (KERNEL32.@)
1980 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1981 DWORD label_len, DWORD *serial,
1982 DWORD *filename_len, DWORD *flags,
1983 LPWSTR fsname, DWORD fsname_len )
1985 int drive;
1986 LPWSTR cp;
1988 /* FIXME, SetLastError()s missing */
1990 if (!root) drive = DRIVE_GetCurrentDrive();
1991 else
1993 if (root[0] && root[1] != ':')
1995 WARN("invalid root %s\n", debugstr_w(root));
1996 return FALSE;
1998 drive = toupperW(root[0]) - 'A';
2000 if (!DRIVE_IsValid( drive )) return FALSE;
2001 if (label && label_len)
2003 strncpyW( label, DRIVE_GetLabel(drive), label_len );
2004 label[label_len - 1] = 0; /* ensure 0 termination */
2005 cp = label + strlenW(label);
2006 while (cp != label && *(cp-1) == ' ') cp--;
2007 *cp = '\0';
2009 if (serial) *serial = DRIVE_GetSerialNumber(drive);
2011 /* Set the filesystem information */
2012 /* Note: we only emulate a FAT fs at present */
2014 if (filename_len) {
2015 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
2016 *filename_len = 12;
2017 else
2018 *filename_len = 255;
2020 if (flags)
2022 *flags=0;
2023 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
2024 *flags|=FS_CASE_SENSITIVE;
2025 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
2026 *flags|=FS_CASE_IS_PRESERVED;
2028 if (fsname && fsname_len)
2030 /* Diablo checks that return code ... */
2031 if (DOSDrives[drive].type == DRIVE_CDROM)
2033 static const WCHAR cdfsW[] = {'C','D','F','S',0};
2034 strncpyW( fsname, cdfsW, fsname_len );
2036 else
2038 static const WCHAR fatW[] = {'F','A','T',0};
2039 strncpyW( fsname, fatW, fsname_len );
2041 fsname[fsname_len - 1] = 0; /* ensure 0 termination */
2043 return TRUE;
2047 /***********************************************************************
2048 * GetVolumeInformationA (KERNEL32.@)
2050 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
2051 DWORD label_len, DWORD *serial,
2052 DWORD *filename_len, DWORD *flags,
2053 LPSTR fsname, DWORD fsname_len )
2055 UNICODE_STRING rootW;
2056 LPWSTR labelW, fsnameW;
2057 BOOL ret;
2059 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
2060 else rootW.Buffer = NULL;
2061 labelW = label ? HeapAlloc(GetProcessHeap(), 0, label_len * sizeof(WCHAR)) : NULL;
2062 fsnameW = fsname ? HeapAlloc(GetProcessHeap(), 0, fsname_len * sizeof(WCHAR)) : NULL;
2064 if ((ret = GetVolumeInformationW(rootW.Buffer, labelW, label_len, serial,
2065 filename_len, flags, fsnameW, fsname_len)))
2067 if (label) WideCharToMultiByte(CP_ACP, 0, labelW, -1, label, label_len, NULL, NULL);
2068 if (fsname) WideCharToMultiByte(CP_ACP, 0, fsnameW, -1, fsname, fsname_len, NULL, NULL);
2071 RtlFreeUnicodeString(&rootW);
2072 if (labelW) HeapFree( GetProcessHeap(), 0, labelW );
2073 if (fsnameW) HeapFree( GetProcessHeap(), 0, fsnameW );
2074 return ret;
2077 /***********************************************************************
2078 * SetVolumeLabelW (KERNEL32.@)
2080 BOOL WINAPI SetVolumeLabelW( LPCWSTR root, LPCWSTR volname )
2082 int drive;
2084 /* FIXME, SetLastErrors missing */
2086 if (!root) drive = DRIVE_GetCurrentDrive();
2087 else
2089 if ((root[1]) && (root[1] != ':'))
2091 WARN("invalid root %s\n", debugstr_w(root));
2092 return FALSE;
2094 drive = toupperW(root[0]) - 'A';
2096 if (!DRIVE_IsValid( drive )) return FALSE;
2098 /* some copy protection stuff check this */
2099 if (DOSDrives[drive].type == DRIVE_CDROM) return FALSE;
2101 strncpyW(DOSDrives[drive].label_conf, volname, 12);
2102 DOSDrives[drive].label_conf[12 - 1] = 0; /* ensure 0 termination */
2103 return TRUE;
2106 /***********************************************************************
2107 * SetVolumeLabelA (KERNEL32.@)
2109 BOOL WINAPI SetVolumeLabelA(LPCSTR root, LPCSTR volname)
2111 UNICODE_STRING rootW, volnameW;
2112 BOOL ret;
2114 if (root) RtlCreateUnicodeStringFromAsciiz(&rootW, root);
2115 else rootW.Buffer = NULL;
2116 if (volname) RtlCreateUnicodeStringFromAsciiz(&volnameW, volname);
2117 else volnameW.Buffer = NULL;
2119 ret = SetVolumeLabelW( rootW.Buffer, volnameW.Buffer );
2121 RtlFreeUnicodeString(&rootW);
2122 RtlFreeUnicodeString(&volnameW);
2123 return ret;
2126 /***********************************************************************
2127 * GetVolumeNameForVolumeMountPointW (KERNEL32.@)
2129 DWORD WINAPI GetVolumeNameForVolumeMountPointW(LPWSTR str, DWORD a, DWORD b)
2131 FIXME("(%s, %lx, %lx): stub\n", debugstr_w(str), a, b);
2132 return 0;