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