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"
34 #include <sys/types.h>
42 #ifdef HAVE_SYS_PARAM_H
43 # include <sys/param.h>
45 #ifdef STATFS_DEFINED_BY_SYS_VFS
48 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
49 # include <sys/mount.h>
51 # ifdef STATFS_DEFINED_BY_SYS_STATFS
52 # include <sys/statfs.h>
57 #define NONAMELESSUNION
58 #define NONAMELESSSTRUCT
61 #include "wine/winbase16.h" /* for GetCurrentTask */
70 #include "wine/unicode.h"
71 #include "wine/library.h"
72 #include "wine/server.h"
73 #include "wine/debug.h"
75 WINE_DEFAULT_DEBUG_CHANNEL(dosfs
);
76 WINE_DECLARE_DEBUG_CHANNEL(file
);
80 char *root
; /* root dir in Unix format without trailing / */
81 LPWSTR dos_cwd
; /* cwd in DOS format without leading or trailing \ */
82 char *unix_cwd
; /* cwd in Unix format without leading or trailing / */
83 char *device
; /* raw device path */
84 WCHAR label_conf
[12]; /* drive label as cfg'd in wine config */
85 WCHAR label_read
[12]; /* drive label as read from device */
86 DWORD serial_conf
; /* drive serial number as cfg'd in wine config */
87 UINT type
; /* drive type */
88 UINT flags
; /* drive flags */
89 UINT codepage
; /* drive code page */
90 dev_t dev
; /* unix device number */
91 ino_t ino
; /* unix inode number */
95 static const WCHAR DRIVE_Types
[][8] =
97 { 0 }, /* DRIVE_UNKNOWN */
98 { 0 }, /* DRIVE_NO_ROOT_DIR */
99 {'f','l','o','p','p','y',0}, /* DRIVE_REMOVABLE */
100 {'h','d',0}, /* DRIVE_FIXED */
101 {'n','e','t','w','o','r','k',0}, /* DRIVE_REMOTE */
102 {'c','d','r','o','m',0}, /* DRIVE_CDROM */
103 {'r','a','m','d','i','s','k',0} /* DRIVE_RAMDISK */
107 /* Known filesystem types */
115 static const FS_DESCR DRIVE_Filesystems
[] =
117 { {'u','n','i','x',0}, DRIVE_CASE_SENSITIVE
| DRIVE_CASE_PRESERVING
},
118 { {'m','s','d','o','s',0}, DRIVE_SHORT_NAMES
},
119 { {'d','o','s',0}, DRIVE_SHORT_NAMES
},
120 { {'f','a','t',0}, DRIVE_SHORT_NAMES
},
121 { {'v','f','a','t',0}, DRIVE_CASE_PRESERVING
},
122 { {'w','i','n','9','5',0}, DRIVE_CASE_PRESERVING
},
127 static DOSDRIVE DOSDrives
[MAX_DOS_DRIVES
];
128 static int DRIVE_CurDrive
= -1;
130 static HTASK16 DRIVE_LastTask
= 0;
132 /* strdup on the process heap */
133 inline static char *heap_strdup( const char *str
)
135 INT len
= strlen(str
) + 1;
136 LPSTR p
= HeapAlloc( GetProcessHeap(), 0, len
);
137 if (p
) memcpy( p
, str
, len
);
141 extern void CDROM_InitRegistry(int dev
);
143 /***********************************************************************
146 static UINT
DRIVE_GetDriveType( LPCWSTR name
)
150 static const WCHAR TypeW
[] = {'T','y','p','e',0};
151 static const WCHAR hdW
[] = {'h','d',0};
153 PROFILE_GetWineIniString( name
, TypeW
, hdW
, buffer
, 20 );
156 for (i
= 0; i
< sizeof(DRIVE_Types
)/sizeof(DRIVE_Types
[0]); i
++)
158 if (!strcmpiW( buffer
, DRIVE_Types
[i
] )) return i
;
160 MESSAGE("%s: unknown drive type %s, defaulting to 'hd'.\n",
161 debugstr_w(name
), debugstr_w(buffer
) );
166 /***********************************************************************
169 static UINT
DRIVE_GetFSFlags( LPCWSTR name
, 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("%s: unknown filesystem type %s, defaulting to 'win95'.\n",
176 debugstr_w(name
), debugstr_w(value
) );
177 return DRIVE_CASE_PRESERVING
;
181 /***********************************************************************
186 int i
, len
, count
= 0;
187 WCHAR name
[] = {'D','r','i','v','e',' ','A',0};
188 WCHAR drive_env
[] = {'=','A',':',0};
189 WCHAR path
[MAX_PATHNAME_LEN
];
191 struct stat drive_stat_buffer
;
194 static const WCHAR PathW
[] = {'P','a','t','h',0};
195 static const WCHAR empty_strW
[] = { 0 };
196 static const WCHAR CodepageW
[] = {'C','o','d','e','p','a','g','e',0};
197 static const WCHAR LabelW
[] = {'L','a','b','e','l',0};
198 static const WCHAR SerialW
[] = {'S','e','r','i','a','l',0};
199 static const WCHAR zeroW
[] = {'0',0};
200 static const WCHAR def_serialW
[] = {'1','2','3','4','5','6','7','8',0};
201 static const WCHAR FilesystemW
[] = {'F','i','l','e','s','y','s','t','e','m',0};
202 static const WCHAR win95W
[] = {'w','i','n','9','5',0};
203 static const WCHAR DeviceW
[] = {'D','e','v','i','c','e',0};
204 static const WCHAR ReadVolInfoW
[] = {'R','e','a','d','V','o','l','I','n','f','o',0};
205 static const WCHAR FailReadOnlyW
[] = {'F','a','i','l','R','e','a','d','O','n','l','y',0};
206 static const WCHAR driveC_labelW
[] = {'D','r','i','v','e',' ','C',' ',' ',' ',' ',0};
208 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, name
[6]++, drive
++)
210 PROFILE_GetWineIniString( name
, PathW
, empty_strW
, path
, MAX_PATHNAME_LEN
);
213 /* Get the code page number */
214 PROFILE_GetWineIniString( name
, CodepageW
, zeroW
, /* 0 == CP_ACP */
216 drive
->codepage
= strtolW( buffer
, NULL
, 10 );
218 p
= path
+ strlenW(path
) - 1;
219 while ((p
> path
) && (*p
== '/')) *p
-- = '\0';
223 len
= WideCharToMultiByte(drive
->codepage
, 0, path
, -1, NULL
, 0, NULL
, NULL
);
224 drive
->root
= HeapAlloc(GetProcessHeap(), 0, len
);
225 WideCharToMultiByte(drive
->codepage
, 0, path
, -1, drive
->root
, len
, NULL
, NULL
);
229 /* relative paths are relative to config dir */
230 const char *config
= wine_get_config_dir();
231 len
= strlen(config
);
232 len
+= WideCharToMultiByte(drive
->codepage
, 0, path
, -1, NULL
, 0, NULL
, NULL
) + 2;
233 drive
->root
= HeapAlloc( GetProcessHeap(), 0, len
);
234 len
-= sprintf( drive
->root
, "%s/", config
);
235 WideCharToMultiByte(drive
->codepage
, 0, path
, -1, drive
->root
+ strlen(drive
->root
), len
, NULL
, NULL
);
238 if (stat( drive
->root
, &drive_stat_buffer
))
240 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
241 drive
->root
, strerror(errno
), 'A' + i
);
242 HeapFree( GetProcessHeap(), 0, drive
->root
);
246 if (!S_ISDIR(drive_stat_buffer
.st_mode
))
248 MESSAGE("%s is not a directory, ignoring drive %c:\n",
249 drive
->root
, 'A' + i
);
250 HeapFree( GetProcessHeap(), 0, drive
->root
);
255 drive
->dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(drive
->dos_cwd
[0]));
256 drive
->unix_cwd
= heap_strdup( "" );
257 drive
->type
= DRIVE_GetDriveType( name
);
258 drive
->device
= NULL
;
260 drive
->dev
= drive_stat_buffer
.st_dev
;
261 drive
->ino
= drive_stat_buffer
.st_ino
;
263 /* Get the drive label */
264 PROFILE_GetWineIniString( name
, LabelW
, empty_strW
, drive
->label_conf
, 12 );
265 if ((len
= strlenW(drive
->label_conf
)) < 11)
267 /* Pad label with spaces */
268 while(len
< 11) drive
->label_conf
[len
++] = ' ';
269 drive
->label_conf
[11] = '\0';
272 /* Get the serial number */
273 PROFILE_GetWineIniString( name
, SerialW
, def_serialW
, buffer
, 80 );
274 drive
->serial_conf
= strtoulW( buffer
, NULL
, 16 );
276 /* Get the filesystem type */
277 PROFILE_GetWineIniString( name
, FilesystemW
, win95W
, buffer
, 80 );
278 drive
->flags
= DRIVE_GetFSFlags( name
, buffer
);
281 PROFILE_GetWineIniString( name
, DeviceW
, empty_strW
, buffer
, 80 );
285 len
= WideCharToMultiByte(CP_ACP
, 0, buffer
, -1, NULL
, 0, NULL
, NULL
);
286 drive
->device
= HeapAlloc(GetProcessHeap(), 0, len
);
287 WideCharToMultiByte(drive
->codepage
, 0, buffer
, -1, drive
->device
, len
, NULL
, NULL
);
289 if (PROFILE_GetWineIniBool( name
, ReadVolInfoW
, 1))
290 drive
->flags
|= DRIVE_READ_VOL_INFO
;
292 if (drive
->type
== DRIVE_CDROM
)
294 if ((cd_fd
= open(drive
->device
, O_RDONLY
|O_NONBLOCK
)) != -1)
296 CDROM_InitRegistry(cd_fd
);
302 /* Get the FailReadOnly flag */
303 if (PROFILE_GetWineIniBool( name
, FailReadOnlyW
, 0 ))
304 drive
->flags
|= DRIVE_FAIL_READ_ONLY
;
306 /* Make the first hard disk the current drive */
307 if ((DRIVE_CurDrive
== -1) && (drive
->type
== DRIVE_FIXED
))
311 TRACE("%s: path=%s type=%s label=%s serial=%08lx "
312 "flags=%08x codepage=%u dev=%x ino=%x\n",
313 debugstr_w(name
), drive
->root
, debugstr_w(DRIVE_Types
[drive
->type
]),
314 debugstr_w(drive
->label_conf
), drive
->serial_conf
, drive
->flags
,
315 drive
->codepage
, (int)drive
->dev
, (int)drive
->ino
);
317 else WARN("%s: not defined\n", debugstr_w(name
) );
322 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
323 /* Create a C drive pointing to Unix root dir */
324 DOSDrives
[2].root
= heap_strdup( "/" );
325 DOSDrives
[2].dos_cwd
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(DOSDrives
[2].dos_cwd
[0]));
326 DOSDrives
[2].unix_cwd
= heap_strdup( "" );
327 strcpyW( DOSDrives
[2].label_conf
, driveC_labelW
);
328 DOSDrives
[2].serial_conf
= 12345678;
329 DOSDrives
[2].type
= DRIVE_FIXED
;
330 DOSDrives
[2].device
= NULL
;
331 DOSDrives
[2].flags
= 0;
335 /* Make sure the current drive is valid */
336 if (DRIVE_CurDrive
== -1)
338 for (i
= 0, drive
= DOSDrives
; i
< MAX_DOS_DRIVES
; i
++, drive
++)
340 if (drive
->root
&& !(drive
->flags
& DRIVE_DISABLED
))
348 /* get current working directory info for all drives */
349 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++, drive_env
[1]++)
351 if (!GetEnvironmentVariableW(drive_env
, path
, MAX_PATHNAME_LEN
)) continue;
353 if (toupperW(path
[0]) != drive_env
[1] || path
[1] != ':') continue;
354 DRIVE_Chdir( i
, path
+ 2 );
360 /***********************************************************************
363 int DRIVE_IsValid( int drive
)
365 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
366 return (DOSDrives
[drive
].root
&&
367 !(DOSDrives
[drive
].flags
& DRIVE_DISABLED
));
371 /***********************************************************************
372 * DRIVE_GetCurrentDrive
374 int DRIVE_GetCurrentDrive(void)
376 TDB
*pTask
= TASK_GetCurrent();
377 if (pTask
&& (pTask
->curdrive
& 0x80)) return pTask
->curdrive
& ~0x80;
378 return DRIVE_CurDrive
;
382 /***********************************************************************
383 * DRIVE_SetCurrentDrive
385 int DRIVE_SetCurrentDrive( int drive
)
387 TDB
*pTask
= TASK_GetCurrent();
388 if (!DRIVE_IsValid( drive
))
390 SetLastError( ERROR_INVALID_DRIVE
);
393 TRACE("%c:\n", 'A' + drive
);
394 DRIVE_CurDrive
= drive
;
395 if (pTask
) pTask
->curdrive
= drive
| 0x80;
400 /***********************************************************************
401 * DRIVE_FindDriveRoot
403 * Find a drive for which the root matches the beginning of the given path.
404 * This can be used to translate a Unix path into a drive + DOS path.
405 * Return value is the drive, or -1 on error. On success, path is modified
406 * to point to the beginning of the DOS path.
408 * Note: path must be in the encoding of the underlying Unix file system.
410 int DRIVE_FindDriveRoot( const char **path
)
412 /* Starting with the full path, check if the device and inode match any of
413 * the wine 'drives'. If not then remove the last path component and try
414 * again. If the last component was a '..' then skip a normal component
415 * since it's a directory that's ascended back out of.
417 int drive
, level
, len
;
418 char buffer
[MAX_PATHNAME_LEN
];
422 strcpy( buffer
, *path
);
423 while ((p
= strchr( buffer
, '\\' )) != NULL
)
425 len
= strlen(buffer
);
427 /* strip off trailing slashes */
428 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
433 if (stat( buffer
, &st
) == 0 && S_ISDIR( st
.st_mode
))
435 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
437 if (!DOSDrives
[drive
].root
||
438 (DOSDrives
[drive
].flags
& DRIVE_DISABLED
))
441 if ((DOSDrives
[drive
].dev
== st
.st_dev
) &&
442 (DOSDrives
[drive
].ino
== st
.st_ino
))
444 if (len
== 1) len
= 0; /* preserve root slash in returned path */
445 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
446 *path
, 'A' + drive
, buffer
, *path
+ len
);
448 if (!**path
) *path
= "\\";
453 if (len
<= 1) return -1; /* reached root */
458 /* find start of the last path component */
459 while (len
> 1 && buffer
[len
- 1] != '/') len
--;
460 if (!buffer
[len
]) break; /* empty component -> reached root */
461 /* does removing it take us up a level? */
462 if (strcmp( buffer
+ len
, "." ) != 0)
463 level
+= strcmp( buffer
+ len
, ".." ) ? 1 : -1;
465 /* strip off trailing slashes */
466 while (len
> 1 && buffer
[len
- 1] == '/') buffer
[--len
] = 0;
472 /***********************************************************************
473 * DRIVE_FindDriveRootW
475 * Unicode version of DRIVE_FindDriveRoot.
477 int DRIVE_FindDriveRootW( LPCWSTR
*path
)
479 int drive
, rootdrive
= -1;
480 char buffer
[MAX_PATHNAME_LEN
];
482 int len
, match_len
= -1;
484 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
486 if (!DOSDrives
[drive
].root
||
487 (DOSDrives
[drive
].flags
& DRIVE_DISABLED
)) continue;
489 WideCharToMultiByte(DOSDrives
[drive
].codepage
, 0, *path
, -1,
490 buffer
, MAX_PATHNAME_LEN
, NULL
, NULL
);
492 len
= strlen(DOSDrives
[drive
].root
);
493 if(strncmp(DOSDrives
[drive
].root
, buffer
, len
))
495 if(len
<= match_len
) continue;
504 TRACE("%s -> drive %c:, root='%s', name=%s\n",
505 buffer
, 'A' + rootdrive
, DOSDrives
[rootdrive
].root
, debugstr_w(*path
) );
511 /***********************************************************************
514 const char * DRIVE_GetRoot( int drive
)
516 if (!DRIVE_IsValid( drive
)) return NULL
;
517 return DOSDrives
[drive
].root
;
521 /***********************************************************************
524 LPCWSTR
DRIVE_GetDosCwd( int drive
)
526 TDB
*pTask
= TASK_GetCurrent();
527 if (!DRIVE_IsValid( drive
)) return NULL
;
529 /* Check if we need to change the directory to the new task. */
530 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
531 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
532 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
534 static const WCHAR rootW
[] = {'\\',0};
535 WCHAR curdirW
[MAX_PATH
];
536 MultiByteToWideChar(CP_ACP
, 0, pTask
->curdir
, -1, curdirW
, MAX_PATH
);
537 /* Perform the task-switch */
538 if (!DRIVE_Chdir( drive
, curdirW
)) DRIVE_Chdir( drive
, rootW
);
539 DRIVE_LastTask
= GetCurrentTask();
541 return DOSDrives
[drive
].dos_cwd
;
545 /***********************************************************************
548 const char * DRIVE_GetUnixCwd( int drive
)
550 TDB
*pTask
= TASK_GetCurrent();
551 if (!DRIVE_IsValid( drive
)) return NULL
;
553 /* Check if we need to change the directory to the new task. */
554 if (pTask
&& (pTask
->curdrive
& 0x80) && /* The task drive is valid */
555 ((pTask
->curdrive
& ~0x80) == drive
) && /* and it's the one we want */
556 (DRIVE_LastTask
!= GetCurrentTask())) /* and the task changed */
558 static const WCHAR rootW
[] = {'\\',0};
559 WCHAR curdirW
[MAX_PATH
];
560 MultiByteToWideChar(CP_ACP
, 0, pTask
->curdir
, -1, curdirW
, MAX_PATH
);
561 /* Perform the task-switch */
562 if (!DRIVE_Chdir( drive
, curdirW
)) DRIVE_Chdir( drive
, rootW
);
563 DRIVE_LastTask
= GetCurrentTask();
565 return DOSDrives
[drive
].unix_cwd
;
569 /***********************************************************************
572 const char * DRIVE_GetDevice( int drive
)
574 return (DRIVE_IsValid( drive
)) ? DOSDrives
[drive
].device
: NULL
;
577 /******************************************************************
578 * static WORD CDROM_Data_FindBestVoldesc
582 static WORD
CDROM_Data_FindBestVoldesc(int fd
)
584 BYTE cur_vd_type
, max_vd_type
= 0;
585 unsigned int offs
, best_offs
= 0, extra_offs
= 0;
588 for (offs
= 0x8000; offs
<= 0x9800; offs
+= 0x800)
590 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
591 * the volume label is displaced forward by 8
593 lseek(fd
, offs
+ 11, SEEK_SET
); /* check for non-ISO9660 signature */
595 if ((sig
[0] == 'R') && (sig
[1] == 'O') && (sig
[2]=='M'))
599 lseek(fd
, offs
+ extra_offs
, SEEK_SET
);
600 read(fd
, &cur_vd_type
, 1);
601 if (cur_vd_type
== 0xff) /* voldesc set terminator */
603 if (cur_vd_type
> max_vd_type
)
605 max_vd_type
= cur_vd_type
;
606 best_offs
= offs
+ extra_offs
;
612 /***********************************************************************
613 * DRIVE_ReadSuperblock
616 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
617 * to check, that they are writing on a FAT filesystem !
619 int DRIVE_ReadSuperblock (int drive
, char * buff
)
621 #define DRIVE_SUPER 96
626 if (memset(buff
,0,DRIVE_SUPER
)!=buff
) return -1;
627 if ((fd
=open(DOSDrives
[drive
].device
,O_RDONLY
)) == -1)
630 if (!DOSDrives
[drive
].device
)
631 ERR("No device configured for drive %c: !\n", 'A'+drive
);
633 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives
[drive
].device
, 'A'+drive
,
634 (stat(DOSDrives
[drive
].device
, &st
)) ?
635 "not available or symlink not valid ?" : "no permission");
636 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
637 PROFILE_UsageWineIni();
641 switch(DOSDrives
[drive
].type
)
643 case DRIVE_REMOVABLE
:
648 offs
= CDROM_Data_FindBestVoldesc(fd
);
655 if ((offs
) && (lseek(fd
,offs
,SEEK_SET
)!=offs
))
660 if (read(fd
,buff
,DRIVE_SUPER
)!=DRIVE_SUPER
)
666 switch(DOSDrives
[drive
].type
)
668 case DRIVE_REMOVABLE
:
670 if ((buff
[0x26]!=0x29) || /* Check for FAT present */
671 /* FIXME: do really all FAT have their name beginning with
672 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
673 memcmp( buff
+0x36,"FAT",3))
675 ERR("The filesystem is not FAT !! (device=%s)\n",
676 DOSDrives
[drive
].device
);
682 if (strncmp(&buff
[1],"CD001",5)) /* Check for iso9660 present */
687 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
701 /***********************************************************************
702 * DRIVE_WriteSuperblockEntry
705 * We are writing as little as possible (ie. not the whole SuperBlock)
706 * not to interfere with kernel. The drive can be mounted !
708 int DRIVE_WriteSuperblockEntry (int drive
, off_t ofs
, size_t len
, char * buff
)
712 if ((fd
=open(DOSDrives
[drive
].device
,O_WRONLY
))==-1)
714 ERR("Cannot open the device %s (for writing)\n",
715 DOSDrives
[drive
].device
);
718 if (lseek(fd
,ofs
,SEEK_SET
)!=ofs
)
720 ERR("lseek failed on device %s !\n",
721 DOSDrives
[drive
].device
);
725 if (write(fd
,buff
,len
)!=len
)
728 ERR("Cannot write on %s !\n",
729 DOSDrives
[drive
].device
);
735 /******************************************************************
736 * static HANDLE CDROM_Open
740 static HANDLE
CDROM_Open(int drive
)
742 WCHAR root
[] = {'\\','\\','.','\\','A',':',0};
744 return CreateFileW(root
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, 0);
747 /**************************************************************************
748 * CDROM_Data_GetLabel [internal]
750 DWORD
CDROM_Data_GetLabel(int drive
, WCHAR
*label
)
752 #define LABEL_LEN 32+1
753 int dev
= open(DOSDrives
[drive
].device
, O_RDONLY
|O_NONBLOCK
);
754 WORD offs
= CDROM_Data_FindBestVoldesc(dev
);
755 WCHAR label_read
[LABEL_LEN
]; /* Unicode possible, too */
756 DWORD unicode_id
= 0;
760 if ((lseek(dev
, offs
+0x58, SEEK_SET
) == offs
+0x58)
761 && (read(dev
, &unicode_id
, 3) == 3))
763 int ver
= (unicode_id
& 0xff0000) >> 16;
765 if ((lseek(dev
, offs
+0x28, SEEK_SET
) != offs
+0x28)
766 || (read(dev
, &label_read
, LABEL_LEN
) != LABEL_LEN
))
770 if ((LOWORD(unicode_id
) == 0x2f25) /* Unicode ID */
771 && ((ver
== 0x40) || (ver
== 0x43) || (ver
== 0x45)))
772 { /* yippee, unicode */
775 for (i
=0; i
<LABEL_LEN
;i
++)
776 { /* Motorola -> Intel Unicode conversion :-\ */
778 label_read
[i
] = (ch
<< 8) | (ch
>> 8);
780 strncpyW(label
, label_read
, 11);
785 MultiByteToWideChar(DOSDrives
[drive
].codepage
, 0, (LPSTR
)label_read
, -1, label
, 11);
793 ERR("error reading label !\n");
797 /**************************************************************************
798 * CDROM_GetLabel [internal]
800 static DWORD
CDROM_GetLabel(int drive
, WCHAR
*label
)
802 HANDLE h
= CDROM_Open(drive
);
807 if (!h
|| !DeviceIoControl(h
, IOCTL_CDROM_DISK_TYPE
, NULL
, 0, &cdd
, sizeof(cdd
), &br
, 0))
810 switch (cdd
.DiskData
& 0x03)
812 case CDROM_DISK_DATA_TRACK
:
813 if (!CDROM_Data_GetLabel(drive
, label
))
816 case CDROM_DISK_AUDIO_TRACK
:
818 static const WCHAR audioCD
[] = {'A','u','d','i','o',' ','C','D',' ',' ',' ',0};
819 strcpyW(label
, audioCD
);
822 case CDROM_DISK_DATA_TRACK
|CDROM_DISK_AUDIO_TRACK
:
823 FIXME("Need to get the label of a mixed mode CD: not implemented yet !\n");
829 TRACE("CD: label is %s\n", debugstr_w(label
));
833 /***********************************************************************
836 LPCWSTR
DRIVE_GetLabel( int drive
)
839 char buff
[DRIVE_SUPER
];
842 if (!DRIVE_IsValid( drive
)) return NULL
;
843 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
845 read
= CDROM_GetLabel(drive
, DOSDrives
[drive
].label_read
);
848 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
850 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
851 ERR("Invalid or unreadable superblock on %s (%c:).\n",
852 DOSDrives
[drive
].device
, (char)(drive
+'A'));
854 if (DOSDrives
[drive
].type
== DRIVE_REMOVABLE
||
855 DOSDrives
[drive
].type
== DRIVE_FIXED
)
858 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
860 MultiByteToWideChar(DOSDrives
[drive
].codepage
, 0, buff
+offs
, 11,
861 DOSDrives
[drive
].label_read
, 11);
862 DOSDrives
[drive
].label_read
[11]='\0';
868 DOSDrives
[drive
].label_read
: DOSDrives
[drive
].label_conf
;
871 #define CDFRAMES_PERSEC 75
872 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
873 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
874 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
876 /**************************************************************************
877 * CDROM_Audio_GetSerial [internal]
879 static DWORD
CDROM_Audio_GetSerial(HANDLE h
)
881 unsigned long serial
= 0;
884 DWORD dwStart
, dwEnd
, br
;
887 if (!DeviceIoControl(h
, IOCTL_CDROM_READ_TOC
, NULL
, 0, &toc
, sizeof(toc
), &br
, 0))
891 * wMagic collects the wFrames from track 1
892 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
894 * There it is collected for correcting the serial when there are less than
897 wMagic
= toc
.TrackData
[0].Address
[2];
898 dwStart
= FRAME_OF_TOC(toc
, toc
.FirstTrack
);
900 for (i
= 0; i
<= toc
.LastTrack
- toc
.FirstTrack
; i
++) {
901 serial
+= (toc
.TrackData
[i
].Address
[0] << 16) |
902 (toc
.TrackData
[i
].Address
[1] << 8) | toc
.TrackData
[i
].Address
[2];
904 dwEnd
= FRAME_OF_TOC(toc
, toc
.LastTrack
+ 1);
906 if (toc
.LastTrack
- toc
.FirstTrack
+ 1 < 3)
907 serial
+= wMagic
+ (dwEnd
- dwStart
);
912 /**************************************************************************
913 * CDROM_Data_GetSerial [internal]
915 static DWORD
CDROM_Data_GetSerial(int drive
)
917 int dev
= open(DOSDrives
[drive
].device
, O_RDONLY
|O_NONBLOCK
);
923 BYTE b0
= 0, b1
= 1, b2
= 2, b3
= 3;
926 if (dev
== -1) return 0;
927 offs
= CDROM_Data_FindBestVoldesc(dev
);
936 lseek(dev
, offs
, SEEK_SET
);
937 read(dev
, buf
, 2048);
939 * OK, another braindead one... argh. Just believe it.
940 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
941 * It's true and nobody will ever be able to change it.
943 ovi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOA
);
945 if ((ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) && (ovi
.dwMajorVersion
>= 4))
947 b0
= 3; b1
= 2; b2
= 1; b3
= 0;
949 for (i
= 0; i
< 2048; i
+= 4)
951 /* DON'T optimize this into DWORD !! (breaks overflow) */
952 serial
.p
[b0
] += buf
[i
+b0
];
953 serial
.p
[b1
] += buf
[i
+b1
];
954 serial
.p
[b2
] += buf
[i
+b2
];
955 serial
.p
[b3
] += buf
[i
+b3
];
962 /**************************************************************************
963 * CDROM_GetSerial [internal]
965 static DWORD
CDROM_GetSerial(int drive
)
968 HANDLE h
= CDROM_Open(drive
);
972 if (!h
|| ! !DeviceIoControl(h
, IOCTL_CDROM_DISK_TYPE
, NULL
, 0, &cdd
, sizeof(cdd
), &br
, 0))
975 switch (cdd
.DiskData
& 0x03)
977 case CDROM_DISK_DATA_TRACK
:
978 /* hopefully a data CD */
979 serial
= CDROM_Data_GetSerial(drive
);
981 case CDROM_DISK_AUDIO_TRACK
:
983 case CDROM_DISK_DATA_TRACK
|CDROM_DISK_AUDIO_TRACK
:
984 serial
= CDROM_Audio_GetSerial(h
);
991 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial
), LOWORD(serial
));
998 /***********************************************************************
999 * DRIVE_GetSerialNumber
1001 DWORD
DRIVE_GetSerialNumber( int drive
)
1004 char buff
[DRIVE_SUPER
];
1006 TRACE("drive %d, type = %d\n", drive
, DOSDrives
[drive
].type
);
1008 if (!DRIVE_IsValid( drive
)) return 0;
1010 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
1012 switch(DOSDrives
[drive
].type
)
1014 case DRIVE_REMOVABLE
:
1016 if (DRIVE_ReadSuperblock(drive
,(char *) buff
))
1017 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
1018 " Maybe not FAT?\n" ,
1019 DOSDrives
[drive
].device
, 'A'+drive
);
1021 serial
= *((DWORD
*)(buff
+0x27));
1024 serial
= CDROM_GetSerial(drive
);
1027 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive
+'A');
1031 return (serial
) ? serial
: DOSDrives
[drive
].serial_conf
;
1035 /***********************************************************************
1036 * DRIVE_SetSerialNumber
1038 int DRIVE_SetSerialNumber( int drive
, DWORD serial
)
1040 char buff
[DRIVE_SUPER
];
1042 if (!DRIVE_IsValid( drive
)) return 0;
1044 if (DOSDrives
[drive
].flags
& DRIVE_READ_VOL_INFO
)
1046 if ((DOSDrives
[drive
].type
!= DRIVE_REMOVABLE
) &&
1047 (DOSDrives
[drive
].type
!= DRIVE_FIXED
)) return 0;
1048 /* check, if the drive has a FAT filesystem */
1049 if (DRIVE_ReadSuperblock(drive
, buff
)) return 0;
1050 if (DRIVE_WriteSuperblockEntry(drive
, 0x27, 4, (char *) &serial
)) return 0;
1054 if (DOSDrives
[drive
].type
== DRIVE_CDROM
) return 0;
1055 DOSDrives
[drive
].serial_conf
= serial
;
1060 /***********************************************************************
1063 static UINT
DRIVE_GetType( int drive
)
1065 if (!DRIVE_IsValid( drive
)) return DRIVE_NO_ROOT_DIR
;
1066 return DOSDrives
[drive
].type
;
1070 /***********************************************************************
1073 UINT
DRIVE_GetFlags( int drive
)
1075 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
1076 return DOSDrives
[drive
].flags
;
1079 /***********************************************************************
1082 UINT
DRIVE_GetCodepage( int drive
)
1084 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
)) return 0;
1085 return DOSDrives
[drive
].codepage
;
1089 /***********************************************************************
1092 int DRIVE_Chdir( int drive
, LPCWSTR path
)
1094 DOS_FULL_NAME full_name
;
1095 WCHAR buffer
[MAX_PATHNAME_LEN
];
1097 BY_HANDLE_FILE_INFORMATION info
;
1098 TDB
*pTask
= TASK_GetCurrent();
1100 buffer
[0] = 'A' + drive
;
1103 TRACE("(%s,%s)\n", debugstr_w(buffer
), debugstr_w(path
) );
1104 strncpyW( buffer
+ 2, path
, MAX_PATHNAME_LEN
- 2 );
1105 buffer
[MAX_PATHNAME_LEN
- 1] = 0; /* ensure 0 termination */
1107 if (!DOSFS_GetFullName( buffer
, TRUE
, &full_name
)) return 0;
1108 if (!FILE_Stat( full_name
.long_name
, &info
, NULL
)) return 0;
1109 if (!(info
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
1111 SetLastError( ERROR_FILE_NOT_FOUND
);
1114 unix_cwd
= full_name
.long_name
+ strlen( DOSDrives
[drive
].root
);
1115 while (*unix_cwd
== '/') unix_cwd
++;
1117 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
1118 'A' + drive
, unix_cwd
, debugstr_w(full_name
.short_name
+ 3) );
1120 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].dos_cwd
);
1121 HeapFree( GetProcessHeap(), 0, DOSDrives
[drive
].unix_cwd
);
1122 DOSDrives
[drive
].dos_cwd
= HeapAlloc(GetProcessHeap(), 0, (strlenW(full_name
.short_name
) - 2) * sizeof(WCHAR
));
1123 strcpyW(DOSDrives
[drive
].dos_cwd
, full_name
.short_name
+ 3);
1124 DOSDrives
[drive
].unix_cwd
= heap_strdup( unix_cwd
);
1126 if (pTask
&& (pTask
->curdrive
& 0x80) &&
1127 ((pTask
->curdrive
& ~0x80) == drive
))
1129 WideCharToMultiByte(CP_ACP
, 0, full_name
.short_name
+ 2, -1,
1130 pTask
->curdir
, sizeof(pTask
->curdir
), NULL
, NULL
);
1131 DRIVE_LastTask
= GetCurrentTask();
1137 /***********************************************************************
1140 int DRIVE_Disable( int drive
)
1142 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
1144 SetLastError( ERROR_INVALID_DRIVE
);
1147 DOSDrives
[drive
].flags
|= DRIVE_DISABLED
;
1152 /***********************************************************************
1155 int DRIVE_Enable( int drive
)
1157 if ((drive
< 0) || (drive
>= MAX_DOS_DRIVES
) || !DOSDrives
[drive
].root
)
1159 SetLastError( ERROR_INVALID_DRIVE
);
1162 DOSDrives
[drive
].flags
&= ~DRIVE_DISABLED
;
1167 /***********************************************************************
1168 * DRIVE_SetLogicalMapping
1170 int DRIVE_SetLogicalMapping ( int existing_drive
, int new_drive
)
1172 /* If new_drive is already valid, do nothing and return 0
1173 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1175 DOSDRIVE
*old
, *new;
1177 old
= DOSDrives
+ existing_drive
;
1178 new = DOSDrives
+ new_drive
;
1180 if ((existing_drive
< 0) || (existing_drive
>= MAX_DOS_DRIVES
) ||
1182 (new_drive
< 0) || (new_drive
>= MAX_DOS_DRIVES
))
1184 SetLastError( ERROR_INVALID_DRIVE
);
1190 TRACE("Can't map drive %c: to already existing drive %c:\n",
1191 'A' + existing_drive
, 'A' + new_drive
);
1192 /* it is already mapped there, so return success */
1193 if (!strcmp(old
->root
,new->root
))
1198 new->root
= heap_strdup( old
->root
);
1199 new->dos_cwd
= HeapAlloc(GetProcessHeap(), 0, (strlenW(old
->dos_cwd
) + 1) * sizeof(WCHAR
));
1200 strcpyW(new->dos_cwd
, old
->dos_cwd
);
1201 new->unix_cwd
= heap_strdup( old
->unix_cwd
);
1202 new->device
= heap_strdup( old
->device
);
1203 memcpy ( new->label_conf
, old
->label_conf
, 12 );
1204 memcpy ( new->label_read
, old
->label_read
, 12 );
1205 new->serial_conf
= old
->serial_conf
;
1206 new->type
= old
->type
;
1207 new->flags
= old
->flags
;
1208 new->dev
= old
->dev
;
1209 new->ino
= old
->ino
;
1211 TRACE("Drive %c: is now equal to drive %c:\n",
1212 'A' + new_drive
, 'A' + existing_drive
);
1218 /***********************************************************************
1221 * Open the drive raw device and return a Unix fd (or -1 on error).
1223 int DRIVE_OpenDevice( int drive
, int flags
)
1225 if (!DRIVE_IsValid( drive
)) return -1;
1226 return open( DOSDrives
[drive
].device
, flags
);
1230 /***********************************************************************
1233 * Read raw sectors from a device
1235 int DRIVE_RawRead(BYTE drive
, DWORD begin
, DWORD nr_sect
, BYTE
*dataptr
, BOOL fake_success
)
1239 if ((fd
= DRIVE_OpenDevice( drive
, O_RDONLY
)) != -1)
1241 lseek( fd
, begin
* 512, SEEK_SET
);
1242 /* FIXME: check errors */
1243 read( fd
, dataptr
, nr_sect
* 512 );
1248 memset(dataptr
, 0, nr_sect
* 512);
1251 if (begin
== 0 && nr_sect
> 1) *(dataptr
+ 512) = 0xf8;
1252 if (begin
== 1) *dataptr
= 0xf8;
1261 /***********************************************************************
1264 * Write raw sectors to a device
1266 int DRIVE_RawWrite(BYTE drive
, DWORD begin
, DWORD nr_sect
, BYTE
*dataptr
, BOOL fake_success
)
1270 if ((fd
= DRIVE_OpenDevice( drive
, O_RDONLY
)) != -1)
1272 lseek( fd
, begin
* 512, SEEK_SET
);
1273 /* FIXME: check errors */
1274 write( fd
, dataptr
, nr_sect
* 512 );
1278 if (!(fake_success
))
1285 /***********************************************************************
1286 * DRIVE_GetFreeSpace
1288 static int DRIVE_GetFreeSpace( int drive
, PULARGE_INTEGER size
,
1289 PULARGE_INTEGER available
)
1293 if (!DRIVE_IsValid(drive
))
1295 SetLastError( ERROR_PATH_NOT_FOUND
);
1299 /* FIXME: add autoconf check for this */
1300 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1301 if (statfs( DOSDrives
[drive
].root
, &info
, 0, 0) < 0)
1303 if (statfs( DOSDrives
[drive
].root
, &info
) < 0)
1307 WARN("cannot do statfs(%s)\n", DOSDrives
[drive
].root
);
1311 size
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bsize
, info
.f_blocks
);
1312 #ifdef STATFS_HAS_BAVAIL
1313 available
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bavail
, info
.f_bsize
);
1315 # ifdef STATFS_HAS_BFREE
1316 available
->QuadPart
= RtlEnlargedUnsignedMultiply( info
.f_bfree
, info
.f_bsize
);
1318 # error "statfs has no bfree/bavail member!"
1321 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
1322 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1323 available
->QuadPart
= 0;
1328 /***********************************************************************
1329 * DRIVE_GetCurrentDirectory
1330 * Returns "X:\\path\\etc\\".
1332 * Despite the API description, return required length including the
1333 * terminating null when buffer too small. This is the real behaviour.
1335 static UINT
DRIVE_GetCurrentDirectory( UINT buflen
, LPWSTR buf
)
1338 LPCWSTR dos_cwd
= DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1339 static const WCHAR driveA_rootW
[] = {'A',':','\\',0};
1341 ret
= strlenW(dos_cwd
) + 3; /* length of WHOLE current directory */
1342 if (ret
>= buflen
) return ret
+ 1;
1344 strcpyW( buf
, driveA_rootW
);
1345 buf
[0] += DRIVE_GetCurrentDrive();
1346 strcatW( buf
, dos_cwd
);
1351 /***********************************************************************
1354 * Build the environment array containing the drives' current directories.
1355 * Resulting pointer must be freed with HeapFree.
1357 char *DRIVE_BuildEnv(void)
1360 LPCWSTR cwd
[MAX_DOS_DRIVES
];
1363 for (i
= 0; i
< MAX_DOS_DRIVES
; i
++)
1365 if ((cwd
[i
] = DRIVE_GetDosCwd(i
)) && cwd
[i
][0])
1366 length
+= WideCharToMultiByte(DRIVE_GetCodepage(i
), 0,
1367 cwd
[i
], -1, NULL
, 0, NULL
, NULL
) + 7;
1369 if (!(env
= HeapAlloc( GetProcessHeap(), 0, length
+1 ))) return NULL
;
1370 for (i
= 0, p
= env
; i
< MAX_DOS_DRIVES
; i
++)
1372 if (cwd
[i
] && cwd
[i
][0])
1374 *p
++ = '='; *p
++ = 'A' + i
; *p
++ = ':';
1375 *p
++ = '='; *p
++ = 'A' + i
; *p
++ = ':'; *p
++ = '\\';
1376 WideCharToMultiByte(DRIVE_GetCodepage(i
), 0, cwd
[i
], -1, p
, 0x7fffffff, NULL
, NULL
);
1385 /***********************************************************************
1386 * GetDiskFreeSpace (KERNEL.422)
1388 BOOL16 WINAPI
GetDiskFreeSpace16( LPCSTR root
, LPDWORD cluster_sectors
,
1389 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1390 LPDWORD total_clusters
)
1392 return GetDiskFreeSpaceA( root
, cluster_sectors
, sector_bytes
,
1393 free_clusters
, total_clusters
);
1397 /***********************************************************************
1398 * GetDiskFreeSpaceW (KERNEL32.@)
1400 * Fails if expression resulting from current drive's dir and "root"
1401 * is not a root dir of the target drive.
1403 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1404 * if the corresponding info is unneeded.
1406 * FIXME: needs to support UNC names from Win95 OSR2 on.
1408 * Behaviour under Win95a:
1409 * CurrDir root result
1410 * "E:\\TEST" "E:" FALSE
1414 * "E:\\TEST" "\\" TRUE
1415 * "E:\\TEST" ":\\" FALSE
1416 * "E:\\TEST" "E:\\" TRUE
1417 * "E:\\TEST" "" FALSE
1418 * "E:\\" "" FALSE (!)
1420 * "E:\\TEST" 0x0 TRUE (!)
1421 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1422 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1424 BOOL WINAPI
GetDiskFreeSpaceW( LPCWSTR root
, LPDWORD cluster_sectors
,
1425 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1426 LPDWORD total_clusters
)
1428 int drive
, sec_size
;
1429 ULARGE_INTEGER size
,available
;
1433 TRACE("%s,%p,%p,%p,%p\n", debugstr_w(root
), cluster_sectors
, sector_bytes
,
1434 free_clusters
, total_clusters
);
1436 if (!root
|| root
[0] == '\\' || root
[0] == '/')
1437 drive
= DRIVE_GetCurrentDrive();
1439 if (root
[0] && root
[1] == ':') /* root contains drive tag */
1441 drive
= toupperW(root
[0]) - 'A';
1443 if (path
[0] == '\0')
1445 path
= DRIVE_GetDosCwd(drive
);
1448 SetLastError(ERROR_PATH_NOT_FOUND
);
1453 if (path
[0] == '\\')
1456 if (path
[0]) /* oops, we are in a subdir */
1458 SetLastError(ERROR_INVALID_NAME
);
1465 SetLastError(ERROR_PATH_NOT_FOUND
);
1467 SetLastError(ERROR_INVALID_NAME
);
1471 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1473 /* Cap the size and available at 2GB as per specs. */
1474 if ((size
.s
.HighPart
) ||(size
.s
.LowPart
> 0x7fffffff))
1476 size
.s
.HighPart
= 0;
1477 size
.s
.LowPart
= 0x7fffffff;
1479 if ((available
.s
.HighPart
) ||(available
.s
.LowPart
> 0x7fffffff))
1481 available
.s
.HighPart
=0;
1482 available
.s
.LowPart
= 0x7fffffff;
1484 sec_size
= (DRIVE_GetType(drive
)==DRIVE_CDROM
) ? 2048 : 512;
1485 size
.s
.LowPart
/= sec_size
;
1486 available
.s
.LowPart
/= sec_size
;
1487 /* FIXME: probably have to adjust those variables too for CDFS */
1489 while (cluster_sec
* 65536 < size
.s
.LowPart
) cluster_sec
*= 2;
1491 if (cluster_sectors
)
1492 *cluster_sectors
= cluster_sec
;
1494 *sector_bytes
= sec_size
;
1496 *free_clusters
= available
.s
.LowPart
/ cluster_sec
;
1498 *total_clusters
= size
.s
.LowPart
/ cluster_sec
;
1503 /***********************************************************************
1504 * GetDiskFreeSpaceA (KERNEL32.@)
1506 BOOL WINAPI
GetDiskFreeSpaceA( LPCSTR root
, LPDWORD cluster_sectors
,
1507 LPDWORD sector_bytes
, LPDWORD free_clusters
,
1508 LPDWORD total_clusters
)
1510 UNICODE_STRING rootW
;
1515 if(!RtlCreateUnicodeStringFromAsciiz(&rootW
, root
))
1517 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1522 rootW
.Buffer
= NULL
;
1524 ret
= GetDiskFreeSpaceW(rootW
.Buffer
, cluster_sectors
, sector_bytes
,
1525 free_clusters
, total_clusters
);
1526 RtlFreeUnicodeString(&rootW
);
1532 /***********************************************************************
1533 * GetDiskFreeSpaceExW (KERNEL32.@)
1535 * This function is used to acquire the size of the available and
1536 * total space on a logical volume.
1540 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1541 * detailed error information.
1544 BOOL WINAPI
GetDiskFreeSpaceExW( LPCWSTR root
,
1545 PULARGE_INTEGER avail
,
1546 PULARGE_INTEGER total
,
1547 PULARGE_INTEGER totalfree
)
1550 ULARGE_INTEGER size
,available
;
1552 if (!root
) drive
= DRIVE_GetCurrentDrive();
1554 { /* C: always works for GetDiskFreeSpaceEx */
1555 if ((root
[1]) && ((root
[1] != ':') || (root
[2] && root
[2] != '\\')))
1557 FIXME("there are valid root names which are not supported yet\n");
1558 /* ..like UNC names, for instance. */
1560 WARN("invalid root '%s'\n", debugstr_w(root
));
1563 drive
= toupperW(root
[0]) - 'A';
1566 if (!DRIVE_GetFreeSpace(drive
, &size
, &available
)) return FALSE
;
1570 total
->s
.HighPart
= size
.s
.HighPart
;
1571 total
->s
.LowPart
= size
.s
.LowPart
;
1576 totalfree
->s
.HighPart
= available
.s
.HighPart
;
1577 totalfree
->s
.LowPart
= available
.s
.LowPart
;
1582 if (FIXME_ON(dosfs
))
1584 /* On Windows2000, we need to check the disk quota
1585 allocated for the user owning the calling process. We
1586 don't want to be more obtrusive than necessary with the
1587 FIXME messages, so don't print the FIXME unless Wine is
1588 actually masquerading as Windows2000. */
1591 ovi
.dwOSVersionInfoSize
= sizeof(OSVERSIONINFOA
);
1592 if (GetVersionExA(&ovi
))
1594 if (ovi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
&& ovi
.dwMajorVersion
> 4)
1595 FIXME("no per-user quota support yet\n");
1599 /* Quick hack, should eventually be fixed to work 100% with
1600 Windows2000 (see comment above). */
1601 avail
->s
.HighPart
= available
.s
.HighPart
;
1602 avail
->s
.LowPart
= available
.s
.LowPart
;
1608 /***********************************************************************
1609 * GetDiskFreeSpaceExA (KERNEL32.@)
1611 BOOL WINAPI
GetDiskFreeSpaceExA( LPCSTR root
, PULARGE_INTEGER avail
,
1612 PULARGE_INTEGER total
,
1613 PULARGE_INTEGER totalfree
)
1615 UNICODE_STRING rootW
;
1618 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
1619 else rootW
.Buffer
= NULL
;
1621 ret
= GetDiskFreeSpaceExW( rootW
.Buffer
, avail
, total
, totalfree
);
1623 RtlFreeUnicodeString(&rootW
);
1627 /***********************************************************************
1628 * GetDriveType (KERNEL.136)
1629 * This function returns the type of a drive in Win16.
1630 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1631 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1632 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1633 * do any pseudo-clever changes.
1636 * drivetype DRIVE_xxx
1638 UINT16 WINAPI
GetDriveType16( UINT16 drive
) /* [in] number (NOT letter) of drive */
1640 UINT type
= DRIVE_GetType(drive
);
1641 TRACE("(%c:)\n", 'A' + drive
);
1642 if (type
== DRIVE_CDROM
) type
= DRIVE_REMOTE
;
1647 /***********************************************************************
1648 * GetDriveTypeW (KERNEL32.@)
1650 * Returns the type of the disk drive specified. If root is NULL the
1651 * root of the current directory is used.
1655 * Type of drive (from Win32 SDK):
1657 * DRIVE_UNKNOWN unable to find out anything about the drive
1658 * DRIVE_NO_ROOT_DIR nonexistent root dir
1659 * DRIVE_REMOVABLE the disk can be removed from the machine
1660 * DRIVE_FIXED the disk can not be removed from the machine
1661 * DRIVE_REMOTE network disk
1662 * DRIVE_CDROM CDROM drive
1663 * DRIVE_RAMDISK virtual disk in RAM
1665 UINT WINAPI
GetDriveTypeW(LPCWSTR root
) /* [in] String describing drive */
1668 TRACE("(%s)\n", debugstr_w(root
));
1670 if (NULL
== root
) drive
= DRIVE_GetCurrentDrive();
1673 if ((root
[1]) && (root
[1] != ':'))
1675 WARN("invalid root %s\n", debugstr_w(root
));
1676 return DRIVE_NO_ROOT_DIR
;
1678 drive
= toupperW(root
[0]) - 'A';
1680 return DRIVE_GetType(drive
);
1684 /***********************************************************************
1685 * GetDriveTypeA (KERNEL32.@)
1687 UINT WINAPI
GetDriveTypeA( LPCSTR root
)
1689 UNICODE_STRING rootW
;
1694 if( !RtlCreateUnicodeStringFromAsciiz(&rootW
, root
))
1696 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1701 rootW
.Buffer
= NULL
;
1703 ret
= GetDriveTypeW(rootW
.Buffer
);
1705 RtlFreeUnicodeString(&rootW
);
1711 /***********************************************************************
1712 * GetCurrentDirectory (KERNEL.411)
1714 UINT16 WINAPI
GetCurrentDirectory16( UINT16 buflen
, LPSTR buf
)
1716 WCHAR cur_dirW
[MAX_PATH
];
1718 DRIVE_GetCurrentDirectory(MAX_PATH
, cur_dirW
);
1719 return (UINT16
)WideCharToMultiByte(CP_ACP
, 0, cur_dirW
, -1, buf
, buflen
, NULL
, NULL
);
1723 /***********************************************************************
1724 * GetCurrentDirectoryW (KERNEL32.@)
1726 UINT WINAPI
GetCurrentDirectoryW( UINT buflen
, LPWSTR buf
)
1729 WCHAR longname
[MAX_PATHNAME_LEN
];
1730 WCHAR shortname
[MAX_PATHNAME_LEN
];
1732 ret
= DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN
, shortname
);
1733 if ( ret
> MAX_PATHNAME_LEN
) {
1734 ERR_(file
)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret
);
1737 GetLongPathNameW(shortname
, longname
, MAX_PATHNAME_LEN
);
1738 ret
= strlenW( longname
) + 1;
1739 if (ret
> buflen
) return ret
;
1740 strcpyW(buf
, longname
);
1744 /***********************************************************************
1745 * GetCurrentDirectoryA (KERNEL32.@)
1747 UINT WINAPI
GetCurrentDirectoryA( UINT buflen
, LPSTR buf
)
1749 WCHAR bufferW
[MAX_PATH
];
1752 retW
= GetCurrentDirectoryW(MAX_PATH
, bufferW
);
1756 else if (retW
> MAX_PATH
)
1758 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
1763 ret
= WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, NULL
, 0, NULL
, NULL
);
1766 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buf
, buflen
, NULL
, NULL
);
1767 ret
--; /* length without 0 */
1774 /***********************************************************************
1775 * SetCurrentDirectory (KERNEL.412)
1777 BOOL16 WINAPI
SetCurrentDirectory16( LPCSTR dir
)
1779 return SetCurrentDirectoryA( dir
);
1783 /***********************************************************************
1784 * SetCurrentDirectoryW (KERNEL32.@)
1786 BOOL WINAPI
SetCurrentDirectoryW( LPCWSTR dir
)
1788 int drive
, olddrive
= DRIVE_GetCurrentDrive();
1792 SetLastError(ERROR_INVALID_PARAMETER
);
1795 if (dir
[0] && (dir
[1]==':'))
1797 drive
= toupperW( *dir
) - 'A';
1803 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1804 sets pTask->curdir only if pTask->curdrive is drive */
1805 if (!(DRIVE_SetCurrentDrive( drive
)))
1808 /* FIXME: what about empty strings? Add a \\ ? */
1809 if (!DRIVE_Chdir( drive
, dir
)) {
1810 DRIVE_SetCurrentDrive(olddrive
);
1817 /***********************************************************************
1818 * SetCurrentDirectoryA (KERNEL32.@)
1820 BOOL WINAPI
SetCurrentDirectoryA( LPCSTR dir
)
1822 UNICODE_STRING dirW
;
1827 SetLastError(ERROR_INVALID_PARAMETER
);
1831 if (RtlCreateUnicodeStringFromAsciiz(&dirW
, dir
))
1833 ret
= SetCurrentDirectoryW(dirW
.Buffer
);
1834 RtlFreeUnicodeString(&dirW
);
1837 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1842 /***********************************************************************
1843 * GetLogicalDriveStringsA (KERNEL32.@)
1845 UINT WINAPI
GetLogicalDriveStringsA( UINT len
, LPSTR buffer
)
1849 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1850 if (DRIVE_IsValid(drive
)) count
++;
1851 if ((count
* 4) + 1 <= len
)
1854 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1855 if (DRIVE_IsValid(drive
))
1866 return (count
* 4) + 1; /* account for terminating null */
1867 /* The API tells about these different return values */
1871 /***********************************************************************
1872 * GetLogicalDriveStringsW (KERNEL32.@)
1874 UINT WINAPI
GetLogicalDriveStringsW( UINT len
, LPWSTR buffer
)
1878 for (drive
= count
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1879 if (DRIVE_IsValid(drive
)) count
++;
1880 if (count
* 4 * sizeof(WCHAR
) <= len
)
1883 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1884 if (DRIVE_IsValid(drive
))
1886 *p
++ = (WCHAR
)('a' + drive
);
1893 return count
* 4 * sizeof(WCHAR
);
1897 /***********************************************************************
1898 * GetLogicalDrives (KERNEL32.@)
1900 DWORD WINAPI
GetLogicalDrives(void)
1905 for (drive
= 0; drive
< MAX_DOS_DRIVES
; drive
++)
1907 if ( (DRIVE_IsValid(drive
)) ||
1908 (DOSDrives
[drive
].type
== DRIVE_CDROM
)) /* audio CD is also valid */
1909 ret
|= (1 << drive
);
1915 /***********************************************************************
1916 * GetVolumeInformationW (KERNEL32.@)
1918 BOOL WINAPI
GetVolumeInformationW( LPCWSTR root
, LPWSTR label
,
1919 DWORD label_len
, DWORD
*serial
,
1920 DWORD
*filename_len
, DWORD
*flags
,
1921 LPWSTR fsname
, DWORD fsname_len
)
1926 /* FIXME, SetLastError()s missing */
1928 if (!root
) drive
= DRIVE_GetCurrentDrive();
1931 if (root
[0] && root
[1] != ':')
1933 WARN("invalid root %s\n", debugstr_w(root
));
1936 drive
= toupperW(root
[0]) - 'A';
1938 if (!DRIVE_IsValid( drive
)) return FALSE
;
1939 if (label
&& label_len
)
1941 strncpyW( label
, DRIVE_GetLabel(drive
), label_len
);
1942 label
[label_len
- 1] = 0; /* ensure 0 termination */
1943 cp
= label
+ strlenW(label
);
1944 while (cp
!= label
&& *(cp
-1) == ' ') cp
--;
1947 if (serial
) *serial
= DRIVE_GetSerialNumber(drive
);
1949 /* Set the filesystem information */
1950 /* Note: we only emulate a FAT fs at present */
1953 if (DOSDrives
[drive
].flags
& DRIVE_SHORT_NAMES
)
1956 *filename_len
= 255;
1961 if (DOSDrives
[drive
].flags
& DRIVE_CASE_SENSITIVE
)
1962 *flags
|=FS_CASE_SENSITIVE
;
1963 if (DOSDrives
[drive
].flags
& DRIVE_CASE_PRESERVING
)
1964 *flags
|=FS_CASE_IS_PRESERVED
;
1966 if (fsname
&& fsname_len
)
1968 /* Diablo checks that return code ... */
1969 if (DOSDrives
[drive
].type
== DRIVE_CDROM
)
1971 static const WCHAR cdfsW
[] = {'C','D','F','S',0};
1972 strncpyW( fsname
, cdfsW
, fsname_len
);
1976 static const WCHAR fatW
[] = {'F','A','T',0};
1977 strncpyW( fsname
, fatW
, fsname_len
);
1979 fsname
[fsname_len
- 1] = 0; /* ensure 0 termination */
1985 /***********************************************************************
1986 * GetVolumeInformationA (KERNEL32.@)
1988 BOOL WINAPI
GetVolumeInformationA( LPCSTR root
, LPSTR label
,
1989 DWORD label_len
, DWORD
*serial
,
1990 DWORD
*filename_len
, DWORD
*flags
,
1991 LPSTR fsname
, DWORD fsname_len
)
1993 UNICODE_STRING rootW
;
1994 LPWSTR labelW
, fsnameW
;
1997 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
1998 else rootW
.Buffer
= NULL
;
1999 labelW
= label
? HeapAlloc(GetProcessHeap(), 0, label_len
* sizeof(WCHAR
)) : NULL
;
2000 fsnameW
= fsname
? HeapAlloc(GetProcessHeap(), 0, fsname_len
* sizeof(WCHAR
)) : NULL
;
2002 if ((ret
= GetVolumeInformationW(rootW
.Buffer
, labelW
, label_len
, serial
,
2003 filename_len
, flags
, fsnameW
, fsname_len
)))
2005 if (label
) WideCharToMultiByte(CP_ACP
, 0, labelW
, -1, label
, label_len
, NULL
, NULL
);
2006 if (fsname
) WideCharToMultiByte(CP_ACP
, 0, fsnameW
, -1, fsname
, fsname_len
, NULL
, NULL
);
2009 RtlFreeUnicodeString(&rootW
);
2010 if (labelW
) HeapFree( GetProcessHeap(), 0, labelW
);
2011 if (fsnameW
) HeapFree( GetProcessHeap(), 0, fsnameW
);
2015 /***********************************************************************
2016 * SetVolumeLabelW (KERNEL32.@)
2018 BOOL WINAPI
SetVolumeLabelW( LPCWSTR root
, LPCWSTR volname
)
2022 /* FIXME, SetLastErrors missing */
2024 if (!root
) drive
= DRIVE_GetCurrentDrive();
2027 if ((root
[1]) && (root
[1] != ':'))
2029 WARN("invalid root %s\n", debugstr_w(root
));
2032 drive
= toupperW(root
[0]) - 'A';
2034 if (!DRIVE_IsValid( drive
)) return FALSE
;
2036 /* some copy protection stuff check this */
2037 if (DOSDrives
[drive
].type
== DRIVE_CDROM
) return FALSE
;
2039 strncpyW(DOSDrives
[drive
].label_conf
, volname
, 12);
2040 DOSDrives
[drive
].label_conf
[12 - 1] = 0; /* ensure 0 termination */
2044 /***********************************************************************
2045 * SetVolumeLabelA (KERNEL32.@)
2047 BOOL WINAPI
SetVolumeLabelA(LPCSTR root
, LPCSTR volname
)
2049 UNICODE_STRING rootW
, volnameW
;
2052 if (root
) RtlCreateUnicodeStringFromAsciiz(&rootW
, root
);
2053 else rootW
.Buffer
= NULL
;
2054 if (volname
) RtlCreateUnicodeStringFromAsciiz(&volnameW
, volname
);
2055 else volnameW
.Buffer
= NULL
;
2057 ret
= SetVolumeLabelW( rootW
.Buffer
, volnameW
.Buffer
);
2059 RtlFreeUnicodeString(&rootW
);
2060 RtlFreeUnicodeString(&volnameW
);
2064 /***********************************************************************
2065 * GetVolumeNameForVolumeMountPointW (KERNEL32.@)
2067 DWORD WINAPI
GetVolumeNameForVolumeMountPointW(LPWSTR str
, DWORD a
, DWORD b
)
2069 FIXME("(%s, %lx, %lx): stub\n", debugstr_w(str
), a
, b
);