2 * DOS drives handling functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
7 * Label & serial number read support.
8 * (c) 1999 Petr Tomasek <tomasek@etf.cuni.cz>
9 * (c) 2000 Andreas Mohr (changes)
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include "wine/port.h"
35 #include <sys/types.h>
43 #ifdef HAVE_SYS_PARAM_H
44 # include <sys/param.h>
46 #ifdef STATFS_DEFINED_BY_SYS_VFS
49 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
50 # include <sys/mount.h>
52 # ifdef STATFS_DEFINED_BY_SYS_STATFS
53 # include <sys/statfs.h>
58 #define NONAMELESSUNION
59 #define NONAMELESSSTRUCT
65 #include "wine/winbase16.h" /* for GetCurrentTask */
73 #include "wine/unicode.h"
74 #include "wine/library.h"
75 #include "wine/server.h"
76 #include "wine/debug.h"
78 WINE_DEFAULT_DEBUG_CHANNEL(dosfs
);
79 WINE_DECLARE_DEBUG_CHANNEL(file
);
83 char *root
; /* root dir in Unix format without trailing / */
84 LPWSTR dos_cwd
; /* cwd in DOS format without leading or trailing \ */
85 char *unix_cwd
; /* cwd in Unix format without leading or trailing / */
86 char *device
; /* raw device path */
87 WCHAR label_conf
[12]; /* drive label as cfg'd in wine config */
88 WCHAR label_read
[12]; /* drive label as read from device */
89 DWORD serial_conf
; /* drive serial number as cfg'd in wine config */
90 UINT type
; /* drive type */
91 UINT flags
; /* drive flags */
92 dev_t dev
; /* unix device number */
93 ino_t ino
; /* unix inode number */
97 static const WCHAR DRIVE_Types
[][8] =
99 { 0 }, /* DRIVE_UNKNOWN */
100 { 0 }, /* DRIVE_NO_ROOT_DIR */
101 {'f','l','o','p','p','y',0}, /* DRIVE_REMOVABLE */
102 {'h','d',0}, /* DRIVE_FIXED */
103 {'n','e','t','w','o','r','k',0}, /* DRIVE_REMOTE */
104 {'c','d','r','o','m',0}, /* DRIVE_CDROM */
105 {'r','a','m','d','i','s','k',0} /* DRIVE_RAMDISK */
109 /* Known filesystem types */
117 static const FS_DESCR DRIVE_Filesystems
[] =
119 { {'u','n','i','x',0}, DRIVE_CASE_SENSITIVE
| DRIVE_CASE_PRESERVING
},
120 { {'m','s','d','o','s',0}, DRIVE_SHORT_NAMES
},
121 { {'d','o','s',0}, DRIVE_SHORT_NAMES
},
122 { {'f','a','t',0}, DRIVE_SHORT_NAMES
},
123 { {'v','f','a','t',0}, DRIVE_CASE_PRESERVING
},
124 { {'w','i','n','9','5',0}, DRIVE_CASE_PRESERVING
},
129 static DOSDRIVE DOSDrives
[MAX_DOS_DRIVES
];
130 static int DRIVE_CurDrive
= -1;
132 static HTASK16 DRIVE_LastTask
= 0;
134 /* strdup on the process heap */
135 inline static char *heap_strdup( const char *str
)
137 INT len
= strlen(str
) + 1;
138 LPSTR p
= HeapAlloc( GetProcessHeap(), 0, len
);
139 if (p
) memcpy( p
, str
, len
);
143 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
145 extern void CDROM_InitRegistry(int dev
, int id
, const char *device
);
147 /***********************************************************************
150 static inline UINT
DRIVE_GetDriveType( INT drive
, LPCWSTR value
)
154 for (i
= 0; i
< sizeof(DRIVE_Types
)/sizeof(DRIVE_Types
[0]); i
++)
156 if (!strcmpiW( value
, DRIVE_Types
[i
] )) return i
;
158 MESSAGE("Drive %c: unknown drive type %s, defaulting to 'hd'.\n",
159 'A' + drive
, debugstr_w(value
) );
164 /***********************************************************************
167 static UINT
DRIVE_GetFSFlags( INT drive
, LPCWSTR value
)
169 const FS_DESCR
*descr
;
171 for (descr
= DRIVE_Filesystems
; *descr
->name
; descr
++)
172 if (!strcmpiW( value
, descr
->name
)) return descr
->flags
;
173 MESSAGE("Drive %c: unknown filesystem type %s, defaulting to 'win95'.\n",
174 'A' + drive
, debugstr_w(value
) );
175 return DRIVE_CASE_PRESERVING
;
179 /***********************************************************************
184 int i
, len
, count
= 0;
185 WCHAR driveW
[] = {'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
186 'W','i','n','e','\\','W','i','n','e','\\',
187 'C','o','n','f','i','g','\\','D','r','i','v','e',' ','A',0};
188 WCHAR drive_env
[] = {'=','A',':',0};
189 WCHAR path
[MAX_PATHNAME_LEN
];
190 char tmp
[MAX_PATHNAME_LEN
*sizeof(WCHAR
) + sizeof(KEY_VALUE_PARTIAL_INFORMATION
)];
191 struct stat drive_stat_buffer
;
196 OBJECT_ATTRIBUTES attr
;
197 UNICODE_STRING nameW
;
199 static const WCHAR PathW
[] = {'P','a','t','h',0};
200 static const WCHAR LabelW
[] = {'L','a','b','e','l',0};
201 static const WCHAR SerialW
[] = {'S','e','r','i','a','l',0};
202 static const WCHAR TypeW
[] = {'T','y','p','e',0};
203 static const WCHAR FilesystemW
[] = {'F','i','l','e','s','y','s','t','e','m',0};
204 static const WCHAR DeviceW
[] = {'D','e','v','i','c','e',0};
205 static const WCHAR ReadVolInfoW
[] = {'R','e','a','d','V','o','l','I','n','f','o',0};
206 static const WCHAR FailReadOnlyW
[] = {'F','a','i','l','R','e','a','d','O','n','l','y',0};
207 static const WCHAR driveC_labelW
[] = {'D','r','i','v','e',' ','C',' ',' ',' ',' ',0};
210 attr
.Length
= sizeof(attr
);
211 attr
.RootDirectory
= 0;
212 attr
.ObjectName
= &nameW
;
214 attr
.SecurityDescriptor
= NULL
;
215 attr
.SecurityQualityOfService
= NULL
;
217 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
219 RtlInitUnicodeString( &nameW
, driveW
);
220 nameW
.Buffer
[(nameW
.Length
/ sizeof(WCHAR
)) - 1] = 'A' + i
;
221 if (NtOpenKey( &hkey
, KEY_ALL_ACCESS
, &attr
) != STATUS_SUCCESS
) continue;
223 /* Get the root path */
224 RtlInitUnicodeString( &nameW
, PathW
);
225 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
227 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
228 ExpandEnvironmentStringsW( data
, path
, sizeof(path
)/sizeof(WCHAR
) );
230 p
= path
+ strlenW(path
) - 1;
231 while ((p
> path
) && (*p
== '/')) *p
-- = '\0';
235 len
= WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1, NULL
, 0, NULL
, NULL
);
236 drive
->root
= HeapAlloc(GetProcessHeap(), 0, len
);
237 WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1, drive
->root
, len
, NULL
, NULL
);
241 /* relative paths are relative to config dir */
242 const char *config
= wine_get_config_dir();
243 len
= strlen(config
);
244 len
+= WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1, NULL
, 0, NULL
, NULL
) + 2;
245 drive
->root
= HeapAlloc( GetProcessHeap(), 0, len
);
246 len
-= sprintf( drive
->root
, "%s/", config
);
247 WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1,
248 drive
->root
+ strlen(drive
->root
), len
, NULL
, NULL
);
251 if (stat( drive
->root
, &drive_stat_buffer
))
253 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
254 drive
->root
, strerror(errno
), 'A' + i
);
255 HeapFree( GetProcessHeap(), 0, drive
->root
);
259 if (!S_ISDIR(drive_stat_buffer
.st_mode
))
261 MESSAGE("%s is not a directory, ignoring drive %c:\n",
262 drive
->root
, 'A' + i
);
263 HeapFree( GetProcessHeap(), 0, drive
->root
);
268 drive
->dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(drive
->dos_cwd
[0]));
269 drive
->unix_cwd
= heap_strdup( "" );
270 drive
->device
= NULL
;
272 drive
->dev
= drive_stat_buffer
.st_dev
;
273 drive
->ino
= drive_stat_buffer
.st_ino
;
275 /* Get the drive type */
276 RtlInitUnicodeString( &nameW
, TypeW
);
277 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
279 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
280 drive
->type
= DRIVE_GetDriveType( i
, data
);
282 else drive
->type
= DRIVE_FIXED
;
284 /* Get the drive label */
285 RtlInitUnicodeString( &nameW
, LabelW
);
286 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
288 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
289 lstrcpynW( drive
->label_conf
, data
, 12 );
291 if ((len
= strlenW(drive
->label_conf
)) < 11)
293 /* Pad label with spaces */
294 while(len
< 11) drive
->label_conf
[len
++] = ' ';
295 drive
->label_conf
[11] = '\0';
298 /* Get the serial number */
299 RtlInitUnicodeString( &nameW
, SerialW
);
300 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
302 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
303 drive
->serial_conf
= strtoulW( data
, NULL
, 16 );
305 else drive
->serial_conf
= 12345678;
307 /* Get the filesystem type */
308 RtlInitUnicodeString( &nameW
, FilesystemW
);
309 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
311 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
312 drive
->flags
= DRIVE_GetFSFlags( i
, data
);
314 else drive
->flags
= DRIVE_CASE_PRESERVING
;
317 RtlInitUnicodeString( &nameW
, DeviceW
);
318 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
320 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
321 len
= WideCharToMultiByte(CP_UNIXCP
, 0, data
, -1, NULL
, 0, NULL
, NULL
);
322 drive
->device
= HeapAlloc(GetProcessHeap(), 0, len
);
323 WideCharToMultiByte(CP_UNIXCP
, 0, data
, -1, drive
->device
, len
, NULL
, NULL
);
325 RtlInitUnicodeString( &nameW
, ReadVolInfoW
);
326 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
328 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
329 if (IS_OPTION_TRUE(data
[0])) drive
->flags
|= DRIVE_READ_VOL_INFO
;
331 else drive
->flags
|= DRIVE_READ_VOL_INFO
;
333 if (drive
->type
== DRIVE_CDROM
)
336 if ((cd_fd
= open(drive
->device
, O_RDONLY
|O_NONBLOCK
)) != -1)
338 CDROM_InitRegistry(cd_fd
, i
, drive
->device
);
344 /* Get the FailReadOnly flag */
345 RtlInitUnicodeString( &nameW
, FailReadOnlyW
);
346 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
348 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
349 if (IS_OPTION_TRUE(data
[0])) drive
->flags
|= DRIVE_FAIL_READ_ONLY
;
352 /* Make the first hard disk the current drive */
353 if ((DRIVE_CurDrive
== -1) && (drive
->type
== DRIVE_FIXED
))
357 TRACE("Drive %c: path=%s type=%s label=%s serial=%08lx "
358 "flags=%08x dev=%x ino=%x\n",
359 'A' + i
, drive
->root
, debugstr_w(DRIVE_Types
[drive
->type
]),
360 debugstr_w(drive
->label_conf
), drive
->serial_conf
, drive
->flags
,
361 (int)drive
->dev
, (int)drive
->ino
);
370 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
371 /* Create a C drive pointing to Unix root dir */
372 DOSDrives
[2].root
= heap_strdup( "/" );
373 DOSDrives
[2].dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(DOSDrives
[2].dos_cwd
[0]));
374 DOSDrives
[2].unix_cwd
= heap_strdup( "" );
375 strcpyW( DOSDrives
[2].label_conf
, driveC_labelW
);
376 DOSDrives
[2].serial_conf
= 12345678;
377 DOSDrives
[2].type
= DRIVE_FIXED
;
378 DOSDrives
[2].device
= NULL
;
379 DOSDrives
[2].flags
= 0;
383 /* Make sure the current drive is valid */
384 if (DRIVE_CurDrive
== -1)
386 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
388 if (drive
->root
&& !(drive
->flags
& DRIVE_DISABLED
))
396 /* get current working directory info for all drives */
397 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++, drive_env
[1]++)
399 if (!GetEnvironmentVariableW(drive_env
, path
, MAX_PATHNAME_LEN
)) continue;
401 if (toupperW(path
[0]) != drive_env
[1] || path
[1] != ':') continue;
402 DRIVE_Chdir( i
, path
+ 2 );
408 /***********************************************************************
411 int DRIVE_IsValid( int drive
)
413 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
414 return (DOSDrives
[drive
].root
&&
415 !(DOSDrives
[drive
].flags
& DRIVE_DISABLED
));
419 /***********************************************************************
420 * DRIVE_GetCurrentDrive
422 int DRIVE_GetCurrentDrive(void)
424 TDB
*pTask
= GlobalLock16(GetCurrentTask());
425 if (pTask
&& (pTask
->curdrive
& 0x80)) return pTask
->curdrive
& ~0x80;
426 return DRIVE_CurDrive
;
430 /***********************************************************************
431 * DRIVE_SetCurrentDrive
433 int DRIVE_SetCurrentDrive( int drive
)
435 TDB
*pTask
= GlobalLock16(GetCurrentTask());
436 if (!DRIVE_IsValid( drive
))
438 SetLastError( ERROR_INVALID_DRIVE
);
441 TRACE("%c:\n", 'A' + drive
);
442 DRIVE_CurDrive
= drive
;
443 if (pTask
) pTask
->curdrive
= drive
| 0x80;
448 /***********************************************************************
449 * DRIVE_FindDriveRoot
451 * Find a drive for which the root matches the beginning of the given path.
452 * This can be used to translate a Unix path into a drive + DOS path.
453 * Return value is the drive, or -1 on error. On success, path is modified
454 * to point to the beginning of the DOS path.
456 * Note: path must be in the encoding of the underlying Unix file system.
458 int DRIVE_FindDriveRoot( const char **path
)
460 /* Starting with the full path, check if the device and inode match any of
461 * the wine 'drives'. If not then remove the last path component and try
462 * again. If the last component was a '..' then skip a normal component
463 * since it's a directory that's ascended back out of.
465 int drive
, level
, len
;
466 char buffer
[MAX_PATHNAME_LEN
];
470 strcpy( buffer
, *path
);
471 for (p
= buffer
; *p
; p
++) if (*p
== '\\') *p
= '/';
474 /* strip off trailing slashes */
475 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
480 if (stat( buffer
, &st
) == 0 && S_ISDIR( st
.st_mode
))
482 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
484 if (!DOSDrives
[drive
].root
||
485 (DOSDrives
[drive
].flags
& DRIVE_DISABLED
))
488 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
489 (DOSDrives
[drive
].ino
== st
.st_ino
))
491 if (len
== 1) len
= 0; /* preserve root slash in returned path */
492 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
493 *path
, 'A' + drive
, buffer
, *path
+ len
);
495 if (!**path
) *path
= "\\";
500 if (len
<= 1) return -1; /* reached root */
505 /* find start of the last path component */
506 while (len
> 1 && buffer
[len
- 1] != '/') len
--;
507 if (!buffer
[len
]) break; /* empty component -> reached root */
508 /* does removing it take us up a level? */
509 if (strcmp( buffer
+ len
, "." ) != 0)
510 level
+= strcmp( buffer
+ len
, ".." ) ? 1 : -1;
512 /* strip off trailing slashes */
513 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
519 /***********************************************************************
520 * DRIVE_FindDriveRootW
522 * Unicode version of DRIVE_FindDriveRoot.
524 int DRIVE_FindDriveRootW( LPCWSTR
*path
)
526 int drive
, level
, len
;
527 WCHAR buffer
[MAX_PATHNAME_LEN
];
531 strcpyW( buffer
, *path
);
532 for (p
= buffer
; *p
; p
++) if (*p
== '\\') *p
= '/';
535 /* strip off trailing slashes */
536 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
540 char buffA
[MAX_PATHNAME_LEN
];
542 WideCharToMultiByte( CP_UNIXCP
, 0, buffer
, -1, buffA
, sizeof(buffA
), NULL
, NULL
);
543 if (stat( buffA
, &st
) == 0 && S_ISDIR( st
.st_mode
))
546 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
548 if (!DOSDrives
[drive
].root
||
549 (DOSDrives
[drive
].flags
& DRIVE_DISABLED
))
552 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
553 (DOSDrives
[drive
].ino
== st
.st_ino
))
555 static const WCHAR rootW
[] = {'\\',0};
557 if (len
== 1) len
= 0; /* preserve root slash in returned path */
558 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
559 debugstr_w(*path
), 'A' + drive
, debugstr_w(buffer
), debugstr_w(*path
+ len
));
561 if (!**path
) *path
= rootW
;
566 if (len
<= 1) return -1; /* reached root */
571 static const WCHAR dotW
[] = {'.',0};
572 static const WCHAR dotdotW
[] = {'.','.',0};
574 /* find start of the last path component */
575 while (len
> 1 && buffer
[len
- 1] != '/') len
--;
576 if (!buffer
[len
]) break; /* empty component -> reached root */
577 /* does removing it take us up a level? */
578 if (strcmpW( buffer
+ len
, dotW
) != 0)
579 level
+= strcmpW( buffer
+ len
, dotdotW
) ? 1 : -1;
581 /* strip off trailing slashes */
582 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
588 /***********************************************************************
591 const char * DRIVE_GetRoot( int drive
)
593 if (!DRIVE_IsValid( drive
)) return NULL
;
594 return DOSDrives
[drive
].root
;
598 /***********************************************************************
601 LPCWSTR
DRIVE_GetDosCwd( int drive
)
603 TDB
*pTask
= GlobalLock16(GetCurrentTask());
604 if (!DRIVE_IsValid( drive
)) return NULL
;
606 /* Check if we need to change the directory to the new task. */
607 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
608 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
609 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
611 static const WCHAR rootW
[] = {'\\',0};
612 WCHAR curdirW
[MAX_PATH
];
613 MultiByteToWideChar(CP_ACP
, 0, pTask
->curdir
, -1, curdirW
, MAX_PATH
);
614 /* Perform the task-switch */
615 if (!DRIVE_Chdir( drive
, curdirW
)) DRIVE_Chdir( drive
, rootW
);
616 DRIVE_LastTask
= GetCurrentTask();
618 return DOSDrives
[drive
].dos_cwd
;
622 /***********************************************************************
625 const char * DRIVE_GetUnixCwd( int drive
)
627 TDB
*pTask
= GlobalLock16(GetCurrentTask());
628 if (!DRIVE_IsValid( drive
)) return NULL
;
630 /* Check if we need to change the directory to the new task. */
631 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
632 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
633 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
635 static const WCHAR rootW
[] = {'\\',0};
636 WCHAR curdirW
[MAX_PATH
];
637 MultiByteToWideChar(CP_ACP
, 0, pTask
->curdir
, -1, curdirW
, MAX_PATH
);
638 /* Perform the task-switch */
639 if (!DRIVE_Chdir( drive
, curdirW
)) DRIVE_Chdir( drive
, rootW
);
640 DRIVE_LastTask
= GetCurrentTask();
642 return DOSDrives
[drive
].unix_cwd
;
646 /***********************************************************************
649 const char * DRIVE_GetDevice( int drive
)
651 return (DRIVE_IsValid( drive
)) ? DOSDrives
[drive
].device
: NULL
;
654 /******************************************************************
655 * static WORD CDROM_Data_FindBestVoldesc
659 static WORD
CDROM_Data_FindBestVoldesc(int fd
)
661 BYTE cur_vd_type
, max_vd_type
= 0;
662 unsigned int offs
, best_offs
= 0, extra_offs
= 0;
665 for (offs
= 0x8000; offs
<= 0x9800; offs
+= 0x800)
667 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
668 * the volume label is displaced forward by 8
670 lseek(fd
, offs
+ 11, SEEK_SET
); /* check for non-ISO9660 signature */
672 if ((sig
[0] == 'R') && (sig
[1] == 'O') && (sig
[2]=='M'))
676 lseek(fd
, offs
+ extra_offs
, SEEK_SET
);
677 read(fd
, &cur_vd_type
, 1);
678 if (cur_vd_type
== 0xff) /* voldesc set terminator */
680 if (cur_vd_type
> max_vd_type
)
682 max_vd_type
= cur_vd_type
;
683 best_offs
= offs
+ extra_offs
;
689 /***********************************************************************
690 * DRIVE_ReadSuperblock
693 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
694 * to check, that they are writing on a FAT filesystem !
696 int DRIVE_ReadSuperblock (int drive
, char * buff
)
698 #define DRIVE_SUPER 96
702 struct stat stat_buf
;
704 memset(buff
, 0, DRIVE_SUPER
);
705 /* O_NONBLOCK in case we're opening FIFO; will be reset later */
706 if ((fd
= open(DOSDrives
[drive
].device
, O_RDONLY
|O_NOCTTY
|O_NONBLOCK
)) != -1) {
707 if (fstat(fd
, &stat_buf
) < 0) { /* shouldn't happen since we just opened that file */
708 ERR("fstat() failed for opened device '%s' (drive %c:) ! IT SHOULDN'T HAPPEN !!!\n",
709 DOSDrives
[drive
].device
, 'A'+drive
);
711 } else if (!S_ISBLK(stat_buf
.st_mode
)) {
712 ERR("Device '%s' (drive %c:) is not a block device - check your config\n",
713 DOSDrives
[drive
].device
, 'A'+drive
);
715 /* reset O_NONBLOCK */
716 } else if (fcntl(fd
, F_SETFL
, 0) < 0 || fcntl(fd
, F_GETFL
) & O_NONBLOCK
) {
717 ERR("fcntl() failed to reset O_NONBLOCK for device '%s' (drive %c:)\n",
718 DOSDrives
[drive
].device
, 'A'+drive
);
726 if (!DOSDrives
[drive
].device
)
727 ERR("No device configured for drive %c: !\n", 'A'+drive
);
729 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives
[drive
].device
, 'A'+drive
,
730 (stat(DOSDrives
[drive
].device
, &stat_buf
)) ?
731 "not available or symlink not valid ?" : "no permission");
734 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
738 switch(DOSDrives
[drive
].type
)
740 case DRIVE_REMOVABLE
:
745 offs
= CDROM_Data_FindBestVoldesc(fd
);
752 if ((offs
) && (lseek(fd
,offs
,SEEK_SET
)!=offs
))
757 if (read(fd
,buff
,DRIVE_SUPER
)!=DRIVE_SUPER
)
763 switch(DOSDrives
[drive
].type
)
765 case DRIVE_REMOVABLE
:
767 if ((buff
[0x26]!=0x29) || /* Check for FAT present */
768 /* FIXME: do really all FAT have their name beginning with
769 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
770 memcmp( buff
+0x36,"FAT",3))
772 ERR("The filesystem is not FAT !! (device=%s)\n",
773 DOSDrives
[drive
].device
);
779 if (strncmp(&buff
[1],"CD001",5)) /* Check for iso9660 present */
784 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
798 /***********************************************************************
799 * DRIVE_WriteSuperblockEntry
802 * We are writing as little as possible (ie. not the whole SuperBlock)
803 * not to interfere with kernel. The drive can be mounted !
805 int DRIVE_WriteSuperblockEntry (int drive
, off_t ofs
, size_t len
, char * buff
)
809 if ((fd
=open(DOSDrives
[drive
].device
,O_WRONLY
))==-1)
811 ERR("Cannot open the device %s (for writing)\n",
812 DOSDrives
[drive
].device
);
815 if (lseek(fd
,ofs
,SEEK_SET
)!=ofs
)
817 ERR("lseek failed on device %s !\n",
818 DOSDrives
[drive
].device
);
822 if (write(fd
,buff
,len
)!=len
)
825 ERR("Cannot write on %s !\n",
826 DOSDrives
[drive
].device
);
832 /******************************************************************
833 * static HANDLE CDROM_Open
837 static HANDLE
CDROM_Open(int drive
)
839 WCHAR root
[] = {'\\','\\','.','\\','A',':',0};
841 return CreateFileW(root
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, 0);
844 /**************************************************************************
845 * CDROM_Data_GetLabel [internal]
847 DWORD
CDROM_Data_GetLabel(int drive
, WCHAR
*label
)
849 #define LABEL_LEN 32+1
850 int dev
= open(DOSDrives
[drive
].device
, O_RDONLY
|O_NONBLOCK
);
851 WORD offs
= CDROM_Data_FindBestVoldesc(dev
);
852 WCHAR label_read
[LABEL_LEN
]; /* Unicode possible, too */
853 DWORD unicode_id
= 0;
857 if ((lseek(dev
, offs
+0x58, SEEK_SET
) == offs
+0x58)
858 && (read(dev
, &unicode_id
, 3) == 3))
860 int ver
= (unicode_id
& 0xff0000) >> 16;
862 if ((lseek(dev
, offs
+0x28, SEEK_SET
) != offs
+0x28)
863 || (read(dev
, &label_read
, LABEL_LEN
) != LABEL_LEN
))
867 if ((LOWORD(unicode_id
) == 0x2f25) /* Unicode ID */
868 && ((ver
== 0x40) || (ver
== 0x43) || (ver
== 0x45)))
869 { /* yippee, unicode */
872 for (i
=0; i
<LABEL_LEN
;i
++)
873 { /* Motorola -> Intel Unicode conversion :-\ */
875 label_read
[i
] = (ch
<< 8) | (ch
>> 8);
877 strncpyW(label
, label_read
, 11);
882 MultiByteToWideChar(CP_UNIXCP
, 0, (LPSTR
)label_read
, -1, label
, 11);
890 ERR("error reading label !\n");
894 /**************************************************************************
895 * CDROM_GetLabel [internal]
897 static DWORD
CDROM_GetLabel(int drive
, WCHAR
*label
)
904 h
= CDROM_Open(drive
);
907 r
= DeviceIoControl(h
, IOCTL_CDROM_DISK_TYPE
, NULL
,
908 0, &cdd
, sizeof(cdd
), &br
, 0);
913 switch (cdd
.DiskData
& 0x03)
915 case CDROM_DISK_DATA_TRACK
:
916 if (!CDROM_Data_GetLabel(drive
, label
))
919 case CDROM_DISK_AUDIO_TRACK
:
921 static const WCHAR audioCD
[] = {'A','u','d','i','o',' ','C','D',' ',' ',' ',0};
922 strcpyW(label
, audioCD
);
925 case CDROM_DISK_DATA_TRACK
|CDROM_DISK_AUDIO_TRACK
:
926 FIXME("Need to get the label of a mixed mode CD!\n");
927 /* This assumes that the first track is a data track! */
928 /* I guess the correct way would be to enumerate all data tracks
929 and check each for the title */
930 if (!CDROM_Data_GetLabel(drive
, label
))
937 TRACE("CD: label is %s\n", debugstr_w(label
));
941 /***********************************************************************
944 LPCWSTR
DRIVE_GetLabel( int drive
)
947 char buff
[DRIVE_SUPER
];
950 if (!DRIVE_IsValid( drive
)) return NULL
;
951 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
953 read
= CDROM_GetLabel(drive
, DOSDrives
[drive
].label_read
);
956 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
958 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
959 ERR("Invalid or unreadable superblock on %s (%c:).\n",
960 DOSDrives
[drive
].device
, (char)(drive
+'A'));
962 if (DOSDrives
[drive
].type
== DRIVE_REMOVABLE
||
963 DOSDrives
[drive
].type
== DRIVE_FIXED
)
966 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
968 MultiByteToWideChar(CP_UNIXCP
, 0, buff
+offs
, 11,
969 DOSDrives
[drive
].label_read
, 11);
970 DOSDrives
[drive
].label_read
[11]='\0';
976 DOSDrives
[drive
].label_read
: DOSDrives
[drive
].label_conf
;
979 #define CDFRAMES_PERSEC 75
980 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
981 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
982 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
984 /**************************************************************************
985 * CDROM_Audio_GetSerial [internal]
987 static DWORD
CDROM_Audio_GetSerial(HANDLE h
)
989 unsigned long serial
= 0;
992 DWORD dwStart
, dwEnd
, br
;
995 if (!DeviceIoControl(h
, IOCTL_CDROM_READ_TOC
, NULL
, 0, &toc
, sizeof(toc
), &br
, 0))
999 * wMagic collects the wFrames from track 1
1000 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
1002 * There it is collected for correcting the serial when there are less than
1005 wMagic
= toc
.TrackData
[0].Address
[2];
1006 dwStart
= FRAME_OF_TOC(toc
, toc
.FirstTrack
);
1008 for (i
= 0; i
<= toc
.LastTrack
- toc
.FirstTrack
; i
++) {
1009 serial
+= (toc
.TrackData
[i
].Address
[0] << 16) |
1010 (toc
.TrackData
[i
].Address
[1] << 8) | toc
.TrackData
[i
].Address
[2];
1012 dwEnd
= FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1);
1014 if (toc
.LastTrack
- toc
.FirstTrack
+ 1 < 3)
1015 serial
+= wMagic
+ (dwEnd
- dwStart
);
1020 /**************************************************************************
1021 * CDROM_Data_GetSerial [internal]
1023 static DWORD
CDROM_Data_GetSerial(int drive
)
1025 int dev
= open(DOSDrives
[drive
].device
, O_RDONLY
|O_NONBLOCK
);
1031 BYTE b0
= 0, b1
= 1, b2
= 2, b3
= 3;
1034 if (dev
== -1) return 0;
1035 offs
= CDROM_Data_FindBestVoldesc(dev
);
1041 RTL_OSVERSIONINFOEXW ovi
;
1044 lseek(dev
, offs
, SEEK_SET
);
1045 read(dev
, buf
, 2048);
1047 * OK, another braindead one... argh. Just believe it.
1048 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
1049 * It's true and nobody will ever be able to change it.
1051 ovi
.dwOSVersionInfoSize
= sizeof(ovi
);
1052 RtlGetVersion(&ovi
);
1053 if ((ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) && (ovi
.dwMajorVersion
>= 4))
1055 b0
= 3; b1
= 2; b2
= 1; b3
= 0;
1057 for (i
= 0; i
< 2048; i
+= 4)
1059 /* DON'T optimize this into DWORD !! (breaks overflow) */
1060 serial
.p
[b0
] += buf
[i
+b0
];
1061 serial
.p
[b1
] += buf
[i
+b1
];
1062 serial
.p
[b2
] += buf
[i
+b2
];
1063 serial
.p
[b3
] += buf
[i
+b3
];
1070 /**************************************************************************
1071 * CDROM_GetSerial [internal]
1073 static DWORD
CDROM_GetSerial(int drive
)
1077 CDROM_DISK_DATA cdd
;
1081 TRACE("%d\n", drive
);
1083 h
= CDROM_Open(drive
);
1086 r
= DeviceIoControl(h
, IOCTL_CDROM_DISK_TYPE
, NULL
,
1087 0, &cdd
, sizeof(cdd
), &br
, 0);
1094 switch (cdd
.DiskData
& 0x03)
1096 case CDROM_DISK_DATA_TRACK
:
1097 /* hopefully a data CD */
1098 serial
= CDROM_Data_GetSerial(drive
);
1100 case CDROM_DISK_AUDIO_TRACK
:
1102 case CDROM_DISK_DATA_TRACK
|CDROM_DISK_AUDIO_TRACK
:
1103 serial
= CDROM_Audio_GetSerial(h
);
1110 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial
), LOWORD(serial
));
1117 /***********************************************************************
1118 * DRIVE_GetSerialNumber
1120 DWORD
DRIVE_GetSerialNumber( int drive
)
1123 char buff
[DRIVE_SUPER
];
1125 TRACE("drive %d, type = %d\n", drive
, DOSDrives
[drive
].type
);
1127 if (!DRIVE_IsValid( drive
)) return 0;
1129 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
1131 switch(DOSDrives
[drive
].type
)
1133 case DRIVE_REMOVABLE
:
1135 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
1136 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
1137 " Maybe not FAT?\n" ,
1138 DOSDrives
[drive
].device
, 'A'+drive
);
1140 serial
= *((DWORD
*)(buff
+0x27));
1143 serial
= CDROM_GetSerial(drive
);
1146 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive
+'A');
1150 return (serial
) ? serial
: DOSDrives
[drive
].serial_conf
;
1154 /***********************************************************************
1155 * DRIVE_SetSerialNumber
1157 int DRIVE_SetSerialNumber( int drive
, DWORD serial
)
1159 char buff
[DRIVE_SUPER
];
1161 if (!DRIVE_IsValid( drive
)) return 0;
1163 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
1165 if ((DOSDrives
[drive
].type
!= DRIVE_REMOVABLE
) &&
1166 (DOSDrives
[drive
].type
!= DRIVE_FIXED
)) return 0;
1167 /* check, if the drive has a FAT filesystem */
1168 if (DRIVE_ReadSuperblock(drive
, buff
)) return 0;
1169 if (DRIVE_WriteSuperblockEntry(drive
, 0x27, 4, (char *) &serial
)) return 0;
1173 if (DOSDrives
[drive
].type
== DRIVE_CDROM
) return 0;
1174 DOSDrives
[drive
].serial_conf
= serial
;
1179 /***********************************************************************
1182 static UINT
DRIVE_GetType( int drive
)
1184 if (!DRIVE_IsValid( drive
)) return DRIVE_NO_ROOT_DIR
;
1185 return DOSDrives
[drive
].type
;
1189 /***********************************************************************
1192 UINT
DRIVE_GetFlags( int drive
)
1194 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
1195 return DOSDrives
[drive
].flags
;
1198 /***********************************************************************
1201 int DRIVE_Chdir( int drive
, LPCWSTR path
)
1203 DOS_FULL_NAME full_name
;
1204 WCHAR buffer
[MAX_PATHNAME_LEN
];
1206 BY_HANDLE_FILE_INFORMATION info
;
1207 TDB
*pTask
= GlobalLock16(GetCurrentTask());
1209 buffer
[0] = 'A' + drive
;
1212 TRACE("(%s,%s)\n", debugstr_w(buffer
), debugstr_w(path
) );
1213 strncpyW( buffer
+ 2, path
, MAX_PATHNAME_LEN
- 2 );
1214 buffer
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
1216 if (!DOSFS_GetFullName( buffer
, TRUE
, &full_name
)) return 0;
1217 if (!FILE_Stat( full_name
.long_name
, &info
, NULL
)) return 0;
1218 if (!(info
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1220 SetLastError( ERROR_FILE_NOT_FOUND
);
1223 unix_cwd
= full_name
.long_name
+ strlen( DOSDrives
[drive
].root
);
1224 while (*unix_cwd
== '/') unix_cwd
++;
1226 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
1227 'A' + drive
, unix_cwd
, debugstr_w(full_name
.short_name
+ 3) );
1229 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].dos_cwd
);
1230 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].unix_cwd
);
1231 DOSDrives
[drive
].dos_cwd
= HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name
.short_name
) - 2) * sizeof(WCHAR
));
1232 strcpyW(DOSDrives
[drive
].dos_cwd
, full_name
.short_name
+ 3);
1233 DOSDrives
[drive
].unix_cwd
= heap_strdup( unix_cwd
);
1235 if (drive
== DRIVE_CurDrive
)
1237 UNICODE_STRING dirW
;
1239 RtlInitUnicodeString( &dirW
, full_name
.short_name
);
1240 RtlSetCurrentDirectory_U( &dirW
);
1243 if (pTask
&& (pTask
->curdrive
& 0x80) &&
1244 ((pTask
->curdrive
& ~0x80) == drive
))
1246 WideCharToMultiByte(CP_ACP
, 0, full_name
.short_name
+ 2, -1,
1247 pTask
->curdir
, sizeof(pTask
->curdir
), NULL
, NULL
);
1248 DRIVE_LastTask
= GetCurrentTask();
1254 /***********************************************************************
1257 int DRIVE_Disable( int drive
)
1259 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
1261 SetLastError( ERROR_INVALID_DRIVE
);
1264 DOSDrives
[drive
].flags
|= DRIVE_DISABLED
;
1269 /***********************************************************************
1272 int DRIVE_Enable( int drive
)
1274 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
1276 SetLastError( ERROR_INVALID_DRIVE
);
1279 DOSDrives
[drive
].flags
&= ~DRIVE_DISABLED
;
1284 /***********************************************************************
1285 * DefineDosDeviceA (KERNEL32.@)
1287 BOOL WINAPI
DefineDosDeviceA(DWORD flags
,LPCSTR devname
,LPCSTR targetpath
)
1289 UNICODE_STRING d
, t
;
1292 if (!RtlCreateUnicodeStringFromAsciiz(&d
, devname
))
1294 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1297 if (!RtlCreateUnicodeStringFromAsciiz(&t
, targetpath
))
1299 RtlFreeUnicodeString(&d
);
1300 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1303 ret
= DefineDosDeviceW(flags
, d
.Buffer
, t
.Buffer
);
1304 RtlFreeUnicodeString(&d
);
1305 RtlFreeUnicodeString(&t
);
1310 /***********************************************************************
1311 * DefineDosDeviceA (KERNEL32.@)
1313 BOOL WINAPI
DefineDosDeviceW(DWORD flags
,LPCWSTR devname
,LPCWSTR targetpath
)
1315 DOSDRIVE
*old
, *new;
1317 /* this is a temporary hack for int21 support. better implementation has to be done */
1318 if (flags
!= DDD_RAW_TARGET_PATH
||
1319 !(toupperW(devname
[0]) >= 'A' && toupperW(devname
[0]) <= 'Z') ||
1320 devname
[1] != ':' || devname
[2] != 0 ||
1321 !(toupperW(targetpath
[0]) >= 'A' && toupperW(targetpath
[0]) <= 'Z') ||
1322 targetpath
[1] != ':' || targetpath
[2] != '\\' || targetpath
[3] != 0)
1324 FIXME("(0x%08lx,%s,%s),stub!\n", flags
, debugstr_w(devname
), debugstr_w(targetpath
));
1325 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
1329 old
= DOSDrives
+ devname
[0] - 'A';
1330 new = DOSDrives
+ targetpath
[0] - 'A';
1334 SetLastError( ERROR_INVALID_DRIVE
);
1340 TRACE("Can't map drive %c: to already existing drive %c:\n",
1341 devname
[0], targetpath
[0] );
1342 /* it is already mapped there, so return success */
1343 if (!strcmp(old
->root
,new->root
))
1348 new->root
= heap_strdup( old
->root
);
1349 new->dos_cwd
= HeapAlloc(GetProcessHeap(), 0, (strlenW(old
->dos_cwd
) + 1) * sizeof(WCHAR
));
1350 strcpyW(new->dos_cwd
, old
->dos_cwd
);
1351 new->unix_cwd
= heap_strdup( old
->unix_cwd
);
1352 new->device
= heap_strdup( old
->device
);
1353 memcpy ( new->label_conf
, old
->label_conf
, 12 );
1354 memcpy ( new->label_read
, old
->label_read
, 12 );
1355 new->serial_conf
= old
->serial_conf
;
1356 new->type
= old
->type
;
1357 new->flags
= old
->flags
;
1358 new->dev
= old
->dev
;
1359 new->ino
= old
->ino
;
1361 TRACE("Drive %c: is now equal to drive %c:\n",
1362 targetpath
[0], devname
[0] );
1368 /***********************************************************************
1371 * Open the drive raw device and return a Unix fd (or -1 on error).
1373 int DRIVE_OpenDevice( int drive
, int flags
)
1375 if (!DRIVE_IsValid( drive
)) return -1;
1376 return open( DOSDrives
[drive
].device
, flags
);
1380 /***********************************************************************
1381 * DRIVE_GetFreeSpace
1383 static int DRIVE_GetFreeSpace( int drive
, PULARGE_INTEGER size
,
1384 PULARGE_INTEGER available
)
1388 if (!DRIVE_IsValid(drive
))
1390 SetLastError( ERROR_PATH_NOT_FOUND
);
1394 /* FIXME: add autoconf check for this */
1395 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1396 if (statfs( DOSDrives
[drive
].root
, &info
, 0, 0) < 0)
1398 if (statfs( DOSDrives
[drive
].root
, &info
) < 0)
1402 WARN("cannot do statfs(%s)\n", DOSDrives
[drive
].root
);
1406 size
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bsize
, info
.f_blocks
);
1407 #ifdef HAVE_STRUCT_STATFS_F_BAVAIL
1408 available
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bavail
, info
.f_bsize
);
1410 # ifdef HAVE_STRUCT_STATFS_F_BFREE
1411 available
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bfree
, info
.f_bsize
);
1413 # error "statfs has no bfree/bavail member!"
1416 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
1417 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1418 available
->QuadPart
= 0;
1423 /***********************************************************************
1424 * DRIVE_GetCurrentDirectory
1425 * Returns "X:\\path\\etc\\".
1427 * Despite the API description, return required length including the
1428 * terminating null when buffer too small. This is the real behaviour.
1430 static UINT
DRIVE_GetCurrentDirectory( UINT buflen
, LPWSTR buf
)
1433 LPCWSTR dos_cwd
= DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1434 static const WCHAR driveA_rootW
[] = {'A',':','\\',0};
1436 ret
= strlenW(dos_cwd
) + 3; /* length of WHOLE current directory */
1437 if (ret
>= buflen
) return ret
+ 1;
1439 strcpyW( buf
, driveA_rootW
);
1440 buf
[0] += DRIVE_GetCurrentDrive();
1441 strcatW( buf
, dos_cwd
);
1446 /***********************************************************************
1449 * Build the environment array containing the drives' current directories.
1450 * Resulting pointer must be freed with HeapFree.
1452 WCHAR
*DRIVE_BuildEnv(void)
1455 LPCWSTR cwd
[MAX_DOS_DRIVES
];
1458 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++)
1460 if ((cwd
[i
] = DRIVE_GetDosCwd(i
)) && cwd
[i
][0])
1461 length
+= strlenW(cwd
[i
]) + 8;
1463 if (!(env
= HeapAlloc( GetProcessHeap(), 0, (length
+1) * sizeof(WCHAR
) ))) return NULL
;
1464 for (i
= 0, p
= env
; i
< MAX_DOS_DRIVES
; i
++)
1466 if (cwd
[i
] && cwd
[i
][0])
1468 *p
++ = '='; *p
++ = 'A' + i
; *p
++ = ':';
1469 *p
++ = '='; *p
++ = 'A' + i
; *p
++ = ':'; *p
++ = '\\';
1470 strcpyW( p
, cwd
[i
] );
1471 p
+= strlenW(p
) + 1;
1479 /***********************************************************************
1480 * GetDiskFreeSpace (KERNEL.422)
1482 BOOL16 WINAPI
GetDiskFreeSpace16( LPCSTR root
, LPDWORD cluster_sectors
,
1483 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1484 LPDWORD total_clusters
)
1486 return GetDiskFreeSpaceA( root
, cluster_sectors
, sector_bytes
,
1487 free_clusters
, total_clusters
);
1491 /***********************************************************************
1492 * GetDiskFreeSpaceW (KERNEL32.@)
1494 * Fails if expression resulting from current drive's dir and "root"
1495 * is not a root dir of the target drive.
1497 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1498 * if the corresponding info is unneeded.
1500 * FIXME: needs to support UNC names from Win95 OSR2 on.
1502 * Behaviour under Win95a:
1503 * CurrDir root result
1504 * "E:\\TEST" "E:" FALSE
1508 * "E:\\TEST" "\\" TRUE
1509 * "E:\\TEST" ":\\" FALSE
1510 * "E:\\TEST" "E:\\" TRUE
1511 * "E:\\TEST" "" FALSE
1512 * "E:\\" "" FALSE (!)
1514 * "E:\\TEST" 0x0 TRUE (!)
1515 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1516 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1518 BOOL WINAPI
GetDiskFreeSpaceW( LPCWSTR root
, LPDWORD cluster_sectors
,
1519 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1520 LPDWORD total_clusters
)
1522 int drive
, sec_size
;
1523 ULARGE_INTEGER size
,available
;
1527 TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root
), cluster_sectors
, sector_bytes
,
1528 free_clusters
, total_clusters
);
1530 if (!root
|| root
[0] == '\\' || root
[0] == '/')
1531 drive
= DRIVE_GetCurrentDrive();
1533 if (root
[0] && root
[1] == ':') /* root contains drive tag */
1535 drive
= toupperW(root
[0]) - 'A';
1537 if (path
[0] == '\0')
1539 path
= DRIVE_GetDosCwd(drive
);
1542 SetLastError(ERROR_PATH_NOT_FOUND
);
1547 if (path
[0] == '\\')
1550 if (path
[0]) /* oops, we are in a subdir */
1552 SetLastError(ERROR_INVALID_NAME
);
1559 SetLastError(ERROR_PATH_NOT_FOUND
);
1561 SetLastError(ERROR_INVALID_NAME
);
1565 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1567 /* Cap the size and available at 2GB as per specs. */
1568 if ((size
.s
.HighPart
) ||(size
.s
.LowPart
> 0x7fffffff))
1570 size
.s
.HighPart
= 0;
1571 size
.s
.LowPart
= 0x7fffffff;
1573 if ((available
.s
.HighPart
) ||(available
.s
.LowPart
> 0x7fffffff))
1575 available
.s
.HighPart
=0;
1576 available
.s
.LowPart
= 0x7fffffff;
1578 sec_size
= (DRIVE_GetType(drive
)==DRIVE_CDROM
) ? 2048 : 512;
1579 size
.s
.LowPart
/= sec_size
;
1580 available
.s
.LowPart
/= sec_size
;
1581 /* FIXME: probably have to adjust those variables too for CDFS */
1583 while (cluster_sec
* 65536 < size
.s
.LowPart
) cluster_sec
*= 2;
1585 if (cluster_sectors
)
1586 *cluster_sectors
= cluster_sec
;
1588 *sector_bytes
= sec_size
;
1590 *free_clusters
= available
.s
.LowPart
/ cluster_sec
;
1592 *total_clusters
= size
.s
.LowPart
/ cluster_sec
;
1597 /***********************************************************************
1598 * GetDiskFreeSpaceA (KERNEL32.@)
1600 BOOL WINAPI
GetDiskFreeSpaceA( LPCSTR root
, LPDWORD cluster_sectors
,
1601 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1602 LPDWORD total_clusters
)
1604 UNICODE_STRING rootW
;
1609 if(!RtlCreateUnicodeStringFromAsciiz(&rootW
, root
))
1611 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1616 rootW
.Buffer
= NULL
;
1618 ret
= GetDiskFreeSpaceW(rootW
.Buffer
, cluster_sectors
, sector_bytes
,
1619 free_clusters
, total_clusters
);
1620 RtlFreeUnicodeString(&rootW
);
1626 /***********************************************************************
1627 * GetDiskFreeSpaceExW (KERNEL32.@)
1629 * This function is used to acquire the size of the available and
1630 * total space on a logical volume.
1634 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1635 * detailed error information.
1638 BOOL WINAPI
GetDiskFreeSpaceExW( LPCWSTR root
,
1639 PULARGE_INTEGER avail
,
1640 PULARGE_INTEGER total
,
1641 PULARGE_INTEGER totalfree
)
1644 ULARGE_INTEGER size
,available
;
1646 if (!root
) drive
= DRIVE_GetCurrentDrive();
1648 { /* C: always works for GetDiskFreeSpaceEx */
1649 if ((root
[1]) && ((root
[1] != ':') || (root
[2] && root
[2] != '\\')))
1651 FIXME("there are valid root names which are not supported yet\n");
1652 /* ..like UNC names, for instance. */
1654 WARN("invalid root '%s'\n", debugstr_w(root
));
1657 drive
= toupperW(root
[0]) - 'A';
1660 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1664 total
->s
.HighPart
= size
.s
.HighPart
;
1665 total
->s
.LowPart
= size
.s
.LowPart
;
1670 totalfree
->s
.HighPart
= available
.s
.HighPart
;
1671 totalfree
->s
.LowPart
= available
.s
.LowPart
;
1676 if (FIXME_ON(dosfs
))
1678 /* On Windows2000, we need to check the disk quota
1679 allocated for the user owning the calling process. We
1680 don't want to be more obtrusive than necessary with the
1681 FIXME messages, so don't print the FIXME unless Wine is
1682 actually masquerading as Windows2000. */
1684 RTL_OSVERSIONINFOEXW ovi
;
1685 ovi
.dwOSVersionInfoSize
= sizeof(ovi
);
1686 if (RtlGetVersion(&ovi
))
1688 if (ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
&& ovi
.dwMajorVersion
> 4)
1689 FIXME("no per-user quota support yet\n");
1693 /* Quick hack, should eventually be fixed to work 100% with
1694 Windows2000 (see comment above). */
1695 avail
->s
.HighPart
= available
.s
.HighPart
;
1696 avail
->s
.LowPart
= available
.s
.LowPart
;
1702 /***********************************************************************
1703 * GetDiskFreeSpaceExA (KERNEL32.@)
1705 BOOL WINAPI
GetDiskFreeSpaceExA( LPCSTR root
, PULARGE_INTEGER avail
,
1706 PULARGE_INTEGER total
,
1707 PULARGE_INTEGER totalfree
)
1709 UNICODE_STRING rootW
;
1712 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
1713 else rootW
.Buffer
= NULL
;
1715 ret
= GetDiskFreeSpaceExW( rootW
.Buffer
, avail
, total
, totalfree
);
1717 RtlFreeUnicodeString(&rootW
);
1721 /***********************************************************************
1722 * GetDriveType (KERNEL.136)
1723 * This function returns the type of a drive in Win16.
1724 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1725 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1726 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1727 * do any pseudo-clever changes.
1730 * drivetype DRIVE_xxx
1732 UINT16 WINAPI
GetDriveType16( UINT16 drive
) /* [in] number (NOT letter) of drive */
1734 UINT type
= DRIVE_GetType(drive
);
1735 TRACE("(%c:)\n", 'A' + drive
);
1736 if (type
== DRIVE_CDROM
) type
= DRIVE_REMOTE
;
1741 /***********************************************************************
1742 * GetDriveTypeW (KERNEL32.@)
1744 * Returns the type of the disk drive specified. If root is NULL the
1745 * root of the current directory is used.
1749 * Type of drive (from Win32 SDK):
1751 * DRIVE_UNKNOWN unable to find out anything about the drive
1752 * DRIVE_NO_ROOT_DIR nonexistent root dir
1753 * DRIVE_REMOVABLE the disk can be removed from the machine
1754 * DRIVE_FIXED the disk can not be removed from the machine
1755 * DRIVE_REMOTE network disk
1756 * DRIVE_CDROM CDROM drive
1757 * DRIVE_RAMDISK virtual disk in RAM
1759 UINT WINAPI
GetDriveTypeW(LPCWSTR root
) /* [in] String describing drive */
1762 TRACE("(%s)\n", debugstr_w(root
));
1764 if (NULL
== root
) drive
= DRIVE_GetCurrentDrive();
1767 if ((root
[1]) && (root
[1] != ':'))
1769 WARN("invalid root %s\n", debugstr_w(root
));
1770 return DRIVE_NO_ROOT_DIR
;
1772 drive
= toupperW(root
[0]) - 'A';
1774 return DRIVE_GetType(drive
);
1778 /***********************************************************************
1779 * GetDriveTypeA (KERNEL32.@)
1781 UINT WINAPI
GetDriveTypeA( LPCSTR root
)
1783 UNICODE_STRING rootW
;
1788 if( !RtlCreateUnicodeStringFromAsciiz(&rootW
, root
))
1790 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1795 rootW
.Buffer
= NULL
;
1797 ret
= GetDriveTypeW(rootW
.Buffer
);
1799 RtlFreeUnicodeString(&rootW
);
1805 /***********************************************************************
1806 * GetCurrentDirectory (KERNEL.411)
1808 UINT16 WINAPI
GetCurrentDirectory16( UINT16 buflen
, LPSTR buf
)
1810 WCHAR cur_dirW
[MAX_PATH
];
1812 DRIVE_GetCurrentDirectory(MAX_PATH
, cur_dirW
);
1813 return (UINT16
)WideCharToMultiByte(CP_ACP
, 0, cur_dirW
, -1, buf
, buflen
, NULL
, NULL
);
1817 /***********************************************************************
1818 * GetCurrentDirectoryW (KERNEL32.@)
1820 UINT WINAPI
GetCurrentDirectoryW( UINT buflen
, LPWSTR buf
)
1823 WCHAR longname
[MAX_PATHNAME_LEN
];
1824 WCHAR shortname
[MAX_PATHNAME_LEN
];
1826 ret
= DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN
, shortname
);
1827 if ( ret
> MAX_PATHNAME_LEN
) {
1828 ERR_(file
)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret
);
1831 GetLongPathNameW(shortname
, longname
, MAX_PATHNAME_LEN
);
1832 ret
= strlenW( longname
) + 1;
1833 if (ret
> buflen
) return ret
;
1834 strcpyW(buf
, longname
);
1838 /***********************************************************************
1839 * GetCurrentDirectoryA (KERNEL32.@)
1841 UINT WINAPI
GetCurrentDirectoryA( UINT buflen
, LPSTR buf
)
1843 WCHAR bufferW
[MAX_PATH
];
1846 retW
= GetCurrentDirectoryW(MAX_PATH
, bufferW
);
1850 else if (retW
> MAX_PATH
)
1852 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1857 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, NULL
, 0, NULL
, NULL
);
1860 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buf
, buflen
, NULL
, NULL
);
1861 ret
--; /* length without 0 */
1868 /***********************************************************************
1869 * SetCurrentDirectory (KERNEL.412)
1871 BOOL16 WINAPI
SetCurrentDirectory16( LPCSTR dir
)
1873 return SetCurrentDirectoryA( dir
);
1877 /***********************************************************************
1878 * SetCurrentDirectoryW (KERNEL32.@)
1880 BOOL WINAPI
SetCurrentDirectoryW( LPCWSTR dir
)
1882 int drive
, olddrive
= DRIVE_GetCurrentDrive();
1886 SetLastError(ERROR_INVALID_PARAMETER
);
1889 if (dir
[0] && (dir
[1]==':'))
1891 drive
= toupperW( *dir
) - 'A';
1897 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1898 sets pTask->curdir only if pTask->curdrive is drive */
1899 if (!(DRIVE_SetCurrentDrive( drive
)))
1902 /* FIXME: what about empty strings? Add a \\ ? */
1903 if (!DRIVE_Chdir( drive
, dir
)) {
1904 DRIVE_SetCurrentDrive(olddrive
);
1911 /***********************************************************************
1912 * SetCurrentDirectoryA (KERNEL32.@)
1914 BOOL WINAPI
SetCurrentDirectoryA( LPCSTR dir
)
1916 UNICODE_STRING dirW
;
1921 SetLastError(ERROR_INVALID_PARAMETER
);
1925 if (RtlCreateUnicodeStringFromAsciiz(&dirW
, dir
))
1927 ret
= SetCurrentDirectoryW(dirW
.Buffer
);
1928 RtlFreeUnicodeString(&dirW
);
1931 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1936 /***********************************************************************
1937 * GetLogicalDriveStringsA (KERNEL32.@)
1939 UINT WINAPI
GetLogicalDriveStringsA( UINT len
, LPSTR buffer
)
1943 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1944 if (DRIVE_IsValid(drive
)) count
++;
1945 if ((count
* 4) + 1 <= len
)
1948 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1949 if (DRIVE_IsValid(drive
))
1960 return (count
* 4) + 1; /* account for terminating null */
1961 /* The API tells about these different return values */
1965 /***********************************************************************
1966 * GetLogicalDriveStringsW (KERNEL32.@)
1968 UINT WINAPI
GetLogicalDriveStringsW( UINT len
, LPWSTR buffer
)
1972 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1973 if (DRIVE_IsValid(drive
)) count
++;
1974 if (count
* 4 * sizeof(WCHAR
) <= len
)
1977 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1978 if (DRIVE_IsValid(drive
))
1980 *p
++ = (WCHAR
)('a' + drive
);
1987 return count
* 4 * sizeof(WCHAR
);
1991 /***********************************************************************
1992 * GetLogicalDrives (KERNEL32.@)
1994 DWORD WINAPI
GetLogicalDrives(void)
1999 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
2001 if ( (DRIVE_IsValid(drive
)) ||
2002 (DOSDrives
[drive
].type
== DRIVE_CDROM
)) /* audio CD is also valid */
2003 ret
|= (1 << drive
);
2009 /***********************************************************************
2010 * GetVolumeInformationW (KERNEL32.@)
2012 BOOL WINAPI
GetVolumeInformationW( LPCWSTR root
, LPWSTR label
,
2013 DWORD label_len
, DWORD
*serial
,
2014 DWORD
*filename_len
, DWORD
*flags
,
2015 LPWSTR fsname
, DWORD fsname_len
)
2020 /* FIXME, SetLastError()s missing */
2022 if (!root
) drive
= DRIVE_GetCurrentDrive();
2025 if (root
[0] && root
[1] != ':')
2027 WARN("invalid root %s\n", debugstr_w(root
));
2030 drive
= toupperW(root
[0]) - 'A';
2032 if (!DRIVE_IsValid( drive
)) return FALSE
;
2033 if (label
&& label_len
)
2035 strncpyW( label
, DRIVE_GetLabel(drive
), label_len
);
2036 label
[label_len
- 1] = 0; /* ensure 0 termination */
2037 cp
= label
+ strlenW(label
);
2038 while (cp
!= label
&& *(cp
-1) == ' ') cp
--;
2041 if (serial
) *serial
= DRIVE_GetSerialNumber(drive
);
2043 /* Set the filesystem information */
2044 /* Note: we only emulate a FAT fs at present */
2047 if (DOSDrives
[drive
].flags
& DRIVE_SHORT_NAMES
)
2050 *filename_len
= 255;
2055 if (DOSDrives
[drive
].flags
& DRIVE_CASE_SENSITIVE
)
2056 *flags
|=FS_CASE_SENSITIVE
;
2057 if (DOSDrives
[drive
].flags
& DRIVE_CASE_PRESERVING
)
2058 *flags
|=FS_CASE_IS_PRESERVED
;
2060 if (fsname
&& fsname_len
)
2062 /* Diablo checks that return code ... */
2063 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
2065 static const WCHAR cdfsW
[] = {'C','D','F','S',0};
2066 strncpyW( fsname
, cdfsW
, fsname_len
);
2070 static const WCHAR fatW
[] = {'F','A','T',0};
2071 strncpyW( fsname
, fatW
, fsname_len
);
2073 fsname
[fsname_len
- 1] = 0; /* ensure 0 termination */
2079 /***********************************************************************
2080 * GetVolumeInformationA (KERNEL32.@)
2082 BOOL WINAPI
GetVolumeInformationA( LPCSTR root
, LPSTR label
,
2083 DWORD label_len
, DWORD
*serial
,
2084 DWORD
*filename_len
, DWORD
*flags
,
2085 LPSTR fsname
, DWORD fsname_len
)
2087 UNICODE_STRING rootW
;
2088 LPWSTR labelW
, fsnameW
;
2091 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
2092 else rootW
.Buffer
= NULL
;
2093 labelW
= label
? HeapAlloc(GetProcessHeap(), 0, label_len
* sizeof(WCHAR
)) : NULL
;
2094 fsnameW
= fsname
? HeapAlloc(GetProcessHeap(), 0, fsname_len
* sizeof(WCHAR
)) : NULL
;
2096 if ((ret
= GetVolumeInformationW(rootW
.Buffer
, labelW
, label_len
, serial
,
2097 filename_len
, flags
, fsnameW
, fsname_len
)))
2099 if (label
) WideCharToMultiByte(CP_ACP
, 0, labelW
, -1, label
, label_len
, NULL
, NULL
);
2100 if (fsname
) WideCharToMultiByte(CP_ACP
, 0, fsnameW
, -1, fsname
, fsname_len
, NULL
, NULL
);
2103 RtlFreeUnicodeString(&rootW
);
2104 if (labelW
) HeapFree( GetProcessHeap(), 0, labelW
);
2105 if (fsnameW
) HeapFree( GetProcessHeap(), 0, fsnameW
);
2109 /***********************************************************************
2110 * SetVolumeLabelW (KERNEL32.@)
2112 BOOL WINAPI
SetVolumeLabelW( LPCWSTR root
, LPCWSTR volname
)
2116 /* FIXME, SetLastErrors missing */
2118 if (!root
) drive
= DRIVE_GetCurrentDrive();
2121 if ((root
[1]) && (root
[1] != ':'))
2123 WARN("invalid root %s\n", debugstr_w(root
));
2126 drive
= toupperW(root
[0]) - 'A';
2128 if (!DRIVE_IsValid( drive
)) return FALSE
;
2130 /* some copy protection stuff check this */
2131 if (DOSDrives
[drive
].type
== DRIVE_CDROM
) return FALSE
;
2133 strncpyW(DOSDrives
[drive
].label_conf
, volname
, 12);
2134 DOSDrives
[drive
].label_conf
[12 - 1] = 0; /* ensure 0 termination */
2138 /***********************************************************************
2139 * SetVolumeLabelA (KERNEL32.@)
2141 BOOL WINAPI
SetVolumeLabelA(LPCSTR root
, LPCSTR volname
)
2143 UNICODE_STRING rootW
, volnameW
;
2146 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
2147 else rootW
.Buffer
= NULL
;
2148 if (volname
) RtlCreateUnicodeStringFromAsciiz(&volnameW
, volname
);
2149 else volnameW
.Buffer
= NULL
;
2151 ret
= SetVolumeLabelW( rootW
.Buffer
, volnameW
.Buffer
);
2153 RtlFreeUnicodeString(&rootW
);
2154 RtlFreeUnicodeString(&volnameW
);
2158 /***********************************************************************
2159 * GetVolumeNameForVolumeMountPointW (KERNEL32.@)
2161 BOOL WINAPI
GetVolumeNameForVolumeMountPointW(LPCWSTR str
, LPWSTR dst
, DWORD size
)
2163 FIXME("(%s, %p, %lx): stub\n", debugstr_w(str
), dst
, size
);