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 */
74 #include "wine/unicode.h"
75 #include "wine/library.h"
76 #include "wine/server.h"
77 #include "wine/debug.h"
79 WINE_DEFAULT_DEBUG_CHANNEL(dosfs
);
80 WINE_DECLARE_DEBUG_CHANNEL(file
);
84 char *root
; /* root dir in Unix format without trailing / */
85 LPWSTR dos_cwd
; /* cwd in DOS format without leading or trailing \ */
86 char *unix_cwd
; /* cwd in Unix format without leading or trailing / */
87 char *device
; /* raw device path */
88 WCHAR label_conf
[12]; /* drive label as cfg'd in wine config */
89 WCHAR label_read
[12]; /* drive label as read from device */
90 DWORD serial_conf
; /* drive serial number as cfg'd in wine config */
91 UINT type
; /* drive type */
92 UINT flags
; /* drive flags */
93 dev_t dev
; /* unix device number */
94 ino_t ino
; /* unix inode number */
98 static const WCHAR DRIVE_Types
[][8] =
100 { 0 }, /* DRIVE_UNKNOWN */
101 { 0 }, /* DRIVE_NO_ROOT_DIR */
102 {'f','l','o','p','p','y',0}, /* DRIVE_REMOVABLE */
103 {'h','d',0}, /* DRIVE_FIXED */
104 {'n','e','t','w','o','r','k',0}, /* DRIVE_REMOTE */
105 {'c','d','r','o','m',0}, /* DRIVE_CDROM */
106 {'r','a','m','d','i','s','k',0} /* DRIVE_RAMDISK */
110 /* Known filesystem types */
118 static const FS_DESCR DRIVE_Filesystems
[] =
120 { {'u','n','i','x',0}, DRIVE_CASE_SENSITIVE
| DRIVE_CASE_PRESERVING
},
121 { {'m','s','d','o','s',0}, DRIVE_SHORT_NAMES
},
122 { {'d','o','s',0}, DRIVE_SHORT_NAMES
},
123 { {'f','a','t',0}, DRIVE_SHORT_NAMES
},
124 { {'v','f','a','t',0}, DRIVE_CASE_PRESERVING
},
125 { {'w','i','n','9','5',0}, DRIVE_CASE_PRESERVING
},
130 static DOSDRIVE DOSDrives
[MAX_DOS_DRIVES
];
131 static int DRIVE_CurDrive
= -1;
133 static HTASK16 DRIVE_LastTask
= 0;
135 /* strdup on the process heap */
136 inline static char *heap_strdup( const char *str
)
138 INT len
= strlen(str
) + 1;
139 LPSTR p
= HeapAlloc( GetProcessHeap(), 0, len
);
140 if (p
) memcpy( p
, str
, len
);
144 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
146 extern void CDROM_InitRegistry(int dev
, int id
, const char *device
);
148 /***********************************************************************
151 static inline UINT
DRIVE_GetDriveType( INT drive
, LPCWSTR value
)
155 for (i
= 0; i
< sizeof(DRIVE_Types
)/sizeof(DRIVE_Types
[0]); i
++)
157 if (!strcmpiW( value
, DRIVE_Types
[i
] )) return i
;
159 MESSAGE("Drive %c: unknown drive type %s, defaulting to 'hd'.\n",
160 'A' + drive
, debugstr_w(value
) );
165 /***********************************************************************
168 static UINT
DRIVE_GetFSFlags( INT drive
, LPCWSTR value
)
170 const FS_DESCR
*descr
;
172 for (descr
= DRIVE_Filesystems
; *descr
->name
; descr
++)
173 if (!strcmpiW( value
, descr
->name
)) return descr
->flags
;
174 MESSAGE("Drive %c: unknown filesystem type %s, defaulting to 'win95'.\n",
175 'A' + drive
, debugstr_w(value
) );
176 return DRIVE_CASE_PRESERVING
;
180 /***********************************************************************
185 int i
, len
, count
= 0;
186 WCHAR driveW
[] = {'M','a','c','h','i','n','e','\\','S','o','f','t','w','a','r','e','\\',
187 'W','i','n','e','\\','W','i','n','e','\\',
188 'C','o','n','f','i','g','\\','D','r','i','v','e',' ','A',0};
189 WCHAR drive_env
[] = {'=','A',':',0};
190 WCHAR path
[MAX_PATHNAME_LEN
];
191 char tmp
[MAX_PATHNAME_LEN
*sizeof(WCHAR
) + sizeof(KEY_VALUE_PARTIAL_INFORMATION
)];
192 struct stat drive_stat_buffer
;
197 OBJECT_ATTRIBUTES attr
;
198 UNICODE_STRING nameW
;
200 static const WCHAR PathW
[] = {'P','a','t','h',0};
201 static const WCHAR LabelW
[] = {'L','a','b','e','l',0};
202 static const WCHAR SerialW
[] = {'S','e','r','i','a','l',0};
203 static const WCHAR TypeW
[] = {'T','y','p','e',0};
204 static const WCHAR FilesystemW
[] = {'F','i','l','e','s','y','s','t','e','m',0};
205 static const WCHAR DeviceW
[] = {'D','e','v','i','c','e',0};
206 static const WCHAR ReadVolInfoW
[] = {'R','e','a','d','V','o','l','I','n','f','o',0};
207 static const WCHAR FailReadOnlyW
[] = {'F','a','i','l','R','e','a','d','O','n','l','y',0};
208 static const WCHAR driveC_labelW
[] = {'D','r','i','v','e',' ','C',' ',' ',' ',' ',0};
211 attr
.Length
= sizeof(attr
);
212 attr
.RootDirectory
= 0;
213 attr
.ObjectName
= &nameW
;
215 attr
.SecurityDescriptor
= NULL
;
216 attr
.SecurityQualityOfService
= NULL
;
218 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
220 RtlInitUnicodeString( &nameW
, driveW
);
221 nameW
.Buffer
[(nameW
.Length
/ sizeof(WCHAR
)) - 1] = 'A' + i
;
222 if (NtOpenKey( &hkey
, KEY_ALL_ACCESS
, &attr
) != STATUS_SUCCESS
) continue;
224 /* Get the root path */
225 RtlInitUnicodeString( &nameW
, PathW
);
226 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
228 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
229 ExpandEnvironmentStringsW( data
, path
, sizeof(path
)/sizeof(WCHAR
) );
231 p
= path
+ strlenW(path
) - 1;
232 while ((p
> path
) && (*p
== '/')) *p
-- = '\0';
236 len
= WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1, NULL
, 0, NULL
, NULL
);
237 drive
->root
= HeapAlloc(GetProcessHeap(), 0, len
);
238 WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1, drive
->root
, len
, NULL
, NULL
);
242 /* relative paths are relative to config dir */
243 const char *config
= wine_get_config_dir();
244 len
= strlen(config
);
245 len
+= WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1, NULL
, 0, NULL
, NULL
) + 2;
246 drive
->root
= HeapAlloc( GetProcessHeap(), 0, len
);
247 len
-= sprintf( drive
->root
, "%s/", config
);
248 WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1,
249 drive
->root
+ strlen(drive
->root
), len
, NULL
, NULL
);
252 if (stat( drive
->root
, &drive_stat_buffer
))
254 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
255 drive
->root
, strerror(errno
), 'A' + i
);
256 HeapFree( GetProcessHeap(), 0, drive
->root
);
260 if (!S_ISDIR(drive_stat_buffer
.st_mode
))
262 MESSAGE("%s is not a directory, ignoring drive %c:\n",
263 drive
->root
, 'A' + i
);
264 HeapFree( GetProcessHeap(), 0, drive
->root
);
269 drive
->dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(drive
->dos_cwd
[0]));
270 drive
->unix_cwd
= heap_strdup( "" );
271 drive
->device
= NULL
;
273 drive
->dev
= drive_stat_buffer
.st_dev
;
274 drive
->ino
= drive_stat_buffer
.st_ino
;
276 /* Get the drive type */
277 RtlInitUnicodeString( &nameW
, TypeW
);
278 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
280 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
281 drive
->type
= DRIVE_GetDriveType( i
, data
);
283 else drive
->type
= DRIVE_FIXED
;
285 /* Get the drive label */
286 RtlInitUnicodeString( &nameW
, LabelW
);
287 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
289 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
290 lstrcpynW( drive
->label_conf
, data
, 12 );
292 if ((len
= strlenW(drive
->label_conf
)) < 11)
294 /* Pad label with spaces */
295 while(len
< 11) drive
->label_conf
[len
++] = ' ';
296 drive
->label_conf
[11] = '\0';
299 /* Get the serial number */
300 RtlInitUnicodeString( &nameW
, SerialW
);
301 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
303 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
304 drive
->serial_conf
= strtoulW( data
, NULL
, 16 );
306 else drive
->serial_conf
= 12345678;
308 /* Get the filesystem type */
309 RtlInitUnicodeString( &nameW
, FilesystemW
);
310 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
312 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
313 drive
->flags
= DRIVE_GetFSFlags( i
, data
);
315 else drive
->flags
= DRIVE_CASE_PRESERVING
;
318 RtlInitUnicodeString( &nameW
, DeviceW
);
319 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
321 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
322 len
= WideCharToMultiByte(CP_UNIXCP
, 0, data
, -1, NULL
, 0, NULL
, NULL
);
323 drive
->device
= HeapAlloc(GetProcessHeap(), 0, len
);
324 WideCharToMultiByte(CP_UNIXCP
, 0, data
, -1, drive
->device
, len
, NULL
, NULL
);
326 RtlInitUnicodeString( &nameW
, ReadVolInfoW
);
327 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
329 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
330 if (IS_OPTION_TRUE(data
[0])) drive
->flags
|= DRIVE_READ_VOL_INFO
;
332 else drive
->flags
|= DRIVE_READ_VOL_INFO
;
334 if (drive
->type
== DRIVE_CDROM
)
337 if ((cd_fd
= open(drive
->device
, O_RDONLY
|O_NONBLOCK
)) != -1)
339 CDROM_InitRegistry(cd_fd
, i
, drive
->device
);
345 /* Get the FailReadOnly flag */
346 RtlInitUnicodeString( &nameW
, FailReadOnlyW
);
347 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
349 WCHAR
*data
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
350 if (IS_OPTION_TRUE(data
[0])) drive
->flags
|= DRIVE_FAIL_READ_ONLY
;
353 /* Make the first hard disk the current drive */
354 if ((DRIVE_CurDrive
== -1) && (drive
->type
== DRIVE_FIXED
))
358 TRACE("Drive %c: path=%s type=%s label=%s serial=%08lx "
359 "flags=%08x dev=%x ino=%x\n",
360 'A' + i
, drive
->root
, debugstr_w(DRIVE_Types
[drive
->type
]),
361 debugstr_w(drive
->label_conf
), drive
->serial_conf
, drive
->flags
,
362 (int)drive
->dev
, (int)drive
->ino
);
371 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
372 /* Create a C drive pointing to Unix root dir */
373 DOSDrives
[2].root
= heap_strdup( "/" );
374 DOSDrives
[2].dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(DOSDrives
[2].dos_cwd
[0]));
375 DOSDrives
[2].unix_cwd
= heap_strdup( "" );
376 strcpyW( DOSDrives
[2].label_conf
, driveC_labelW
);
377 DOSDrives
[2].serial_conf
= 12345678;
378 DOSDrives
[2].type
= DRIVE_FIXED
;
379 DOSDrives
[2].device
= NULL
;
380 DOSDrives
[2].flags
= 0;
384 /* Make sure the current drive is valid */
385 if (DRIVE_CurDrive
== -1)
387 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
389 if (drive
->root
&& !(drive
->flags
& DRIVE_DISABLED
))
397 /* get current working directory info for all drives */
398 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++, drive_env
[1]++)
400 if (!GetEnvironmentVariableW(drive_env
, path
, MAX_PATHNAME_LEN
)) continue;
402 if (toupperW(path
[0]) != drive_env
[1] || path
[1] != ':') continue;
403 DRIVE_Chdir( i
, path
+ 2 );
409 /***********************************************************************
412 int DRIVE_IsValid( int drive
)
414 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
415 return (DOSDrives
[drive
].root
&&
416 !(DOSDrives
[drive
].flags
& DRIVE_DISABLED
));
420 /***********************************************************************
421 * DRIVE_GetCurrentDrive
423 int DRIVE_GetCurrentDrive(void)
425 TDB
*pTask
= GlobalLock16(GetCurrentTask());
426 if (pTask
&& (pTask
->curdrive
& 0x80)) return pTask
->curdrive
& ~0x80;
427 return DRIVE_CurDrive
;
431 /***********************************************************************
432 * DRIVE_SetCurrentDrive
434 int DRIVE_SetCurrentDrive( int drive
)
436 TDB
*pTask
= GlobalLock16(GetCurrentTask());
437 if (!DRIVE_IsValid( drive
))
439 SetLastError( ERROR_INVALID_DRIVE
);
442 TRACE("%c:\n", 'A' + drive
);
443 DRIVE_CurDrive
= drive
;
444 if (pTask
) pTask
->curdrive
= drive
| 0x80;
449 /***********************************************************************
450 * DRIVE_FindDriveRoot
452 * Find a drive for which the root matches the beginning of the given path.
453 * This can be used to translate a Unix path into a drive + DOS path.
454 * Return value is the drive, or -1 on error. On success, path is modified
455 * to point to the beginning of the DOS path.
457 * Note: path must be in the encoding of the underlying Unix file system.
459 int DRIVE_FindDriveRoot( const char **path
)
461 /* Starting with the full path, check if the device and inode match any of
462 * the wine 'drives'. If not then remove the last path component and try
463 * again. If the last component was a '..' then skip a normal component
464 * since it's a directory that's ascended back out of.
466 int drive
, level
, len
;
467 char buffer
[MAX_PATHNAME_LEN
];
471 strcpy( buffer
, *path
);
472 for (p
= buffer
; *p
; p
++) if (*p
== '\\') *p
= '/';
475 /* strip off trailing slashes */
476 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
481 if (stat( buffer
, &st
) == 0 && S_ISDIR( st
.st_mode
))
483 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
485 if (!DOSDrives
[drive
].root
||
486 (DOSDrives
[drive
].flags
& DRIVE_DISABLED
))
489 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
490 (DOSDrives
[drive
].ino
== st
.st_ino
))
492 if (len
== 1) len
= 0; /* preserve root slash in returned path */
493 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
494 *path
, 'A' + drive
, buffer
, *path
+ len
);
496 if (!**path
) *path
= "\\";
501 if (len
<= 1) return -1; /* reached root */
506 /* find start of the last path component */
507 while (len
> 1 && buffer
[len
- 1] != '/') len
--;
508 if (!buffer
[len
]) break; /* empty component -> reached root */
509 /* does removing it take us up a level? */
510 if (strcmp( buffer
+ len
, "." ) != 0)
511 level
+= strcmp( buffer
+ len
, ".." ) ? 1 : -1;
513 /* strip off trailing slashes */
514 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
520 /***********************************************************************
521 * DRIVE_FindDriveRootW
523 * Unicode version of DRIVE_FindDriveRoot.
525 int DRIVE_FindDriveRootW( LPCWSTR
*path
)
527 int drive
, level
, len
;
528 WCHAR buffer
[MAX_PATHNAME_LEN
];
532 strcpyW( buffer
, *path
);
533 for (p
= buffer
; *p
; p
++) if (*p
== '\\') *p
= '/';
536 /* strip off trailing slashes */
537 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
541 char buffA
[MAX_PATHNAME_LEN
];
543 WideCharToMultiByte( CP_UNIXCP
, 0, buffer
, -1, buffA
, sizeof(buffA
), NULL
, NULL
);
544 if (stat( buffA
, &st
) == 0 && S_ISDIR( st
.st_mode
))
547 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
549 if (!DOSDrives
[drive
].root
||
550 (DOSDrives
[drive
].flags
& DRIVE_DISABLED
))
553 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
554 (DOSDrives
[drive
].ino
== st
.st_ino
))
556 static const WCHAR rootW
[] = {'\\',0};
558 if (len
== 1) len
= 0; /* preserve root slash in returned path */
559 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
560 debugstr_w(*path
), 'A' + drive
, debugstr_w(buffer
), debugstr_w(*path
+ len
));
562 if (!**path
) *path
= rootW
;
567 if (len
<= 1) return -1; /* reached root */
572 static const WCHAR dotW
[] = {'.',0};
573 static const WCHAR dotdotW
[] = {'.','.',0};
575 /* find start of the last path component */
576 while (len
> 1 && buffer
[len
- 1] != '/') len
--;
577 if (!buffer
[len
]) break; /* empty component -> reached root */
578 /* does removing it take us up a level? */
579 if (strcmpW( buffer
+ len
, dotW
) != 0)
580 level
+= strcmpW( buffer
+ len
, dotdotW
) ? 1 : -1;
582 /* strip off trailing slashes */
583 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
589 /***********************************************************************
592 const char * DRIVE_GetRoot( int drive
)
594 if (!DRIVE_IsValid( drive
)) return NULL
;
595 return DOSDrives
[drive
].root
;
599 /***********************************************************************
602 LPCWSTR
DRIVE_GetDosCwd( int drive
)
604 TDB
*pTask
= GlobalLock16(GetCurrentTask());
605 if (!DRIVE_IsValid( drive
)) return NULL
;
607 /* Check if we need to change the directory to the new task. */
608 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
609 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
610 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
612 static const WCHAR rootW
[] = {'\\',0};
613 WCHAR curdirW
[MAX_PATH
];
614 MultiByteToWideChar(CP_ACP
, 0, pTask
->curdir
, -1, curdirW
, MAX_PATH
);
615 /* Perform the task-switch */
616 if (!DRIVE_Chdir( drive
, curdirW
)) DRIVE_Chdir( drive
, rootW
);
617 DRIVE_LastTask
= GetCurrentTask();
619 return DOSDrives
[drive
].dos_cwd
;
623 /***********************************************************************
626 const char * DRIVE_GetUnixCwd( int drive
)
628 TDB
*pTask
= GlobalLock16(GetCurrentTask());
629 if (!DRIVE_IsValid( drive
)) return NULL
;
631 /* Check if we need to change the directory to the new task. */
632 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
633 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
634 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
636 static const WCHAR rootW
[] = {'\\',0};
637 WCHAR curdirW
[MAX_PATH
];
638 MultiByteToWideChar(CP_ACP
, 0, pTask
->curdir
, -1, curdirW
, MAX_PATH
);
639 /* Perform the task-switch */
640 if (!DRIVE_Chdir( drive
, curdirW
)) DRIVE_Chdir( drive
, rootW
);
641 DRIVE_LastTask
= GetCurrentTask();
643 return DOSDrives
[drive
].unix_cwd
;
647 /***********************************************************************
650 const char * DRIVE_GetDevice( int drive
)
652 return (DRIVE_IsValid( drive
)) ? DOSDrives
[drive
].device
: NULL
;
655 /******************************************************************
656 * static WORD CDROM_Data_FindBestVoldesc
660 static WORD
CDROM_Data_FindBestVoldesc(int fd
)
662 BYTE cur_vd_type
, max_vd_type
= 0;
663 unsigned int offs
, best_offs
= 0, extra_offs
= 0;
666 for (offs
= 0x8000; offs
<= 0x9800; offs
+= 0x800)
668 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
669 * the volume label is displaced forward by 8
671 lseek(fd
, offs
+ 11, SEEK_SET
); /* check for non-ISO9660 signature */
673 if ((sig
[0] == 'R') && (sig
[1] == 'O') && (sig
[2]=='M'))
677 lseek(fd
, offs
+ extra_offs
, SEEK_SET
);
678 read(fd
, &cur_vd_type
, 1);
679 if (cur_vd_type
== 0xff) /* voldesc set terminator */
681 if (cur_vd_type
> max_vd_type
)
683 max_vd_type
= cur_vd_type
;
684 best_offs
= offs
+ extra_offs
;
690 /***********************************************************************
691 * DRIVE_ReadSuperblock
694 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
695 * to check, that they are writing on a FAT filesystem !
697 int DRIVE_ReadSuperblock (int drive
, char * buff
)
699 #define DRIVE_SUPER 96
703 struct stat stat_buf
;
705 memset(buff
, 0, DRIVE_SUPER
);
706 /* O_NONBLOCK in case we're opening FIFO; will be reset later */
707 if ((fd
= open(DOSDrives
[drive
].device
, O_RDONLY
|O_NOCTTY
|O_NONBLOCK
)) != -1) {
708 if (fstat(fd
, &stat_buf
) < 0) { /* shouldn't happen since we just opened that file */
709 ERR("fstat() failed for opened device '%s' (drive %c:) ! IT SHOULDN'T HAPPEN !!!\n",
710 DOSDrives
[drive
].device
, 'A'+drive
);
712 } else if (!S_ISBLK(stat_buf
.st_mode
)) {
713 ERR("Device '%s' (drive %c:) is not a block device - check your config\n",
714 DOSDrives
[drive
].device
, 'A'+drive
);
716 /* reset O_NONBLOCK */
717 } else if (fcntl(fd
, F_SETFL
, 0) < 0 || fcntl(fd
, F_GETFL
) & O_NONBLOCK
) {
718 ERR("fcntl() failed to reset O_NONBLOCK for device '%s' (drive %c:)\n",
719 DOSDrives
[drive
].device
, 'A'+drive
);
727 if (!DOSDrives
[drive
].device
)
728 ERR("No device configured for drive %c: !\n", 'A'+drive
);
730 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives
[drive
].device
, 'A'+drive
,
731 (stat(DOSDrives
[drive
].device
, &stat_buf
)) ?
732 "not available or symlink not valid ?" : "no permission");
735 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
739 switch(DOSDrives
[drive
].type
)
741 case DRIVE_REMOVABLE
:
746 offs
= CDROM_Data_FindBestVoldesc(fd
);
753 if ((offs
) && (lseek(fd
,offs
,SEEK_SET
)!=offs
))
758 if (read(fd
,buff
,DRIVE_SUPER
)!=DRIVE_SUPER
)
764 switch(DOSDrives
[drive
].type
)
766 case DRIVE_REMOVABLE
:
768 if ((buff
[0x26]!=0x29) || /* Check for FAT present */
769 /* FIXME: do really all FAT have their name beginning with
770 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
771 memcmp( buff
+0x36,"FAT",3))
773 ERR("The filesystem is not FAT !! (device=%s)\n",
774 DOSDrives
[drive
].device
);
780 if (strncmp(&buff
[1],"CD001",5)) /* Check for iso9660 present */
785 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
799 /***********************************************************************
800 * DRIVE_WriteSuperblockEntry
803 * We are writing as little as possible (ie. not the whole SuperBlock)
804 * not to interfere with kernel. The drive can be mounted !
806 int DRIVE_WriteSuperblockEntry (int drive
, off_t ofs
, size_t len
, char * buff
)
810 if ((fd
=open(DOSDrives
[drive
].device
,O_WRONLY
))==-1)
812 ERR("Cannot open the device %s (for writing)\n",
813 DOSDrives
[drive
].device
);
816 if (lseek(fd
,ofs
,SEEK_SET
)!=ofs
)
818 ERR("lseek failed on device %s !\n",
819 DOSDrives
[drive
].device
);
823 if (write(fd
,buff
,len
)!=len
)
826 ERR("Cannot write on %s !\n",
827 DOSDrives
[drive
].device
);
833 /******************************************************************
834 * static HANDLE CDROM_Open
838 static HANDLE
CDROM_Open(int drive
)
840 WCHAR root
[] = {'\\','\\','.','\\','A',':',0};
842 return CreateFileW(root
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, 0);
845 /**************************************************************************
846 * CDROM_Data_GetLabel [internal]
848 DWORD
CDROM_Data_GetLabel(int drive
, WCHAR
*label
)
850 #define LABEL_LEN 32+1
851 int dev
= open(DOSDrives
[drive
].device
, O_RDONLY
|O_NONBLOCK
);
852 WORD offs
= CDROM_Data_FindBestVoldesc(dev
);
853 WCHAR label_read
[LABEL_LEN
]; /* Unicode possible, too */
854 DWORD unicode_id
= 0;
858 if ((lseek(dev
, offs
+0x58, SEEK_SET
) == offs
+0x58)
859 && (read(dev
, &unicode_id
, 3) == 3))
861 int ver
= (unicode_id
& 0xff0000) >> 16;
863 if ((lseek(dev
, offs
+0x28, SEEK_SET
) != offs
+0x28)
864 || (read(dev
, &label_read
, LABEL_LEN
) != LABEL_LEN
))
868 if ((LOWORD(unicode_id
) == 0x2f25) /* Unicode ID */
869 && ((ver
== 0x40) || (ver
== 0x43) || (ver
== 0x45)))
870 { /* yippee, unicode */
873 for (i
=0; i
<LABEL_LEN
;i
++)
874 { /* Motorola -> Intel Unicode conversion :-\ */
876 label_read
[i
] = (ch
<< 8) | (ch
>> 8);
878 strncpyW(label
, label_read
, 11);
883 MultiByteToWideChar(CP_UNIXCP
, 0, (LPSTR
)label_read
, -1, label
, 11);
891 ERR("error reading label !\n");
895 /**************************************************************************
896 * CDROM_GetLabel [internal]
898 static DWORD
CDROM_GetLabel(int drive
, WCHAR
*label
)
905 h
= CDROM_Open(drive
);
908 r
= DeviceIoControl(h
, IOCTL_CDROM_DISK_TYPE
, NULL
,
909 0, &cdd
, sizeof(cdd
), &br
, 0);
914 switch (cdd
.DiskData
& 0x03)
916 case CDROM_DISK_DATA_TRACK
:
917 if (!CDROM_Data_GetLabel(drive
, label
))
920 case CDROM_DISK_AUDIO_TRACK
:
922 static const WCHAR audioCD
[] = {'A','u','d','i','o',' ','C','D',' ',' ',' ',0};
923 strcpyW(label
, audioCD
);
926 case CDROM_DISK_DATA_TRACK
|CDROM_DISK_AUDIO_TRACK
:
927 FIXME("Need to get the label of a mixed mode CD!\n");
928 /* This assumes that the first track is a data track! */
929 /* I guess the correct way would be to enumerate all data tracks
930 and check each for the title */
931 if (!CDROM_Data_GetLabel(drive
, label
))
938 TRACE("CD: label is %s\n", debugstr_w(label
));
942 /***********************************************************************
945 LPCWSTR
DRIVE_GetLabel( int drive
)
948 char buff
[DRIVE_SUPER
];
951 if (!DRIVE_IsValid( drive
)) return NULL
;
952 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
954 read
= CDROM_GetLabel(drive
, DOSDrives
[drive
].label_read
);
957 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
959 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
960 ERR("Invalid or unreadable superblock on %s (%c:).\n",
961 DOSDrives
[drive
].device
, (char)(drive
+'A'));
963 if (DOSDrives
[drive
].type
== DRIVE_REMOVABLE
||
964 DOSDrives
[drive
].type
== DRIVE_FIXED
)
967 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
969 MultiByteToWideChar(CP_UNIXCP
, 0, buff
+offs
, 11,
970 DOSDrives
[drive
].label_read
, 11);
971 DOSDrives
[drive
].label_read
[11]='\0';
977 DOSDrives
[drive
].label_read
: DOSDrives
[drive
].label_conf
;
980 #define CDFRAMES_PERSEC 75
981 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
982 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
983 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
985 /**************************************************************************
986 * CDROM_Audio_GetSerial [internal]
988 static DWORD
CDROM_Audio_GetSerial(HANDLE h
)
990 unsigned long serial
= 0;
993 DWORD dwStart
, dwEnd
, br
;
996 if (!DeviceIoControl(h
, IOCTL_CDROM_READ_TOC
, NULL
, 0, &toc
, sizeof(toc
), &br
, 0))
1000 * wMagic collects the wFrames from track 1
1001 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
1003 * There it is collected for correcting the serial when there are less than
1006 wMagic
= toc
.TrackData
[0].Address
[2];
1007 dwStart
= FRAME_OF_TOC(toc
, toc
.FirstTrack
);
1009 for (i
= 0; i
<= toc
.LastTrack
- toc
.FirstTrack
; i
++) {
1010 serial
+= (toc
.TrackData
[i
].Address
[0] << 16) |
1011 (toc
.TrackData
[i
].Address
[1] << 8) | toc
.TrackData
[i
].Address
[2];
1013 dwEnd
= FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1);
1015 if (toc
.LastTrack
- toc
.FirstTrack
+ 1 < 3)
1016 serial
+= wMagic
+ (dwEnd
- dwStart
);
1021 /**************************************************************************
1022 * CDROM_Data_GetSerial [internal]
1024 static DWORD
CDROM_Data_GetSerial(int drive
)
1026 int dev
= open(DOSDrives
[drive
].device
, O_RDONLY
|O_NONBLOCK
);
1032 BYTE b0
= 0, b1
= 1, b2
= 2, b3
= 3;
1035 if (dev
== -1) return 0;
1036 offs
= CDROM_Data_FindBestVoldesc(dev
);
1042 RTL_OSVERSIONINFOEXW ovi
;
1045 lseek(dev
, offs
, SEEK_SET
);
1046 read(dev
, buf
, 2048);
1048 * OK, another braindead one... argh. Just believe it.
1049 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
1050 * It's true and nobody will ever be able to change it.
1052 ovi
.dwOSVersionInfoSize
= sizeof(ovi
);
1053 RtlGetVersion(&ovi
);
1054 if ((ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) && (ovi
.dwMajorVersion
>= 4))
1056 b0
= 3; b1
= 2; b2
= 1; b3
= 0;
1058 for (i
= 0; i
< 2048; i
+= 4)
1060 /* DON'T optimize this into DWORD !! (breaks overflow) */
1061 serial
.p
[b0
] += buf
[i
+b0
];
1062 serial
.p
[b1
] += buf
[i
+b1
];
1063 serial
.p
[b2
] += buf
[i
+b2
];
1064 serial
.p
[b3
] += buf
[i
+b3
];
1071 /**************************************************************************
1072 * CDROM_GetSerial [internal]
1074 static DWORD
CDROM_GetSerial(int drive
)
1078 CDROM_DISK_DATA cdd
;
1082 TRACE("%d\n", drive
);
1084 h
= CDROM_Open(drive
);
1087 r
= DeviceIoControl(h
, IOCTL_CDROM_DISK_TYPE
, NULL
,
1088 0, &cdd
, sizeof(cdd
), &br
, 0);
1095 switch (cdd
.DiskData
& 0x03)
1097 case CDROM_DISK_DATA_TRACK
:
1098 /* hopefully a data CD */
1099 serial
= CDROM_Data_GetSerial(drive
);
1101 case CDROM_DISK_AUDIO_TRACK
:
1103 case CDROM_DISK_DATA_TRACK
|CDROM_DISK_AUDIO_TRACK
:
1104 serial
= CDROM_Audio_GetSerial(h
);
1111 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial
), LOWORD(serial
));
1118 /***********************************************************************
1119 * DRIVE_GetSerialNumber
1121 DWORD
DRIVE_GetSerialNumber( int drive
)
1124 char buff
[DRIVE_SUPER
];
1126 TRACE("drive %d, type = %d\n", drive
, DOSDrives
[drive
].type
);
1128 if (!DRIVE_IsValid( drive
)) return 0;
1130 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
1132 switch(DOSDrives
[drive
].type
)
1134 case DRIVE_REMOVABLE
:
1136 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
1137 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
1138 " Maybe not FAT?\n" ,
1139 DOSDrives
[drive
].device
, 'A'+drive
);
1141 serial
= *((DWORD
*)(buff
+0x27));
1144 serial
= CDROM_GetSerial(drive
);
1147 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive
+'A');
1151 return (serial
) ? serial
: DOSDrives
[drive
].serial_conf
;
1155 /***********************************************************************
1156 * DRIVE_SetSerialNumber
1158 int DRIVE_SetSerialNumber( int drive
, DWORD serial
)
1160 char buff
[DRIVE_SUPER
];
1162 if (!DRIVE_IsValid( drive
)) return 0;
1164 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
1166 if ((DOSDrives
[drive
].type
!= DRIVE_REMOVABLE
) &&
1167 (DOSDrives
[drive
].type
!= DRIVE_FIXED
)) return 0;
1168 /* check, if the drive has a FAT filesystem */
1169 if (DRIVE_ReadSuperblock(drive
, buff
)) return 0;
1170 if (DRIVE_WriteSuperblockEntry(drive
, 0x27, 4, (char *) &serial
)) return 0;
1174 if (DOSDrives
[drive
].type
== DRIVE_CDROM
) return 0;
1175 DOSDrives
[drive
].serial_conf
= serial
;
1180 /***********************************************************************
1183 static UINT
DRIVE_GetType( int drive
)
1185 if (!DRIVE_IsValid( drive
)) return DRIVE_NO_ROOT_DIR
;
1186 return DOSDrives
[drive
].type
;
1190 /***********************************************************************
1193 UINT
DRIVE_GetFlags( int drive
)
1195 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
1196 return DOSDrives
[drive
].flags
;
1199 /***********************************************************************
1202 int DRIVE_Chdir( int drive
, LPCWSTR path
)
1204 DOS_FULL_NAME full_name
;
1205 WCHAR buffer
[MAX_PATHNAME_LEN
];
1207 BY_HANDLE_FILE_INFORMATION info
;
1208 TDB
*pTask
= GlobalLock16(GetCurrentTask());
1210 buffer
[0] = 'A' + drive
;
1213 TRACE("(%s,%s)\n", debugstr_w(buffer
), debugstr_w(path
) );
1214 strncpyW( buffer
+ 2, path
, MAX_PATHNAME_LEN
- 2 );
1215 buffer
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
1217 if (!DOSFS_GetFullName( buffer
, TRUE
, &full_name
)) return 0;
1218 if (!FILE_Stat( full_name
.long_name
, &info
, NULL
)) return 0;
1219 if (!(info
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1221 SetLastError( ERROR_FILE_NOT_FOUND
);
1224 unix_cwd
= full_name
.long_name
+ strlen( DOSDrives
[drive
].root
);
1225 while (*unix_cwd
== '/') unix_cwd
++;
1227 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
1228 'A' + drive
, unix_cwd
, debugstr_w(full_name
.short_name
+ 3) );
1230 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].dos_cwd
);
1231 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].unix_cwd
);
1232 DOSDrives
[drive
].dos_cwd
= HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name
.short_name
) - 2) * sizeof(WCHAR
));
1233 strcpyW(DOSDrives
[drive
].dos_cwd
, full_name
.short_name
+ 3);
1234 DOSDrives
[drive
].unix_cwd
= heap_strdup( unix_cwd
);
1236 if (drive
== DRIVE_CurDrive
)
1238 UNICODE_STRING dirW
;
1240 RtlInitUnicodeString( &dirW
, full_name
.short_name
);
1241 RtlSetCurrentDirectory_U( &dirW
);
1244 if (pTask
&& (pTask
->curdrive
& 0x80) &&
1245 ((pTask
->curdrive
& ~0x80) == drive
))
1247 WideCharToMultiByte(CP_ACP
, 0, full_name
.short_name
+ 2, -1,
1248 pTask
->curdir
, sizeof(pTask
->curdir
), NULL
, NULL
);
1249 DRIVE_LastTask
= GetCurrentTask();
1255 /***********************************************************************
1258 int DRIVE_Disable( int drive
)
1260 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
1262 SetLastError( ERROR_INVALID_DRIVE
);
1265 DOSDrives
[drive
].flags
|= DRIVE_DISABLED
;
1270 /***********************************************************************
1273 int DRIVE_Enable( int drive
)
1275 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
1277 SetLastError( ERROR_INVALID_DRIVE
);
1280 DOSDrives
[drive
].flags
&= ~DRIVE_DISABLED
;
1285 /***********************************************************************
1286 * DRIVE_SetLogicalMapping
1288 int DRIVE_SetLogicalMapping ( int existing_drive
, int new_drive
)
1290 /* If new_drive is already valid, do nothing and return 0
1291 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1293 DOSDRIVE
*old
, *new;
1295 old
= DOSDrives
+ existing_drive
;
1296 new = DOSDrives
+ new_drive
;
1298 if ((existing_drive
< 0) || (existing_drive
>= MAX_DOS_DRIVES
) ||
1300 (new_drive
< 0) || (new_drive
>= MAX_DOS_DRIVES
))
1302 SetLastError( ERROR_INVALID_DRIVE
);
1308 TRACE("Can't map drive %c: to already existing drive %c:\n",
1309 'A' + existing_drive
, 'A' + new_drive
);
1310 /* it is already mapped there, so return success */
1311 if (!strcmp(old
->root
,new->root
))
1316 new->root
= heap_strdup( old
->root
);
1317 new->dos_cwd
= HeapAlloc(GetProcessHeap(), 0, (strlenW(old
->dos_cwd
) + 1) * sizeof(WCHAR
));
1318 strcpyW(new->dos_cwd
, old
->dos_cwd
);
1319 new->unix_cwd
= heap_strdup( old
->unix_cwd
);
1320 new->device
= heap_strdup( old
->device
);
1321 memcpy ( new->label_conf
, old
->label_conf
, 12 );
1322 memcpy ( new->label_read
, old
->label_read
, 12 );
1323 new->serial_conf
= old
->serial_conf
;
1324 new->type
= old
->type
;
1325 new->flags
= old
->flags
;
1326 new->dev
= old
->dev
;
1327 new->ino
= old
->ino
;
1329 TRACE("Drive %c: is now equal to drive %c:\n",
1330 'A' + new_drive
, 'A' + existing_drive
);
1336 /***********************************************************************
1339 * Open the drive raw device and return a Unix fd (or -1 on error).
1341 int DRIVE_OpenDevice( int drive
, int flags
)
1343 if (!DRIVE_IsValid( drive
)) return -1;
1344 return open( DOSDrives
[drive
].device
, flags
);
1348 /***********************************************************************
1349 * DRIVE_GetFreeSpace
1351 static int DRIVE_GetFreeSpace( int drive
, PULARGE_INTEGER size
,
1352 PULARGE_INTEGER available
)
1356 if (!DRIVE_IsValid(drive
))
1358 SetLastError( ERROR_PATH_NOT_FOUND
);
1362 /* FIXME: add autoconf check for this */
1363 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1364 if (statfs( DOSDrives
[drive
].root
, &info
, 0, 0) < 0)
1366 if (statfs( DOSDrives
[drive
].root
, &info
) < 0)
1370 WARN("cannot do statfs(%s)\n", DOSDrives
[drive
].root
);
1374 size
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bsize
, info
.f_blocks
);
1375 #ifdef HAVE_STRUCT_STATFS_F_BAVAIL
1376 available
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bavail
, info
.f_bsize
);
1378 # ifdef HAVE_STRUCT_STATFS_F_BFREE
1379 available
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bfree
, info
.f_bsize
);
1381 # error "statfs has no bfree/bavail member!"
1384 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
1385 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1386 available
->QuadPart
= 0;
1391 /***********************************************************************
1392 * DRIVE_GetCurrentDirectory
1393 * Returns "X:\\path\\etc\\".
1395 * Despite the API description, return required length including the
1396 * terminating null when buffer too small. This is the real behaviour.
1398 static UINT
DRIVE_GetCurrentDirectory( UINT buflen
, LPWSTR buf
)
1401 LPCWSTR dos_cwd
= DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1402 static const WCHAR driveA_rootW
[] = {'A',':','\\',0};
1404 ret
= strlenW(dos_cwd
) + 3; /* length of WHOLE current directory */
1405 if (ret
>= buflen
) return ret
+ 1;
1407 strcpyW( buf
, driveA_rootW
);
1408 buf
[0] += DRIVE_GetCurrentDrive();
1409 strcatW( buf
, dos_cwd
);
1414 /***********************************************************************
1417 * Build the environment array containing the drives' current directories.
1418 * Resulting pointer must be freed with HeapFree.
1420 WCHAR
*DRIVE_BuildEnv(void)
1423 LPCWSTR cwd
[MAX_DOS_DRIVES
];
1426 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++)
1428 if ((cwd
[i
] = DRIVE_GetDosCwd(i
)) && cwd
[i
][0])
1429 length
+= strlenW(cwd
[i
]) + 8;
1431 if (!(env
= HeapAlloc( GetProcessHeap(), 0, (length
+1) * sizeof(WCHAR
) ))) return NULL
;
1432 for (i
= 0, p
= env
; i
< MAX_DOS_DRIVES
; i
++)
1434 if (cwd
[i
] && cwd
[i
][0])
1436 *p
++ = '='; *p
++ = 'A' + i
; *p
++ = ':';
1437 *p
++ = '='; *p
++ = 'A' + i
; *p
++ = ':'; *p
++ = '\\';
1438 strcpyW( p
, cwd
[i
] );
1439 p
+= strlenW(p
) + 1;
1447 /***********************************************************************
1448 * GetDiskFreeSpace (KERNEL.422)
1450 BOOL16 WINAPI
GetDiskFreeSpace16( LPCSTR root
, LPDWORD cluster_sectors
,
1451 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1452 LPDWORD total_clusters
)
1454 return GetDiskFreeSpaceA( root
, cluster_sectors
, sector_bytes
,
1455 free_clusters
, total_clusters
);
1459 /***********************************************************************
1460 * GetDiskFreeSpaceW (KERNEL32.@)
1462 * Fails if expression resulting from current drive's dir and "root"
1463 * is not a root dir of the target drive.
1465 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1466 * if the corresponding info is unneeded.
1468 * FIXME: needs to support UNC names from Win95 OSR2 on.
1470 * Behaviour under Win95a:
1471 * CurrDir root result
1472 * "E:\\TEST" "E:" FALSE
1476 * "E:\\TEST" "\\" TRUE
1477 * "E:\\TEST" ":\\" FALSE
1478 * "E:\\TEST" "E:\\" TRUE
1479 * "E:\\TEST" "" FALSE
1480 * "E:\\" "" FALSE (!)
1482 * "E:\\TEST" 0x0 TRUE (!)
1483 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1484 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1486 BOOL WINAPI
GetDiskFreeSpaceW( LPCWSTR root
, LPDWORD cluster_sectors
,
1487 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1488 LPDWORD total_clusters
)
1490 int drive
, sec_size
;
1491 ULARGE_INTEGER size
,available
;
1495 TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root
), cluster_sectors
, sector_bytes
,
1496 free_clusters
, total_clusters
);
1498 if (!root
|| root
[0] == '\\' || root
[0] == '/')
1499 drive
= DRIVE_GetCurrentDrive();
1501 if (root
[0] && root
[1] == ':') /* root contains drive tag */
1503 drive
= toupperW(root
[0]) - 'A';
1505 if (path
[0] == '\0')
1507 path
= DRIVE_GetDosCwd(drive
);
1510 SetLastError(ERROR_PATH_NOT_FOUND
);
1515 if (path
[0] == '\\')
1518 if (path
[0]) /* oops, we are in a subdir */
1520 SetLastError(ERROR_INVALID_NAME
);
1527 SetLastError(ERROR_PATH_NOT_FOUND
);
1529 SetLastError(ERROR_INVALID_NAME
);
1533 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1535 /* Cap the size and available at 2GB as per specs. */
1536 if ((size
.s
.HighPart
) ||(size
.s
.LowPart
> 0x7fffffff))
1538 size
.s
.HighPart
= 0;
1539 size
.s
.LowPart
= 0x7fffffff;
1541 if ((available
.s
.HighPart
) ||(available
.s
.LowPart
> 0x7fffffff))
1543 available
.s
.HighPart
=0;
1544 available
.s
.LowPart
= 0x7fffffff;
1546 sec_size
= (DRIVE_GetType(drive
)==DRIVE_CDROM
) ? 2048 : 512;
1547 size
.s
.LowPart
/= sec_size
;
1548 available
.s
.LowPart
/= sec_size
;
1549 /* FIXME: probably have to adjust those variables too for CDFS */
1551 while (cluster_sec
* 65536 < size
.s
.LowPart
) cluster_sec
*= 2;
1553 if (cluster_sectors
)
1554 *cluster_sectors
= cluster_sec
;
1556 *sector_bytes
= sec_size
;
1558 *free_clusters
= available
.s
.LowPart
/ cluster_sec
;
1560 *total_clusters
= size
.s
.LowPart
/ cluster_sec
;
1565 /***********************************************************************
1566 * GetDiskFreeSpaceA (KERNEL32.@)
1568 BOOL WINAPI
GetDiskFreeSpaceA( LPCSTR root
, LPDWORD cluster_sectors
,
1569 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1570 LPDWORD total_clusters
)
1572 UNICODE_STRING rootW
;
1577 if(!RtlCreateUnicodeStringFromAsciiz(&rootW
, root
))
1579 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1584 rootW
.Buffer
= NULL
;
1586 ret
= GetDiskFreeSpaceW(rootW
.Buffer
, cluster_sectors
, sector_bytes
,
1587 free_clusters
, total_clusters
);
1588 RtlFreeUnicodeString(&rootW
);
1594 /***********************************************************************
1595 * GetDiskFreeSpaceExW (KERNEL32.@)
1597 * This function is used to acquire the size of the available and
1598 * total space on a logical volume.
1602 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1603 * detailed error information.
1606 BOOL WINAPI
GetDiskFreeSpaceExW( LPCWSTR root
,
1607 PULARGE_INTEGER avail
,
1608 PULARGE_INTEGER total
,
1609 PULARGE_INTEGER totalfree
)
1612 ULARGE_INTEGER size
,available
;
1614 if (!root
) drive
= DRIVE_GetCurrentDrive();
1616 { /* C: always works for GetDiskFreeSpaceEx */
1617 if ((root
[1]) && ((root
[1] != ':') || (root
[2] && root
[2] != '\\')))
1619 FIXME("there are valid root names which are not supported yet\n");
1620 /* ..like UNC names, for instance. */
1622 WARN("invalid root '%s'\n", debugstr_w(root
));
1625 drive
= toupperW(root
[0]) - 'A';
1628 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1632 total
->s
.HighPart
= size
.s
.HighPart
;
1633 total
->s
.LowPart
= size
.s
.LowPart
;
1638 totalfree
->s
.HighPart
= available
.s
.HighPart
;
1639 totalfree
->s
.LowPart
= available
.s
.LowPart
;
1644 if (FIXME_ON(dosfs
))
1646 /* On Windows2000, we need to check the disk quota
1647 allocated for the user owning the calling process. We
1648 don't want to be more obtrusive than necessary with the
1649 FIXME messages, so don't print the FIXME unless Wine is
1650 actually masquerading as Windows2000. */
1652 RTL_OSVERSIONINFOEXW ovi
;
1653 ovi
.dwOSVersionInfoSize
= sizeof(ovi
);
1654 if (RtlGetVersion(&ovi
))
1656 if (ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
&& ovi
.dwMajorVersion
> 4)
1657 FIXME("no per-user quota support yet\n");
1661 /* Quick hack, should eventually be fixed to work 100% with
1662 Windows2000 (see comment above). */
1663 avail
->s
.HighPart
= available
.s
.HighPart
;
1664 avail
->s
.LowPart
= available
.s
.LowPart
;
1670 /***********************************************************************
1671 * GetDiskFreeSpaceExA (KERNEL32.@)
1673 BOOL WINAPI
GetDiskFreeSpaceExA( LPCSTR root
, PULARGE_INTEGER avail
,
1674 PULARGE_INTEGER total
,
1675 PULARGE_INTEGER totalfree
)
1677 UNICODE_STRING rootW
;
1680 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
1681 else rootW
.Buffer
= NULL
;
1683 ret
= GetDiskFreeSpaceExW( rootW
.Buffer
, avail
, total
, totalfree
);
1685 RtlFreeUnicodeString(&rootW
);
1689 /***********************************************************************
1690 * GetDriveType (KERNEL.136)
1691 * This function returns the type of a drive in Win16.
1692 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1693 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1694 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1695 * do any pseudo-clever changes.
1698 * drivetype DRIVE_xxx
1700 UINT16 WINAPI
GetDriveType16( UINT16 drive
) /* [in] number (NOT letter) of drive */
1702 UINT type
= DRIVE_GetType(drive
);
1703 TRACE("(%c:)\n", 'A' + drive
);
1704 if (type
== DRIVE_CDROM
) type
= DRIVE_REMOTE
;
1709 /***********************************************************************
1710 * GetDriveTypeW (KERNEL32.@)
1712 * Returns the type of the disk drive specified. If root is NULL the
1713 * root of the current directory is used.
1717 * Type of drive (from Win32 SDK):
1719 * DRIVE_UNKNOWN unable to find out anything about the drive
1720 * DRIVE_NO_ROOT_DIR nonexistent root dir
1721 * DRIVE_REMOVABLE the disk can be removed from the machine
1722 * DRIVE_FIXED the disk can not be removed from the machine
1723 * DRIVE_REMOTE network disk
1724 * DRIVE_CDROM CDROM drive
1725 * DRIVE_RAMDISK virtual disk in RAM
1727 UINT WINAPI
GetDriveTypeW(LPCWSTR root
) /* [in] String describing drive */
1730 TRACE("(%s)\n", debugstr_w(root
));
1732 if (NULL
== root
) drive
= DRIVE_GetCurrentDrive();
1735 if ((root
[1]) && (root
[1] != ':'))
1737 WARN("invalid root %s\n", debugstr_w(root
));
1738 return DRIVE_NO_ROOT_DIR
;
1740 drive
= toupperW(root
[0]) - 'A';
1742 return DRIVE_GetType(drive
);
1746 /***********************************************************************
1747 * GetDriveTypeA (KERNEL32.@)
1749 UINT WINAPI
GetDriveTypeA( LPCSTR root
)
1751 UNICODE_STRING rootW
;
1756 if( !RtlCreateUnicodeStringFromAsciiz(&rootW
, root
))
1758 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1763 rootW
.Buffer
= NULL
;
1765 ret
= GetDriveTypeW(rootW
.Buffer
);
1767 RtlFreeUnicodeString(&rootW
);
1773 /***********************************************************************
1774 * GetCurrentDirectory (KERNEL.411)
1776 UINT16 WINAPI
GetCurrentDirectory16( UINT16 buflen
, LPSTR buf
)
1778 WCHAR cur_dirW
[MAX_PATH
];
1780 DRIVE_GetCurrentDirectory(MAX_PATH
, cur_dirW
);
1781 return (UINT16
)WideCharToMultiByte(CP_ACP
, 0, cur_dirW
, -1, buf
, buflen
, NULL
, NULL
);
1785 /***********************************************************************
1786 * GetCurrentDirectoryW (KERNEL32.@)
1788 UINT WINAPI
GetCurrentDirectoryW( UINT buflen
, LPWSTR buf
)
1791 WCHAR longname
[MAX_PATHNAME_LEN
];
1792 WCHAR shortname
[MAX_PATHNAME_LEN
];
1794 ret
= DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN
, shortname
);
1795 if ( ret
> MAX_PATHNAME_LEN
) {
1796 ERR_(file
)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret
);
1799 GetLongPathNameW(shortname
, longname
, MAX_PATHNAME_LEN
);
1800 ret
= strlenW( longname
) + 1;
1801 if (ret
> buflen
) return ret
;
1802 strcpyW(buf
, longname
);
1806 /***********************************************************************
1807 * GetCurrentDirectoryA (KERNEL32.@)
1809 UINT WINAPI
GetCurrentDirectoryA( UINT buflen
, LPSTR buf
)
1811 WCHAR bufferW
[MAX_PATH
];
1814 retW
= GetCurrentDirectoryW(MAX_PATH
, bufferW
);
1818 else if (retW
> MAX_PATH
)
1820 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1825 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, NULL
, 0, NULL
, NULL
);
1828 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buf
, buflen
, NULL
, NULL
);
1829 ret
--; /* length without 0 */
1836 /***********************************************************************
1837 * SetCurrentDirectory (KERNEL.412)
1839 BOOL16 WINAPI
SetCurrentDirectory16( LPCSTR dir
)
1841 return SetCurrentDirectoryA( dir
);
1845 /***********************************************************************
1846 * SetCurrentDirectoryW (KERNEL32.@)
1848 BOOL WINAPI
SetCurrentDirectoryW( LPCWSTR dir
)
1850 int drive
, olddrive
= DRIVE_GetCurrentDrive();
1854 SetLastError(ERROR_INVALID_PARAMETER
);
1857 if (dir
[0] && (dir
[1]==':'))
1859 drive
= toupperW( *dir
) - 'A';
1865 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1866 sets pTask->curdir only if pTask->curdrive is drive */
1867 if (!(DRIVE_SetCurrentDrive( drive
)))
1870 /* FIXME: what about empty strings? Add a \\ ? */
1871 if (!DRIVE_Chdir( drive
, dir
)) {
1872 DRIVE_SetCurrentDrive(olddrive
);
1879 /***********************************************************************
1880 * SetCurrentDirectoryA (KERNEL32.@)
1882 BOOL WINAPI
SetCurrentDirectoryA( LPCSTR dir
)
1884 UNICODE_STRING dirW
;
1889 SetLastError(ERROR_INVALID_PARAMETER
);
1893 if (RtlCreateUnicodeStringFromAsciiz(&dirW
, dir
))
1895 ret
= SetCurrentDirectoryW(dirW
.Buffer
);
1896 RtlFreeUnicodeString(&dirW
);
1899 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1904 /***********************************************************************
1905 * GetLogicalDriveStringsA (KERNEL32.@)
1907 UINT WINAPI
GetLogicalDriveStringsA( UINT len
, LPSTR buffer
)
1911 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1912 if (DRIVE_IsValid(drive
)) count
++;
1913 if ((count
* 4) + 1 <= len
)
1916 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1917 if (DRIVE_IsValid(drive
))
1928 return (count
* 4) + 1; /* account for terminating null */
1929 /* The API tells about these different return values */
1933 /***********************************************************************
1934 * GetLogicalDriveStringsW (KERNEL32.@)
1936 UINT WINAPI
GetLogicalDriveStringsW( UINT len
, LPWSTR buffer
)
1940 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1941 if (DRIVE_IsValid(drive
)) count
++;
1942 if (count
* 4 * sizeof(WCHAR
) <= len
)
1945 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1946 if (DRIVE_IsValid(drive
))
1948 *p
++ = (WCHAR
)('a' + drive
);
1955 return count
* 4 * sizeof(WCHAR
);
1959 /***********************************************************************
1960 * GetLogicalDrives (KERNEL32.@)
1962 DWORD WINAPI
GetLogicalDrives(void)
1967 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1969 if ( (DRIVE_IsValid(drive
)) ||
1970 (DOSDrives
[drive
].type
== DRIVE_CDROM
)) /* audio CD is also valid */
1971 ret
|= (1 << drive
);
1977 /***********************************************************************
1978 * GetVolumeInformationW (KERNEL32.@)
1980 BOOL WINAPI
GetVolumeInformationW( LPCWSTR root
, LPWSTR label
,
1981 DWORD label_len
, DWORD
*serial
,
1982 DWORD
*filename_len
, DWORD
*flags
,
1983 LPWSTR fsname
, DWORD fsname_len
)
1988 /* FIXME, SetLastError()s missing */
1990 if (!root
) drive
= DRIVE_GetCurrentDrive();
1993 if (root
[0] && root
[1] != ':')
1995 WARN("invalid root %s\n", debugstr_w(root
));
1998 drive
= toupperW(root
[0]) - 'A';
2000 if (!DRIVE_IsValid( drive
)) return FALSE
;
2001 if (label
&& label_len
)
2003 strncpyW( label
, DRIVE_GetLabel(drive
), label_len
);
2004 label
[label_len
- 1] = 0; /* ensure 0 termination */
2005 cp
= label
+ strlenW(label
);
2006 while (cp
!= label
&& *(cp
-1) == ' ') cp
--;
2009 if (serial
) *serial
= DRIVE_GetSerialNumber(drive
);
2011 /* Set the filesystem information */
2012 /* Note: we only emulate a FAT fs at present */
2015 if (DOSDrives
[drive
].flags
& DRIVE_SHORT_NAMES
)
2018 *filename_len
= 255;
2023 if (DOSDrives
[drive
].flags
& DRIVE_CASE_SENSITIVE
)
2024 *flags
|=FS_CASE_SENSITIVE
;
2025 if (DOSDrives
[drive
].flags
& DRIVE_CASE_PRESERVING
)
2026 *flags
|=FS_CASE_IS_PRESERVED
;
2028 if (fsname
&& fsname_len
)
2030 /* Diablo checks that return code ... */
2031 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
2033 static const WCHAR cdfsW
[] = {'C','D','F','S',0};
2034 strncpyW( fsname
, cdfsW
, fsname_len
);
2038 static const WCHAR fatW
[] = {'F','A','T',0};
2039 strncpyW( fsname
, fatW
, fsname_len
);
2041 fsname
[fsname_len
- 1] = 0; /* ensure 0 termination */
2047 /***********************************************************************
2048 * GetVolumeInformationA (KERNEL32.@)
2050 BOOL WINAPI
GetVolumeInformationA( LPCSTR root
, LPSTR label
,
2051 DWORD label_len
, DWORD
*serial
,
2052 DWORD
*filename_len
, DWORD
*flags
,
2053 LPSTR fsname
, DWORD fsname_len
)
2055 UNICODE_STRING rootW
;
2056 LPWSTR labelW
, fsnameW
;
2059 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
2060 else rootW
.Buffer
= NULL
;
2061 labelW
= label
? HeapAlloc(GetProcessHeap(), 0, label_len
* sizeof(WCHAR
)) : NULL
;
2062 fsnameW
= fsname
? HeapAlloc(GetProcessHeap(), 0, fsname_len
* sizeof(WCHAR
)) : NULL
;
2064 if ((ret
= GetVolumeInformationW(rootW
.Buffer
, labelW
, label_len
, serial
,
2065 filename_len
, flags
, fsnameW
, fsname_len
)))
2067 if (label
) WideCharToMultiByte(CP_ACP
, 0, labelW
, -1, label
, label_len
, NULL
, NULL
);
2068 if (fsname
) WideCharToMultiByte(CP_ACP
, 0, fsnameW
, -1, fsname
, fsname_len
, NULL
, NULL
);
2071 RtlFreeUnicodeString(&rootW
);
2072 if (labelW
) HeapFree( GetProcessHeap(), 0, labelW
);
2073 if (fsnameW
) HeapFree( GetProcessHeap(), 0, fsnameW
);
2077 /***********************************************************************
2078 * SetVolumeLabelW (KERNEL32.@)
2080 BOOL WINAPI
SetVolumeLabelW( LPCWSTR root
, LPCWSTR volname
)
2084 /* FIXME, SetLastErrors missing */
2086 if (!root
) drive
= DRIVE_GetCurrentDrive();
2089 if ((root
[1]) && (root
[1] != ':'))
2091 WARN("invalid root %s\n", debugstr_w(root
));
2094 drive
= toupperW(root
[0]) - 'A';
2096 if (!DRIVE_IsValid( drive
)) return FALSE
;
2098 /* some copy protection stuff check this */
2099 if (DOSDrives
[drive
].type
== DRIVE_CDROM
) return FALSE
;
2101 strncpyW(DOSDrives
[drive
].label_conf
, volname
, 12);
2102 DOSDrives
[drive
].label_conf
[12 - 1] = 0; /* ensure 0 termination */
2106 /***********************************************************************
2107 * SetVolumeLabelA (KERNEL32.@)
2109 BOOL WINAPI
SetVolumeLabelA(LPCSTR root
, LPCSTR volname
)
2111 UNICODE_STRING rootW
, volnameW
;
2114 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
2115 else rootW
.Buffer
= NULL
;
2116 if (volname
) RtlCreateUnicodeStringFromAsciiz(&volnameW
, volname
);
2117 else volnameW
.Buffer
= NULL
;
2119 ret
= SetVolumeLabelW( rootW
.Buffer
, volnameW
.Buffer
);
2121 RtlFreeUnicodeString(&rootW
);
2122 RtlFreeUnicodeString(&volnameW
);
2126 /***********************************************************************
2127 * GetVolumeNameForVolumeMountPointW (KERNEL32.@)
2129 DWORD WINAPI
GetVolumeNameForVolumeMountPointW(LPWSTR str
, DWORD a
, DWORD b
)
2131 FIXME("(%s, %lx, %lx): stub\n", debugstr_w(str
), a
, b
);