Add decoding logic for VGA indexed registers.
[wine/multimedia.git] / files / drive.c
blob37da65e370ba051406fb930196fdc999bf0f3b46
1 /*
2 * DOS drives handling functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
7 * Label & serial number read support.
8 * (c) 1999 Petr Tomasek <tomasek@etf.cuni.cz>
9 * (c) 2000 Andreas Mohr (changes)
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "config.h"
27 #include "wine/port.h"
29 #include <assert.h>
30 #include <ctype.h>
31 #include <string.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <errno.h>
38 #include <unistd.h>
40 #ifdef HAVE_SYS_PARAM_H
41 # include <sys/param.h>
42 #endif
43 #ifdef STATFS_DEFINED_BY_SYS_VFS
44 # include <sys/vfs.h>
45 #else
46 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
47 # include <sys/mount.h>
48 # else
49 # ifdef STATFS_DEFINED_BY_SYS_STATFS
50 # include <sys/statfs.h>
51 # endif
52 # endif
53 #endif
55 #include "winbase.h"
56 #include "ntddk.h"
57 #include "wine/winbase16.h" /* for GetCurrentTask */
58 #include "winerror.h"
59 #include "winioctl.h"
60 #include "ntddstor.h"
61 #include "ntddcdrm.h"
62 #include "drive.h"
63 #include "file.h"
64 #include "heap.h"
65 #include "msdos.h"
66 #include "task.h"
67 #include "wine/library.h"
68 #include "wine/server.h"
69 #include "wine/debug.h"
71 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
72 WINE_DECLARE_DEBUG_CHANNEL(file);
74 typedef struct
76 char *root; /* root dir in Unix format without trailing / */
77 char *dos_cwd; /* cwd in DOS format without leading or trailing \ */
78 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
79 char *device; /* raw device path */
80 char label_conf[12]; /* drive label as cfg'd in wine config */
81 char label_read[12]; /* drive label as read from device */
82 DWORD serial_conf; /* drive serial number as cfg'd in wine config */
83 UINT type; /* drive type */
84 UINT flags; /* drive flags */
85 dev_t dev; /* unix device number */
86 ino_t ino; /* unix inode number */
87 } DOSDRIVE;
90 static const char * const DRIVE_Types[] =
92 "", /* DRIVE_UNKNOWN */
93 "", /* DRIVE_NO_ROOT_DIR */
94 "floppy", /* DRIVE_REMOVABLE */
95 "hd", /* DRIVE_FIXED */
96 "network", /* DRIVE_REMOTE */
97 "cdrom", /* DRIVE_CDROM */
98 "ramdisk" /* DRIVE_RAMDISK */
102 /* Known filesystem types */
104 typedef struct
106 const char *name;
107 UINT flags;
108 } FS_DESCR;
110 static const FS_DESCR DRIVE_Filesystems[] =
112 { "unix", DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
113 { "msdos", DRIVE_SHORT_NAMES },
114 { "dos", DRIVE_SHORT_NAMES },
115 { "fat", DRIVE_SHORT_NAMES },
116 { "vfat", DRIVE_CASE_PRESERVING },
117 { "win95", DRIVE_CASE_PRESERVING },
118 { NULL, 0 }
122 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
123 static int DRIVE_CurDrive = -1;
125 static HTASK16 DRIVE_LastTask = 0;
127 /* strdup on the process heap */
128 inline static char *heap_strdup( const char *str )
130 INT len = strlen(str) + 1;
131 LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
132 if (p) memcpy( p, str, len );
133 return p;
136 extern void CDROM_InitRegistry(int dev);
138 /***********************************************************************
139 * DRIVE_GetDriveType
141 static UINT DRIVE_GetDriveType( const char *name )
143 char buffer[20];
144 int i;
146 PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
147 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
149 if (!strcasecmp( buffer, DRIVE_Types[i] )) return i;
151 MESSAGE("%s: unknown drive type '%s', defaulting to 'hd'.\n",
152 name, buffer );
153 return DRIVE_FIXED;
157 /***********************************************************************
158 * DRIVE_GetFSFlags
160 static UINT DRIVE_GetFSFlags( const char *name, const char *value )
162 const FS_DESCR *descr;
164 for (descr = DRIVE_Filesystems; descr->name; descr++)
165 if (!strcasecmp( value, descr->name )) return descr->flags;
166 MESSAGE("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
167 name, value );
168 return DRIVE_CASE_PRESERVING;
172 /***********************************************************************
173 * DRIVE_Init
175 int DRIVE_Init(void)
177 int i, len, count = 0;
178 char name[] = "Drive A";
179 char drive_env[] = "=A:";
180 char path[MAX_PATHNAME_LEN];
181 char buffer[80];
182 struct stat drive_stat_buffer;
183 char *p;
184 DOSDRIVE *drive;
186 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
188 PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
189 if (path[0])
191 p = path + strlen(path) - 1;
192 while ((p > path) && (*p == '/')) *p-- = '\0';
194 if (path[0] == '/')
196 drive->root = heap_strdup( path );
198 else
200 /* relative paths are relative to config dir */
201 const char *config = wine_get_config_dir();
202 drive->root = HeapAlloc( GetProcessHeap(), 0, strlen(config) + strlen(path) + 2 );
203 sprintf( drive->root, "%s/%s", config, path );
206 if (stat( drive->root, &drive_stat_buffer ))
208 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
209 drive->root, strerror(errno), 'A' + i);
210 HeapFree( GetProcessHeap(), 0, drive->root );
211 drive->root = NULL;
212 continue;
214 if (!S_ISDIR(drive_stat_buffer.st_mode))
216 MESSAGE("%s is not a directory, ignoring drive %c:\n",
217 drive->root, 'A' + i );
218 HeapFree( GetProcessHeap(), 0, drive->root );
219 drive->root = NULL;
220 continue;
223 drive->dos_cwd = heap_strdup( "" );
224 drive->unix_cwd = heap_strdup( "" );
225 drive->type = DRIVE_GetDriveType( name );
226 drive->device = NULL;
227 drive->flags = 0;
228 drive->dev = drive_stat_buffer.st_dev;
229 drive->ino = drive_stat_buffer.st_ino;
231 /* Get the drive label */
232 PROFILE_GetWineIniString( name, "Label", "", drive->label_conf, 12 );
233 if ((len = strlen(drive->label_conf)) < 11)
235 /* Pad label with spaces */
236 memset( drive->label_conf + len, ' ', 11 - len );
237 drive->label_conf[11] = '\0';
240 /* Get the serial number */
241 PROFILE_GetWineIniString( name, "Serial", "12345678",
242 buffer, sizeof(buffer) );
243 drive->serial_conf = strtoul( buffer, NULL, 16 );
245 /* Get the filesystem type */
246 PROFILE_GetWineIniString( name, "Filesystem", "win95",
247 buffer, sizeof(buffer) );
248 drive->flags = DRIVE_GetFSFlags( name, buffer );
250 /* Get the device */
251 PROFILE_GetWineIniString( name, "Device", "",
252 buffer, sizeof(buffer) );
253 if (buffer[0])
255 int cd_fd;
256 drive->device = heap_strdup( buffer );
257 if (PROFILE_GetWineIniBool( name, "ReadVolInfo", 1))
258 drive->flags |= DRIVE_READ_VOL_INFO;
259 if (drive->type == DRIVE_CDROM)
261 if ((cd_fd = open(buffer,O_RDONLY|O_NONBLOCK)) != -1) {
262 CDROM_InitRegistry(cd_fd);
263 close(cd_fd);
268 /* Get the FailReadOnly flag */
269 if (PROFILE_GetWineIniBool( name, "FailReadOnly", 0 ))
270 drive->flags |= DRIVE_FAIL_READ_ONLY;
272 /* Make the first hard disk the current drive */
273 if ((DRIVE_CurDrive == -1) && (drive->type == DRIVE_FIXED))
274 DRIVE_CurDrive = i;
276 count++;
277 TRACE("%s: path=%s type=%s label='%s' serial=%08lx "
278 "flags=%08x dev=%x ino=%x\n",
279 name, drive->root, DRIVE_Types[drive->type],
280 drive->label_conf, drive->serial_conf, drive->flags,
281 (int)drive->dev, (int)drive->ino );
283 else WARN("%s: not defined\n", name );
286 if (!count)
288 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
289 /* Create a C drive pointing to Unix root dir */
290 DOSDrives[2].root = heap_strdup( "/" );
291 DOSDrives[2].dos_cwd = heap_strdup( "" );
292 DOSDrives[2].unix_cwd = heap_strdup( "" );
293 strcpy( DOSDrives[2].label_conf, "Drive C " );
294 DOSDrives[2].serial_conf = 12345678;
295 DOSDrives[2].type = DRIVE_FIXED;
296 DOSDrives[2].device = NULL;
297 DOSDrives[2].flags = 0;
298 DRIVE_CurDrive = 2;
301 /* Make sure the current drive is valid */
302 if (DRIVE_CurDrive == -1)
304 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
306 if (drive->root && !(drive->flags & DRIVE_DISABLED))
308 DRIVE_CurDrive = i;
309 break;
314 /* get current working directory info for all drives */
315 for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
317 if (!GetEnvironmentVariableA(drive_env, path, sizeof(path))) continue;
318 /* sanity check */
319 if (toupper(path[0]) != drive_env[1] || path[1] != ':') continue;
320 DRIVE_Chdir( i, path + 2 );
322 return 1;
326 /***********************************************************************
327 * DRIVE_IsValid
329 int DRIVE_IsValid( int drive )
331 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
332 return (DOSDrives[drive].root &&
333 !(DOSDrives[drive].flags & DRIVE_DISABLED));
337 /***********************************************************************
338 * DRIVE_GetCurrentDrive
340 int DRIVE_GetCurrentDrive(void)
342 TDB *pTask = TASK_GetCurrent();
343 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
344 return DRIVE_CurDrive;
348 /***********************************************************************
349 * DRIVE_SetCurrentDrive
351 int DRIVE_SetCurrentDrive( int drive )
353 TDB *pTask = TASK_GetCurrent();
354 if (!DRIVE_IsValid( drive ))
356 SetLastError( ERROR_INVALID_DRIVE );
357 return 0;
359 TRACE("%c:\n", 'A' + drive );
360 DRIVE_CurDrive = drive;
361 if (pTask) pTask->curdrive = drive | 0x80;
362 chdir(DRIVE_GetUnixCwd(drive));
363 return 1;
367 /***********************************************************************
368 * DRIVE_FindDriveRoot
370 * Find a drive for which the root matches the beginning of the given path.
371 * This can be used to translate a Unix path into a drive + DOS path.
372 * Return value is the drive, or -1 on error. On success, path is modified
373 * to point to the beginning of the DOS path.
375 int DRIVE_FindDriveRoot( const char **path )
377 /* Starting with the full path, check if the device and inode match any of
378 * the wine 'drives'. If not then remove the last path component and try
379 * again. If the last component was a '..' then skip a normal component
380 * since it's a directory that's ascended back out of.
382 int drive, level, len;
383 char buffer[MAX_PATHNAME_LEN];
384 char *p;
385 struct stat st;
387 strcpy( buffer, *path );
388 while ((p = strchr( buffer, '\\' )) != NULL)
389 *p = '/';
390 len = strlen(buffer);
392 /* strip off trailing slashes */
393 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
395 for (;;)
397 /* Find the drive */
398 if (stat( buffer, &st ) == 0 && S_ISDIR( st.st_mode ))
400 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
402 if (!DOSDrives[drive].root ||
403 (DOSDrives[drive].flags & DRIVE_DISABLED))
404 continue;
406 if ((DOSDrives[drive].dev == st.st_dev) &&
407 (DOSDrives[drive].ino == st.st_ino))
409 if (len == 1) len = 0; /* preserve root slash in returned path */
410 TRACE( "%s -> drive %c:, root='%s', name='%s'\n",
411 *path, 'A' + drive, buffer, *path + len);
412 *path += len;
413 if (!**path) *path = "\\";
414 return drive;
418 if (len <= 1) return -1; /* reached root */
420 level = 0;
421 while (level < 1)
423 /* find start of the last path component */
424 while (len > 1 && buffer[len - 1] != '/') len--;
425 if (!buffer[len]) break; /* empty component -> reached root */
426 /* does removing it take us up a level? */
427 if (strcmp( buffer + len, "." ) != 0)
428 level += strcmp( buffer + len, ".." ) ? 1 : -1;
429 buffer[len] = 0;
430 /* strip off trailing slashes */
431 while (len > 1 && buffer[len - 1] == '/') buffer[--len] = 0;
437 /***********************************************************************
438 * DRIVE_GetRoot
440 const char * DRIVE_GetRoot( int drive )
442 if (!DRIVE_IsValid( drive )) return NULL;
443 return DOSDrives[drive].root;
447 /***********************************************************************
448 * DRIVE_GetDosCwd
450 const char * DRIVE_GetDosCwd( int drive )
452 TDB *pTask = TASK_GetCurrent();
453 if (!DRIVE_IsValid( drive )) return NULL;
455 /* Check if we need to change the directory to the new task. */
456 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
457 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
458 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
460 /* Perform the task-switch */
461 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
462 DRIVE_LastTask = GetCurrentTask();
464 return DOSDrives[drive].dos_cwd;
468 /***********************************************************************
469 * DRIVE_GetUnixCwd
471 const char * DRIVE_GetUnixCwd( int drive )
473 TDB *pTask = TASK_GetCurrent();
474 if (!DRIVE_IsValid( drive )) return NULL;
476 /* Check if we need to change the directory to the new task. */
477 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
478 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
479 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
481 /* Perform the task-switch */
482 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
483 DRIVE_LastTask = GetCurrentTask();
485 return DOSDrives[drive].unix_cwd;
489 /***********************************************************************
490 * DRIVE_GetDevice
492 const char * DRIVE_GetDevice( int drive )
494 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
497 /******************************************************************
498 * static WORD CDROM_Data_FindBestVoldesc
502 static WORD CDROM_Data_FindBestVoldesc(int fd)
504 BYTE cur_vd_type, max_vd_type = 0;
505 unsigned int offs, best_offs = 0, extra_offs = 0;
506 char sig[3];
508 for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
510 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
511 * the volume label is displaced forward by 8
513 lseek(fd, offs + 11, SEEK_SET); /* check for non-ISO9660 signature */
514 read(fd, &sig, 3);
515 if ((sig[0] == 'R') && (sig[1] == 'O') && (sig[2]=='M'))
517 extra_offs = 8;
519 lseek(fd, offs + extra_offs, SEEK_SET);
520 read(fd, &cur_vd_type, 1);
521 if (cur_vd_type == 0xff) /* voldesc set terminator */
522 break;
523 if (cur_vd_type > max_vd_type)
525 max_vd_type = cur_vd_type;
526 best_offs = offs + extra_offs;
529 return best_offs;
532 /***********************************************************************
533 * DRIVE_ReadSuperblock
535 * NOTE
536 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
537 * to check, that they are writing on a FAT filesystem !
539 int DRIVE_ReadSuperblock (int drive, char * buff)
541 #define DRIVE_SUPER 96
542 int fd;
543 off_t offs;
544 int ret = 0;
546 if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
547 if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
549 struct stat st;
550 if (!DOSDrives[drive].device)
551 ERR("No device configured for drive %c: !\n", 'A'+drive);
552 else
553 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
554 (stat(DOSDrives[drive].device, &st)) ?
555 "not available or symlink not valid ?" : "no permission");
556 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
557 PROFILE_UsageWineIni();
558 return -1;
561 switch(DOSDrives[drive].type)
563 case DRIVE_REMOVABLE:
564 case DRIVE_FIXED:
565 offs = 0;
566 break;
567 case DRIVE_CDROM:
568 offs = CDROM_Data_FindBestVoldesc(fd);
569 break;
570 default:
571 offs = 0;
572 break;
575 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs))
577 ret = -4;
578 goto the_end;
580 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER)
582 ret = -2;
583 goto the_end;
586 switch(DOSDrives[drive].type)
588 case DRIVE_REMOVABLE:
589 case DRIVE_FIXED:
590 if ((buff[0x26]!=0x29) || /* Check for FAT present */
591 /* FIXME: do really all FAT have their name beginning with
592 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
593 memcmp( buff+0x36,"FAT",3))
595 ERR("The filesystem is not FAT !! (device=%s)\n",
596 DOSDrives[drive].device);
597 ret = -3;
598 goto the_end;
600 break;
601 case DRIVE_CDROM:
602 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
604 ret = -3;
605 goto the_end;
607 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
608 break;
609 default:
610 ret = -3;
611 goto the_end;
614 return close(fd);
615 the_end:
616 close(fd);
617 return ret;
621 /***********************************************************************
622 * DRIVE_WriteSuperblockEntry
624 * NOTE
625 * We are writing as little as possible (ie. not the whole SuperBlock)
626 * not to interfere with kernel. The drive can be mounted !
628 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
630 int fd;
632 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
634 ERR("Cannot open the device %s (for writing)\n",
635 DOSDrives[drive].device);
636 return -1;
638 if (lseek(fd,ofs,SEEK_SET)!=ofs)
640 ERR("lseek failed on device %s !\n",
641 DOSDrives[drive].device);
642 close(fd);
643 return -2;
645 if (write(fd,buff,len)!=len)
647 close(fd);
648 ERR("Cannot write on %s !\n",
649 DOSDrives[drive].device);
650 return -3;
652 return close (fd);
655 /******************************************************************
656 * static HANDLE CDROM_Open
660 static HANDLE CDROM_Open(int drive)
662 char root[6];
664 strcpy(root, "\\\\.\\A:");
665 root[4] += drive;
667 return CreateFileA(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
670 /**************************************************************************
671 * CDROM_Data_GetLabel [internal]
673 DWORD CDROM_Data_GetLabel(int drive, char *label)
675 #define LABEL_LEN 32+1
676 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
677 WORD offs = CDROM_Data_FindBestVoldesc(dev);
678 WCHAR label_read[LABEL_LEN]; /* Unicode possible, too */
679 DWORD unicode_id = 0;
681 if (offs)
683 if ((lseek(dev, offs+0x58, SEEK_SET) == offs+0x58)
684 && (read(dev, &unicode_id, 3) == 3))
686 int ver = (unicode_id & 0xff0000) >> 16;
688 if ((lseek(dev, offs+0x28, SEEK_SET) != offs+0x28)
689 || (read(dev, &label_read, LABEL_LEN) != LABEL_LEN))
690 goto failure;
692 close(dev);
693 if ((LOWORD(unicode_id) == 0x2f25) /* Unicode ID */
694 && ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
695 { /* yippee, unicode */
696 int i;
697 WORD ch;
698 for (i=0; i<LABEL_LEN;i++)
699 { /* Motorola -> Intel Unicode conversion :-\ */
700 ch = label_read[i];
701 label_read[i] = (ch << 8) | (ch >> 8);
703 WideCharToMultiByte( CP_ACP, 0, label_read, -1, label, 12, NULL, NULL );
704 label[11] = 0;
706 else
708 strncpy(label, (LPSTR)label_read, 11);
709 label[11] = '\0';
711 return 1;
714 failure:
715 close(dev);
716 ERR("error reading label !\n");
717 return 0;
720 /**************************************************************************
721 * CDROM_GetLabel [internal]
723 static DWORD CDROM_GetLabel(int drive, char *label)
725 HANDLE h = CDROM_Open(drive);
726 CDROM_DISK_DATA cdd;
727 DWORD br;
728 DWORD ret = 1;
730 if (!h || !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
731 return 0;
733 switch (cdd.DiskData & 0x03)
735 case CDROM_DISK_DATA_TRACK:
736 if (!CDROM_Data_GetLabel(drive, label))
737 ret = 0;
738 break;
739 case CDROM_DISK_AUDIO_TRACK:
740 strcpy(label, "Audio CD ");
741 break;
742 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
743 FIXME("Need to get the label of a mixed mode CD: not implemented yet !\n");
744 /* fall through */
745 case 0:
746 ret = 0;
747 break;
749 TRACE("CD: label is '%s'.\n", label);
751 return ret;
753 /***********************************************************************
754 * DRIVE_GetLabel
756 const char * DRIVE_GetLabel( int drive )
758 int read = 0;
759 char buff[DRIVE_SUPER];
760 int offs = -1;
762 if (!DRIVE_IsValid( drive )) return NULL;
763 if (DOSDrives[drive].type == DRIVE_CDROM)
765 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
767 else
768 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
770 if (DRIVE_ReadSuperblock(drive,(char *) buff))
771 ERR("Invalid or unreadable superblock on %s (%c:).\n",
772 DOSDrives[drive].device, (char)(drive+'A'));
773 else {
774 if (DOSDrives[drive].type == DRIVE_REMOVABLE ||
775 DOSDrives[drive].type == DRIVE_FIXED)
776 offs = 0x2b;
778 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
779 if (offs != -1) memcpy(DOSDrives[drive].label_read,buff+offs,11);
780 DOSDrives[drive].label_read[11]='\0';
781 read = 1;
785 return (read) ?
786 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
789 #define CDFRAMES_PERSEC 75
790 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
791 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
792 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
794 /**************************************************************************
795 * CDROM_Audio_GetSerial [internal]
797 static DWORD CDROM_Audio_GetSerial(HANDLE h)
799 unsigned long serial = 0;
800 int i;
801 WORD wMagic;
802 DWORD dwStart, dwEnd, br;
803 CDROM_TOC toc;
805 if (!DeviceIoControl(h, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0))
806 return 0;
809 * wMagic collects the wFrames from track 1
810 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
811 * frames.
812 * There it is collected for correcting the serial when there are less than
813 * 3 tracks.
815 wMagic = toc.TrackData[0].Address[2];
816 dwStart = FRAME_OF_TOC(toc, toc.FirstTrack);
818 for (i = 0; i <= toc.LastTrack - toc.FirstTrack; i++) {
819 serial += (toc.TrackData[i].Address[0] << 16) |
820 (toc.TrackData[i].Address[1] << 8) | toc.TrackData[i].Address[2];
822 dwEnd = FRAME_OF_TOC(toc, toc.LastTrack + 1);
824 if (toc.LastTrack - toc.FirstTrack + 1 < 3)
825 serial += wMagic + (dwEnd - dwStart);
827 return serial;
830 /**************************************************************************
831 * CDROM_Data_GetSerial [internal]
833 static DWORD CDROM_Data_GetSerial(int drive)
835 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
836 WORD offs;
837 union {
838 unsigned long val;
839 unsigned char p[4];
840 } serial;
841 BYTE b0 = 0, b1 = 1, b2 = 2, b3 = 3;
844 if (dev == -1) return 0;
845 offs = CDROM_Data_FindBestVoldesc(dev);
847 serial.val = 0;
848 if (offs)
850 BYTE buf[2048];
851 OSVERSIONINFOA ovi;
852 int i;
854 lseek(dev, offs, SEEK_SET);
855 read(dev, buf, 2048);
857 * OK, another braindead one... argh. Just believe it.
858 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
859 * It's true and nobody will ever be able to change it.
861 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
862 GetVersionExA(&ovi);
863 if ((ovi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (ovi.dwMajorVersion >= 4))
865 b0 = 3; b1 = 2; b2 = 1; b3 = 0;
867 for (i = 0; i < 2048; i += 4)
869 /* DON'T optimize this into DWORD !! (breaks overflow) */
870 serial.p[b0] += buf[i+b0];
871 serial.p[b1] += buf[i+b1];
872 serial.p[b2] += buf[i+b2];
873 serial.p[b3] += buf[i+b3];
876 close(dev);
877 return serial.val;
880 /**************************************************************************
881 * CDROM_GetSerial [internal]
883 static DWORD CDROM_GetSerial(int drive)
885 DWORD serial = 0;
886 HANDLE h = CDROM_Open(drive);
887 CDROM_DISK_DATA cdd;
888 DWORD br;
890 if (!h || ! !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
891 return 0;
893 switch (cdd.DiskData & 0x03)
895 case CDROM_DISK_DATA_TRACK:
896 /* hopefully a data CD */
897 serial = CDROM_Data_GetSerial(drive);
898 break;
899 case CDROM_DISK_AUDIO_TRACK:
900 /* fall thru */
901 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
902 serial = CDROM_Audio_GetSerial(h);
903 break;
904 case 0:
905 break;
908 if (serial)
909 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial), LOWORD(serial));
911 CloseHandle(h);
913 return serial;
916 /***********************************************************************
917 * DRIVE_GetSerialNumber
919 DWORD DRIVE_GetSerialNumber( int drive )
921 DWORD serial = 0;
922 char buff[DRIVE_SUPER];
924 if (!DRIVE_IsValid( drive )) return 0;
926 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
928 switch(DOSDrives[drive].type)
930 case DRIVE_REMOVABLE:
931 case DRIVE_FIXED:
932 if (DRIVE_ReadSuperblock(drive,(char *) buff))
933 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
934 " Maybe not FAT?\n" ,
935 DOSDrives[drive].device, 'A'+drive);
936 else
937 serial = *((DWORD*)(buff+0x27));
938 break;
939 case DRIVE_CDROM:
940 serial = CDROM_GetSerial(drive);
941 break;
942 default:
943 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
947 return (serial) ? serial : DOSDrives[drive].serial_conf;
951 /***********************************************************************
952 * DRIVE_SetSerialNumber
954 int DRIVE_SetSerialNumber( int drive, DWORD serial )
956 char buff[DRIVE_SUPER];
958 if (!DRIVE_IsValid( drive )) return 0;
960 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
962 if ((DOSDrives[drive].type != DRIVE_REMOVABLE) &&
963 (DOSDrives[drive].type != DRIVE_FIXED)) return 0;
964 /* check, if the drive has a FAT filesystem */
965 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
966 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
967 return 1;
970 if (DOSDrives[drive].type == DRIVE_CDROM) return 0;
971 DOSDrives[drive].serial_conf = serial;
972 return 1;
976 /***********************************************************************
977 * DRIVE_GetType
979 static UINT DRIVE_GetType( int drive )
981 if (!DRIVE_IsValid( drive )) return DRIVE_UNKNOWN;
982 return DOSDrives[drive].type;
986 /***********************************************************************
987 * DRIVE_GetFlags
989 UINT DRIVE_GetFlags( int drive )
991 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
992 return DOSDrives[drive].flags;
996 /***********************************************************************
997 * DRIVE_Chdir
999 int DRIVE_Chdir( int drive, const char *path )
1001 DOS_FULL_NAME full_name;
1002 char buffer[MAX_PATHNAME_LEN];
1003 LPSTR unix_cwd;
1004 BY_HANDLE_FILE_INFORMATION info;
1005 TDB *pTask = TASK_GetCurrent();
1007 strcpy( buffer, "A:" );
1008 buffer[0] += drive;
1009 TRACE("(%s,%s)\n", buffer, path );
1010 lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
1012 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
1013 if (!FILE_Stat( full_name.long_name, &info )) return 0;
1014 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
1016 SetLastError( ERROR_FILE_NOT_FOUND );
1017 return 0;
1019 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
1020 while (*unix_cwd == '/') unix_cwd++;
1022 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
1023 'A' + drive, unix_cwd, full_name.short_name + 3 );
1025 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
1026 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
1027 DOSDrives[drive].dos_cwd = heap_strdup( full_name.short_name + 3 );
1028 DOSDrives[drive].unix_cwd = heap_strdup( unix_cwd );
1030 if (pTask && (pTask->curdrive & 0x80) &&
1031 ((pTask->curdrive & ~0x80) == drive))
1033 lstrcpynA( pTask->curdir, full_name.short_name + 2,
1034 sizeof(pTask->curdir) );
1035 DRIVE_LastTask = GetCurrentTask();
1036 chdir(unix_cwd); /* Only change if on current drive */
1038 return 1;
1042 /***********************************************************************
1043 * DRIVE_Disable
1045 int DRIVE_Disable( int drive )
1047 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1049 SetLastError( ERROR_INVALID_DRIVE );
1050 return 0;
1052 DOSDrives[drive].flags |= DRIVE_DISABLED;
1053 return 1;
1057 /***********************************************************************
1058 * DRIVE_Enable
1060 int DRIVE_Enable( int drive )
1062 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1064 SetLastError( ERROR_INVALID_DRIVE );
1065 return 0;
1067 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
1068 return 1;
1072 /***********************************************************************
1073 * DRIVE_SetLogicalMapping
1075 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
1077 /* If new_drive is already valid, do nothing and return 0
1078 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1080 DOSDRIVE *old, *new;
1082 old = DOSDrives + existing_drive;
1083 new = DOSDrives + new_drive;
1085 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
1086 !old->root ||
1087 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
1089 SetLastError( ERROR_INVALID_DRIVE );
1090 return 0;
1093 if ( new->root )
1095 TRACE("Can't map drive %c: to already existing drive %c:\n",
1096 'A' + existing_drive, 'A' + new_drive );
1097 /* it is already mapped there, so return success */
1098 if (!strcmp(old->root,new->root))
1099 return 1;
1100 return 0;
1103 new->root = heap_strdup( old->root );
1104 new->dos_cwd = heap_strdup( old->dos_cwd );
1105 new->unix_cwd = heap_strdup( old->unix_cwd );
1106 new->device = heap_strdup( old->device );
1107 memcpy ( new->label_conf, old->label_conf, 12 );
1108 memcpy ( new->label_read, old->label_read, 12 );
1109 new->serial_conf = old->serial_conf;
1110 new->type = old->type;
1111 new->flags = old->flags;
1112 new->dev = old->dev;
1113 new->ino = old->ino;
1115 TRACE("Drive %c: is now equal to drive %c:\n",
1116 'A' + new_drive, 'A' + existing_drive );
1118 return 1;
1122 /***********************************************************************
1123 * DRIVE_OpenDevice
1125 * Open the drive raw device and return a Unix fd (or -1 on error).
1127 int DRIVE_OpenDevice( int drive, int flags )
1129 if (!DRIVE_IsValid( drive )) return -1;
1130 return open( DOSDrives[drive].device, flags );
1134 /***********************************************************************
1135 * DRIVE_RawRead
1137 * Read raw sectors from a device
1139 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1141 int fd;
1143 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1145 lseek( fd, begin * 512, SEEK_SET );
1146 /* FIXME: check errors */
1147 read( fd, dataptr, nr_sect * 512 );
1148 close( fd );
1150 else
1152 memset(dataptr, 0, nr_sect * 512);
1153 if (fake_success)
1155 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
1156 if (begin == 1) *dataptr = 0xf8;
1158 else
1159 return 0;
1161 return 1;
1165 /***********************************************************************
1166 * DRIVE_RawWrite
1168 * Write raw sectors to a device
1170 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1172 int fd;
1174 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1176 lseek( fd, begin * 512, SEEK_SET );
1177 /* FIXME: check errors */
1178 write( fd, dataptr, nr_sect * 512 );
1179 close( fd );
1181 else
1182 if (!(fake_success))
1183 return 0;
1185 return 1;
1189 /***********************************************************************
1190 * DRIVE_GetFreeSpace
1192 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
1193 PULARGE_INTEGER available )
1195 struct statfs info;
1197 if (!DRIVE_IsValid(drive))
1199 SetLastError( ERROR_INVALID_DRIVE );
1200 return 0;
1203 /* FIXME: add autoconf check for this */
1204 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1205 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
1206 #else
1207 if (statfs( DOSDrives[drive].root, &info) < 0)
1208 #endif
1210 FILE_SetDosError();
1211 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
1212 return 0;
1215 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bsize, info.f_blocks );
1216 #ifdef STATFS_HAS_BAVAIL
1217 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bavail, info.f_bsize );
1218 #else
1219 # ifdef STATFS_HAS_BFREE
1220 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bfree, info.f_bsize );
1221 # else
1222 # error "statfs has no bfree/bavail member!"
1223 # endif
1224 #endif
1225 if (DOSDrives[drive].type == DRIVE_CDROM)
1226 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1227 available->QuadPart = 0;
1229 return 1;
1232 /***********************************************************************
1233 * DRIVE_GetCurrentDirectory
1234 * Returns "X:\\path\\etc\\".
1236 * Despite the API description, return required length including the
1237 * terminating null when buffer too small. This is the real behaviour.
1240 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPSTR buf )
1242 UINT ret;
1243 const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1245 assert(s);
1246 ret = strlen(s) + 3; /* length of WHOLE current directory */
1247 if (ret >= buflen) return ret + 1;
1248 lstrcpynA( buf, "A:\\", min( 4u, buflen ) );
1249 if (buflen) buf[0] += DRIVE_GetCurrentDrive();
1250 if (buflen > 3) lstrcpynA( buf + 3, s, buflen - 3 );
1251 return ret;
1255 /***********************************************************************
1256 * DRIVE_BuildEnv
1258 * Build the environment array containing the drives' current directories.
1259 * Resulting pointer must be freed with HeapFree.
1261 char *DRIVE_BuildEnv(void)
1263 int i, length = 0;
1264 const char *cwd[MAX_DOS_DRIVES];
1265 char *env, *p;
1267 for (i = 0; i < MAX_DOS_DRIVES; i++)
1269 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0]) length += strlen(cwd[i]) + 8;
1271 if (!(env = HeapAlloc( GetProcessHeap(), 0, length+1 ))) return NULL;
1272 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
1274 if (cwd[i] && cwd[i][0])
1275 p += sprintf( p, "=%c:=%c:\\%s", 'A'+i, 'A'+i, cwd[i] ) + 1;
1277 *p = 0;
1278 return env;
1282 /***********************************************************************
1283 * GetDiskFreeSpace (KERNEL.422)
1285 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
1286 LPDWORD sector_bytes, LPDWORD free_clusters,
1287 LPDWORD total_clusters )
1289 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
1290 free_clusters, total_clusters );
1294 /***********************************************************************
1295 * GetDiskFreeSpaceA (KERNEL32.@)
1297 * Fails if expression resulting from current drive's dir and "root"
1298 * is not a root dir of the target drive.
1300 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1301 * if the corresponding info is unneeded.
1303 * FIXME: needs to support UNC names from Win95 OSR2 on.
1305 * Behaviour under Win95a:
1306 * CurrDir root result
1307 * "E:\\TEST" "E:" FALSE
1308 * "E:\\" "E:" TRUE
1309 * "E:\\" "E" FALSE
1310 * "E:\\" "\\" TRUE
1311 * "E:\\TEST" "\\" TRUE
1312 * "E:\\TEST" ":\\" FALSE
1313 * "E:\\TEST" "E:\\" TRUE
1314 * "E:\\TEST" "" FALSE
1315 * "E:\\" "" FALSE (!)
1316 * "E:\\" 0x0 TRUE
1317 * "E:\\TEST" 0x0 TRUE (!)
1318 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1319 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1321 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1322 LPDWORD sector_bytes, LPDWORD free_clusters,
1323 LPDWORD total_clusters )
1325 int drive, sec_size;
1326 ULARGE_INTEGER size,available;
1327 LPCSTR path;
1328 DWORD cluster_sec;
1330 if ((!root) || (strcmp(root,"\\") == 0))
1331 drive = DRIVE_GetCurrentDrive();
1332 else
1333 if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
1335 drive = toupper(root[0]) - 'A';
1336 path = &root[2];
1337 if (path[0] == '\0')
1338 path = DRIVE_GetDosCwd(drive);
1339 else
1340 if (path[0] == '\\')
1341 path++;
1342 if (path[0]) /* oops, we are in a subdir */
1344 SetLastError(ERROR_INVALID_NAME);
1345 return FALSE;
1348 else
1350 SetLastError(ERROR_INVALID_NAME);
1351 return FALSE;
1354 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1356 /* Cap the size and available at 2GB as per specs. */
1357 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1359 size.s.HighPart = 0;
1360 size.s.LowPart = 0x7fffffff;
1362 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1364 available.s.HighPart =0;
1365 available.s.LowPart = 0x7fffffff;
1367 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
1368 size.s.LowPart /= sec_size;
1369 available.s.LowPart /= sec_size;
1370 /* FIXME: probably have to adjust those variables too for CDFS */
1371 cluster_sec = 1;
1372 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1374 if (cluster_sectors)
1375 *cluster_sectors = cluster_sec;
1376 if (sector_bytes)
1377 *sector_bytes = sec_size;
1378 if (free_clusters)
1379 *free_clusters = available.s.LowPart / cluster_sec;
1380 if (total_clusters)
1381 *total_clusters = size.s.LowPart / cluster_sec;
1382 return TRUE;
1386 /***********************************************************************
1387 * GetDiskFreeSpaceW (KERNEL32.@)
1389 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1390 LPDWORD sector_bytes, LPDWORD free_clusters,
1391 LPDWORD total_clusters )
1393 LPSTR xroot;
1394 BOOL ret;
1396 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1397 ret = GetDiskFreeSpaceA( xroot,cluster_sectors, sector_bytes,
1398 free_clusters, total_clusters );
1399 HeapFree( GetProcessHeap(), 0, xroot );
1400 return ret;
1404 /***********************************************************************
1405 * GetDiskFreeSpaceExA (KERNEL32.@)
1407 * This function is used to acquire the size of the available and
1408 * total space on a logical volume.
1410 * RETURNS
1412 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1413 * detailed error information.
1416 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
1417 PULARGE_INTEGER avail,
1418 PULARGE_INTEGER total,
1419 PULARGE_INTEGER totalfree)
1421 int drive;
1422 ULARGE_INTEGER size,available;
1424 if (!root) drive = DRIVE_GetCurrentDrive();
1425 else
1426 { /* C: always works for GetDiskFreeSpaceEx */
1427 if ((root[1]) && ((root[1] != ':') || (root[2] && root[2] != '\\')))
1429 FIXME("there are valid root names which are not supported yet\n");
1430 /* ..like UNC names, for instance. */
1432 WARN("invalid root '%s'\n", root );
1433 return FALSE;
1435 drive = toupper(root[0]) - 'A';
1438 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1440 if (total)
1442 total->s.HighPart = size.s.HighPart;
1443 total->s.LowPart = size.s.LowPart;
1446 if (totalfree)
1448 totalfree->s.HighPart = available.s.HighPart;
1449 totalfree->s.LowPart = available.s.LowPart;
1452 if (avail)
1454 if (FIXME_ON(dosfs))
1456 /* On Windows2000, we need to check the disk quota
1457 allocated for the user owning the calling process. We
1458 don't want to be more obtrusive than necessary with the
1459 FIXME messages, so don't print the FIXME unless Wine is
1460 actually masquerading as Windows2000. */
1462 OSVERSIONINFOA ovi;
1463 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1464 if (GetVersionExA(&ovi))
1466 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1467 FIXME("no per-user quota support yet\n");
1471 /* Quick hack, should eventually be fixed to work 100% with
1472 Windows2000 (see comment above). */
1473 avail->s.HighPart = available.s.HighPart;
1474 avail->s.LowPart = available.s.LowPart;
1477 return TRUE;
1480 /***********************************************************************
1481 * GetDiskFreeSpaceExW (KERNEL32.@)
1483 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1484 PULARGE_INTEGER total,
1485 PULARGE_INTEGER totalfree)
1487 LPSTR xroot;
1488 BOOL ret;
1490 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1491 ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
1492 HeapFree( GetProcessHeap(), 0, xroot );
1493 return ret;
1496 /***********************************************************************
1497 * GetDriveType (KERNEL.136)
1498 * This function returns the type of a drive in Win16.
1499 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1500 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1501 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1502 * do any pseudo-clever changes.
1504 * RETURNS
1505 * drivetype DRIVE_xxx
1507 UINT16 WINAPI GetDriveType16( UINT16 drive ) /* [in] number (NOT letter) of drive */
1509 UINT type = DRIVE_GetType(drive);
1510 TRACE("(%c:)\n", 'A' + drive );
1511 if (type == DRIVE_CDROM) type = DRIVE_REMOTE;
1512 return type;
1516 /***********************************************************************
1517 * GetDriveTypeA (KERNEL32.@)
1519 * Returns the type of the disk drive specified. If root is NULL the
1520 * root of the current directory is used.
1522 * RETURNS
1524 * Type of drive (from Win32 SDK):
1526 * DRIVE_UNKNOWN unable to find out anything about the drive
1527 * DRIVE_NO_ROOT_DIR nonexistent root dir
1528 * DRIVE_REMOVABLE the disk can be removed from the machine
1529 * DRIVE_FIXED the disk can not be removed from the machine
1530 * DRIVE_REMOTE network disk
1531 * DRIVE_CDROM CDROM drive
1532 * DRIVE_RAMDISK virtual disk in RAM
1534 UINT WINAPI GetDriveTypeA(LPCSTR root) /* [in] String describing drive */
1536 int drive;
1537 TRACE("(%s)\n", debugstr_a(root));
1539 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1540 else
1542 if ((root[1]) && (root[1] != ':'))
1544 WARN("invalid root %s\n", debugstr_a(root));
1545 return DRIVE_NO_ROOT_DIR;
1547 drive = toupper(root[0]) - 'A';
1549 return DRIVE_GetType(drive);
1553 /***********************************************************************
1554 * GetDriveTypeW (KERNEL32.@)
1556 UINT WINAPI GetDriveTypeW( LPCWSTR root )
1558 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1559 UINT ret = GetDriveTypeA( xpath );
1560 HeapFree( GetProcessHeap(), 0, xpath );
1561 return ret;
1565 /***********************************************************************
1566 * GetCurrentDirectory (KERNEL.411)
1568 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1570 return (UINT16)DRIVE_GetCurrentDirectory(buflen, buf);
1574 /***********************************************************************
1575 * GetCurrentDirectoryA (KERNEL32.@)
1577 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1579 UINT ret;
1580 char longname[MAX_PATHNAME_LEN];
1581 char shortname[MAX_PATHNAME_LEN];
1582 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1583 if ( ret > MAX_PATHNAME_LEN ) {
1584 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1585 return ret;
1587 GetLongPathNameA(shortname, longname, MAX_PATHNAME_LEN);
1588 ret = strlen( longname ) + 1;
1589 if (ret > buflen) return ret;
1590 strcpy(buf, longname);
1591 return ret - 1;
1594 /***********************************************************************
1595 * GetCurrentDirectoryW (KERNEL32.@)
1597 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1599 LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
1600 UINT ret = GetCurrentDirectoryA( buflen, xpath );
1601 if (ret < buflen) ret = MultiByteToWideChar( CP_ACP, 0, xpath, -1, buf, buflen ) - 1;
1602 HeapFree( GetProcessHeap(), 0, xpath );
1603 return ret;
1607 /***********************************************************************
1608 * SetCurrentDirectory (KERNEL.412)
1610 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1612 return SetCurrentDirectoryA( dir );
1616 /***********************************************************************
1617 * SetCurrentDirectoryA (KERNEL32.@)
1619 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1621 int drive, olddrive = DRIVE_GetCurrentDrive();
1623 if (!dir) {
1624 ERR_(file)("(NULL)!\n");
1625 return FALSE;
1627 if (dir[0] && (dir[1]==':'))
1629 drive = toupper( *dir ) - 'A';
1630 dir += 2;
1632 else
1633 drive = olddrive;
1635 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1636 sets pTask->curdir only if pTask->curdrive is drive */
1637 if (!(DRIVE_SetCurrentDrive( drive )))
1638 return FALSE;
1639 /* FIXME: what about empty strings? Add a \\ ? */
1640 if (!DRIVE_Chdir( drive, dir )) {
1641 DRIVE_SetCurrentDrive(olddrive);
1642 return FALSE;
1644 return TRUE;
1648 /***********************************************************************
1649 * SetCurrentDirectoryW (KERNEL32.@)
1651 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1653 LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1654 BOOL res = SetCurrentDirectoryA( dir );
1655 HeapFree( GetProcessHeap(), 0, dir );
1656 return res;
1660 /***********************************************************************
1661 * GetLogicalDriveStringsA (KERNEL32.@)
1663 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1665 int drive, count;
1667 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1668 if (DRIVE_IsValid(drive)) count++;
1669 if ((count * 4) + 1 <= len)
1671 LPSTR p = buffer;
1672 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1673 if (DRIVE_IsValid(drive))
1675 *p++ = 'a' + drive;
1676 *p++ = ':';
1677 *p++ = '\\';
1678 *p++ = '\0';
1680 *p = '\0';
1681 return count * 4;
1683 else
1684 return (count * 4) + 1; /* account for terminating null */
1685 /* The API tells about these different return values */
1689 /***********************************************************************
1690 * GetLogicalDriveStringsW (KERNEL32.@)
1692 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1694 int drive, count;
1696 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1697 if (DRIVE_IsValid(drive)) count++;
1698 if (count * 4 * sizeof(WCHAR) <= len)
1700 LPWSTR p = buffer;
1701 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1702 if (DRIVE_IsValid(drive))
1704 *p++ = (WCHAR)('a' + drive);
1705 *p++ = (WCHAR)':';
1706 *p++ = (WCHAR)'\\';
1707 *p++ = (WCHAR)'\0';
1709 *p = (WCHAR)'\0';
1711 return count * 4 * sizeof(WCHAR);
1715 /***********************************************************************
1716 * GetLogicalDrives (KERNEL32.@)
1718 DWORD WINAPI GetLogicalDrives(void)
1720 DWORD ret = 0;
1721 int drive;
1723 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1725 if ( (DRIVE_IsValid(drive)) ||
1726 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1727 ret |= (1 << drive);
1729 return ret;
1733 /***********************************************************************
1734 * GetVolumeInformationA (KERNEL32.@)
1736 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1737 DWORD label_len, DWORD *serial,
1738 DWORD *filename_len, DWORD *flags,
1739 LPSTR fsname, DWORD fsname_len )
1741 int drive;
1742 char *cp;
1744 /* FIXME, SetLastError()s missing */
1746 if (!root) drive = DRIVE_GetCurrentDrive();
1747 else
1749 if ((root[1]) && (root[1] != ':'))
1751 WARN("invalid root '%s'\n",root);
1752 return FALSE;
1754 drive = toupper(root[0]) - 'A';
1756 if (!DRIVE_IsValid( drive )) return FALSE;
1757 if (label)
1759 lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1760 cp = label + strlen(label);
1761 while (cp != label && *(cp-1) == ' ') cp--;
1762 *cp = '\0';
1764 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1766 /* Set the filesystem information */
1767 /* Note: we only emulate a FAT fs at present */
1769 if (filename_len) {
1770 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1771 *filename_len = 12;
1772 else
1773 *filename_len = 255;
1775 if (flags)
1777 *flags=0;
1778 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1779 *flags|=FS_CASE_SENSITIVE;
1780 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1781 *flags|=FS_CASE_IS_PRESERVED;
1783 if (fsname) {
1784 /* Diablo checks that return code ... */
1785 if (DOSDrives[drive].type == DRIVE_CDROM)
1786 lstrcpynA( fsname, "CDFS", fsname_len );
1787 else
1788 lstrcpynA( fsname, "FAT", fsname_len );
1790 return TRUE;
1794 /***********************************************************************
1795 * GetVolumeInformationW (KERNEL32.@)
1797 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1798 DWORD label_len, DWORD *serial,
1799 DWORD *filename_len, DWORD *flags,
1800 LPWSTR fsname, DWORD fsname_len )
1802 LPSTR xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1803 LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1804 LPSTR xfsname = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1805 BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1806 filename_len, flags, xfsname,
1807 fsname_len );
1808 if (ret)
1810 if (label) MultiByteToWideChar( CP_ACP, 0, xvolname, -1, label, label_len );
1811 if (fsname) MultiByteToWideChar( CP_ACP, 0, xfsname, -1, fsname, fsname_len );
1813 HeapFree( GetProcessHeap(), 0, xroot );
1814 HeapFree( GetProcessHeap(), 0, xvolname );
1815 HeapFree( GetProcessHeap(), 0, xfsname );
1816 return ret;
1819 /***********************************************************************
1820 * SetVolumeLabelA (KERNEL32.@)
1822 BOOL WINAPI SetVolumeLabelA( LPCSTR root, LPCSTR volname )
1824 int drive;
1826 /* FIXME, SetLastErrors missing */
1828 if (!root) drive = DRIVE_GetCurrentDrive();
1829 else
1831 if ((root[1]) && (root[1] != ':'))
1833 WARN("invalid root '%s'\n",root);
1834 return FALSE;
1836 drive = toupper(root[0]) - 'A';
1838 if (!DRIVE_IsValid( drive )) return FALSE;
1840 /* some copy protection stuff check this */
1841 if (DOSDrives[drive].type == DRIVE_CDROM) return FALSE;
1843 FIXME("(%s,%s),stub!\n", root, volname);
1844 return TRUE;
1847 /***********************************************************************
1848 * SetVolumeLabelW (KERNEL32.@)
1850 BOOL WINAPI SetVolumeLabelW(LPCWSTR rootpath,LPCWSTR volname)
1852 LPSTR xroot, xvol;
1853 BOOL ret;
1855 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath);
1856 xvol = HEAP_strdupWtoA( GetProcessHeap(), 0, volname);
1857 ret = SetVolumeLabelA( xroot, xvol );
1858 HeapFree( GetProcessHeap(), 0, xroot );
1859 HeapFree( GetProcessHeap(), 0, xvol );
1860 return ret;