Partially repair hotspot handling.
[wine/testsucceed.git] / files / drive.c
blob9ed9c58ec427f1313b2650d3723d95fc2dfdc224
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)
13 #include "config.h"
14 #include "wine/port.h"
16 #include <assert.h>
17 #include <ctype.h>
18 #include <string.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <errno.h>
25 #include <unistd.h>
27 #ifdef HAVE_SYS_PARAM_H
28 # include <sys/param.h>
29 #endif
30 #ifdef STATFS_DEFINED_BY_SYS_VFS
31 # include <sys/vfs.h>
32 #else
33 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
34 # include <sys/mount.h>
35 # else
36 # ifdef STATFS_DEFINED_BY_SYS_STATFS
37 # include <sys/statfs.h>
38 # endif
39 # endif
40 #endif
42 #include "winbase.h"
43 #include "ntddk.h"
44 #include "wine/winbase16.h" /* for GetCurrentTask */
45 #include "winerror.h"
46 #include "drive.h"
47 #include "file.h"
48 #include "heap.h"
49 #include "msdos.h"
50 #include "options.h"
51 #include "task.h"
52 #include "debugtools.h"
53 #include "wine/server.h"
54 #include "winioctl.h"
55 #include "ntddstor.h"
56 #include "ntddcdrm.h"
58 DEFAULT_DEBUG_CHANNEL(dosfs);
59 DECLARE_DEBUG_CHANNEL(file);
61 typedef struct
63 char *root; /* root dir in Unix format without trailing / */
64 char *dos_cwd; /* cwd in DOS format without leading or trailing \ */
65 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
66 char *device; /* raw device path */
67 char label_conf[12]; /* drive label as cfg'd in wine config */
68 char label_read[12]; /* drive label as read from device */
69 DWORD serial_conf; /* drive serial number as cfg'd in wine config */
70 UINT type; /* drive type */
71 UINT flags; /* drive flags */
72 dev_t dev; /* unix device number */
73 ino_t ino; /* unix inode number */
74 } DOSDRIVE;
77 static const char * const DRIVE_Types[] =
79 "", /* DRIVE_UNKNOWN */
80 "", /* DRIVE_NO_ROOT_DIR */
81 "floppy", /* DRIVE_REMOVABLE */
82 "hd", /* DRIVE_FIXED */
83 "network", /* DRIVE_REMOTE */
84 "cdrom", /* DRIVE_CDROM */
85 "ramdisk" /* DRIVE_RAMDISK */
89 /* Known filesystem types */
91 typedef struct
93 const char *name;
94 UINT flags;
95 } FS_DESCR;
97 static const FS_DESCR DRIVE_Filesystems[] =
99 { "unix", DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
100 { "msdos", DRIVE_SHORT_NAMES },
101 { "dos", DRIVE_SHORT_NAMES },
102 { "fat", DRIVE_SHORT_NAMES },
103 { "vfat", DRIVE_CASE_PRESERVING },
104 { "win95", DRIVE_CASE_PRESERVING },
105 { NULL, 0 }
109 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
110 static int DRIVE_CurDrive = -1;
112 static HTASK16 DRIVE_LastTask = 0;
114 /* strdup on the process heap */
115 inline static char *heap_strdup( const char *str )
117 INT len = strlen(str) + 1;
118 LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
119 if (p) memcpy( p, str, len );
120 return p;
123 /***********************************************************************
124 * DRIVE_GetDriveType
126 static UINT DRIVE_GetDriveType( const char *name )
128 char buffer[20];
129 int i;
131 PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
132 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
134 if (!strcasecmp( buffer, DRIVE_Types[i] )) return i;
136 MESSAGE("%s: unknown drive type '%s', defaulting to 'hd'.\n",
137 name, buffer );
138 return DRIVE_FIXED;
142 /***********************************************************************
143 * DRIVE_GetFSFlags
145 static UINT DRIVE_GetFSFlags( const char *name, const char *value )
147 const FS_DESCR *descr;
149 for (descr = DRIVE_Filesystems; descr->name; descr++)
150 if (!strcasecmp( value, descr->name )) return descr->flags;
151 MESSAGE("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
152 name, value );
153 return DRIVE_CASE_PRESERVING;
157 /***********************************************************************
158 * DRIVE_Init
160 int DRIVE_Init(void)
162 int i, len, count = 0;
163 char name[] = "Drive A";
164 char drive_env[] = "=A:";
165 char path[MAX_PATHNAME_LEN];
166 char buffer[80];
167 struct stat drive_stat_buffer;
168 char *p;
169 DOSDRIVE *drive;
171 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
173 PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
174 if (path[0])
176 p = path + strlen(path) - 1;
177 while ((p > path) && (*p == '/')) *p-- = '\0';
179 if (path[0] == '/')
181 drive->root = heap_strdup( path );
183 else
185 /* relative paths are relative to config dir */
186 const char *config = get_config_dir();
187 drive->root = HeapAlloc( GetProcessHeap(), 0, strlen(config) + strlen(path) + 2 );
188 sprintf( drive->root, "%s/%s", config, path );
191 if (stat( drive->root, &drive_stat_buffer ))
193 MESSAGE("Could not stat %s (%s), ignoring drive %c:\n",
194 drive->root, strerror(errno), 'A' + i);
195 HeapFree( GetProcessHeap(), 0, drive->root );
196 drive->root = NULL;
197 continue;
199 if (!S_ISDIR(drive_stat_buffer.st_mode))
201 MESSAGE("%s is not a directory, ignoring drive %c:\n",
202 drive->root, 'A' + i );
203 HeapFree( GetProcessHeap(), 0, drive->root );
204 drive->root = NULL;
205 continue;
208 drive->dos_cwd = heap_strdup( "" );
209 drive->unix_cwd = heap_strdup( "" );
210 drive->type = DRIVE_GetDriveType( name );
211 drive->device = NULL;
212 drive->flags = 0;
213 drive->dev = drive_stat_buffer.st_dev;
214 drive->ino = drive_stat_buffer.st_ino;
216 /* Get the drive label */
217 PROFILE_GetWineIniString( name, "Label", "", drive->label_conf, 12 );
218 if ((len = strlen(drive->label_conf)) < 11)
220 /* Pad label with spaces */
221 memset( drive->label_conf + len, ' ', 11 - len );
222 drive->label_conf[11] = '\0';
225 /* Get the serial number */
226 PROFILE_GetWineIniString( name, "Serial", "12345678",
227 buffer, sizeof(buffer) );
228 drive->serial_conf = strtoul( buffer, NULL, 16 );
230 /* Get the filesystem type */
231 PROFILE_GetWineIniString( name, "Filesystem", "win95",
232 buffer, sizeof(buffer) );
233 drive->flags = DRIVE_GetFSFlags( name, buffer );
235 /* Get the device */
236 PROFILE_GetWineIniString( name, "Device", "",
237 buffer, sizeof(buffer) );
238 if (buffer[0])
240 drive->device = heap_strdup( buffer );
241 if (PROFILE_GetWineIniBool( name, "ReadVolInfo", 1))
242 drive->flags |= DRIVE_READ_VOL_INFO;
245 /* Get the FailReadOnly flag */
246 if (PROFILE_GetWineIniBool( name, "FailReadOnly", 0 ))
247 drive->flags |= DRIVE_FAIL_READ_ONLY;
249 /* Make the first hard disk the current drive */
250 if ((DRIVE_CurDrive == -1) && (drive->type == DRIVE_FIXED))
251 DRIVE_CurDrive = i;
253 count++;
254 TRACE("%s: path=%s type=%s label='%s' serial=%08lx "
255 "flags=%08x dev=%x ino=%x\n",
256 name, drive->root, DRIVE_Types[drive->type],
257 drive->label_conf, drive->serial_conf, drive->flags,
258 (int)drive->dev, (int)drive->ino );
260 else WARN("%s: not defined\n", name );
263 if (!count)
265 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
266 /* Create a C drive pointing to Unix root dir */
267 DOSDrives[2].root = heap_strdup( "/" );
268 DOSDrives[2].dos_cwd = heap_strdup( "" );
269 DOSDrives[2].unix_cwd = heap_strdup( "" );
270 strcpy( DOSDrives[2].label_conf, "Drive C " );
271 DOSDrives[2].serial_conf = 12345678;
272 DOSDrives[2].type = DRIVE_FIXED;
273 DOSDrives[2].device = NULL;
274 DOSDrives[2].flags = 0;
275 DRIVE_CurDrive = 2;
278 /* Make sure the current drive is valid */
279 if (DRIVE_CurDrive == -1)
281 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
283 if (drive->root && !(drive->flags & DRIVE_DISABLED))
285 DRIVE_CurDrive = i;
286 break;
291 /* get current working directory info for all drives */
292 for (i = 0; i < MAX_DOS_DRIVES; i++, drive_env[1]++)
294 if (!GetEnvironmentVariableA(drive_env, path, sizeof(path))) continue;
295 /* sanity check */
296 if (toupper(path[0]) != drive_env[1] || path[1] != ':') continue;
297 DRIVE_Chdir( i, path + 2 );
299 return 1;
303 /***********************************************************************
304 * DRIVE_IsValid
306 int DRIVE_IsValid( int drive )
308 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
309 return (DOSDrives[drive].root &&
310 !(DOSDrives[drive].flags & DRIVE_DISABLED));
314 /***********************************************************************
315 * DRIVE_GetCurrentDrive
317 int DRIVE_GetCurrentDrive(void)
319 TDB *pTask = TASK_GetCurrent();
320 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
321 return DRIVE_CurDrive;
325 /***********************************************************************
326 * DRIVE_SetCurrentDrive
328 int DRIVE_SetCurrentDrive( int drive )
330 TDB *pTask = TASK_GetCurrent();
331 if (!DRIVE_IsValid( drive ))
333 SetLastError( ERROR_INVALID_DRIVE );
334 return 0;
336 TRACE("%c:\n", 'A' + drive );
337 DRIVE_CurDrive = drive;
338 if (pTask) pTask->curdrive = drive | 0x80;
339 chdir(DRIVE_GetUnixCwd(drive));
340 return 1;
344 /***********************************************************************
345 * DRIVE_FindDriveRoot
347 * Find a drive for which the root matches the beginning of the given path.
348 * This can be used to translate a Unix path into a drive + DOS path.
349 * Return value is the drive, or -1 on error. On success, path is modified
350 * to point to the beginning of the DOS path.
352 int DRIVE_FindDriveRoot( const char **path )
354 /* idea: check at all '/' positions.
355 * If the device and inode of that path is identical with the
356 * device and inode of the current drive then we found a solution.
357 * If there is another drive pointing to a deeper position in
358 * the file tree, we want to find that one, not the earlier solution.
360 int drive, rootdrive = -1;
361 char buffer[MAX_PATHNAME_LEN];
362 char *next = buffer;
363 const char *p = *path;
364 struct stat st;
366 strcpy( buffer, "/" );
367 for (;;)
369 if (stat( buffer, &st ) || !S_ISDIR( st.st_mode )) break;
371 /* Find the drive */
373 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
375 if (!DOSDrives[drive].root ||
376 (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
378 if ((DOSDrives[drive].dev == st.st_dev) &&
379 (DOSDrives[drive].ino == st.st_ino))
381 rootdrive = drive;
382 *path = p;
383 break;
387 /* Get the next path component */
389 *next++ = '/';
390 while ((*p == '/') || (*p == '\\')) p++;
391 if (!*p) break;
392 while (!IS_END_OF_NAME(*p)) *next++ = *p++;
393 *next = 0;
395 *next = 0;
397 if (rootdrive != -1)
398 TRACE("%s -> drive %c:, root='%s', name='%s'\n",
399 buffer, 'A' + rootdrive, DOSDrives[rootdrive].root, *path );
400 return rootdrive;
404 /***********************************************************************
405 * DRIVE_GetRoot
407 const char * DRIVE_GetRoot( int drive )
409 if (!DRIVE_IsValid( drive )) return NULL;
410 return DOSDrives[drive].root;
414 /***********************************************************************
415 * DRIVE_GetDosCwd
417 const char * DRIVE_GetDosCwd( int drive )
419 TDB *pTask = TASK_GetCurrent();
420 if (!DRIVE_IsValid( drive )) return NULL;
422 /* Check if we need to change the directory to the new task. */
423 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
424 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
425 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
427 /* Perform the task-switch */
428 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
429 DRIVE_LastTask = GetCurrentTask();
431 return DOSDrives[drive].dos_cwd;
435 /***********************************************************************
436 * DRIVE_GetUnixCwd
438 const char * DRIVE_GetUnixCwd( int drive )
440 TDB *pTask = TASK_GetCurrent();
441 if (!DRIVE_IsValid( drive )) return NULL;
443 /* Check if we need to change the directory to the new task. */
444 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
445 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
446 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
448 /* Perform the task-switch */
449 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
450 DRIVE_LastTask = GetCurrentTask();
452 return DOSDrives[drive].unix_cwd;
456 /***********************************************************************
457 * DRIVE_GetDevice
459 const char * DRIVE_GetDevice( int drive )
461 return (DRIVE_IsValid( drive )) ? DOSDrives[drive].device : NULL;
464 /******************************************************************
465 * static WORD CDROM_Data_FindBestVoldesc
469 static WORD CDROM_Data_FindBestVoldesc(int fd)
471 BYTE cur_vd_type, max_vd_type = 0;
472 unsigned int offs, best_offs = 0, extra_offs = 0;
473 char sig[3];
475 for (offs = 0x8000; offs <= 0x9800; offs += 0x800)
477 /* if 'CDROM' occurs at position 8, this is a pre-iso9660 cd, and
478 * the volume label is displaced forward by 8
480 lseek(fd, offs + 11, SEEK_SET); /* check for non-ISO9660 signature */
481 read(fd, &sig, 3);
482 if ((sig[0] == 'R') && (sig[1] == 'O') && (sig[2]=='M'))
484 extra_offs = 8;
486 lseek(fd, offs + extra_offs, SEEK_SET);
487 read(fd, &cur_vd_type, 1);
488 if (cur_vd_type == 0xff) /* voldesc set terminator */
489 break;
490 if (cur_vd_type > max_vd_type)
492 max_vd_type = cur_vd_type;
493 best_offs = offs + extra_offs;
496 return best_offs;
499 /***********************************************************************
500 * DRIVE_ReadSuperblock
502 * NOTE
503 * DRIVE_SetLabel and DRIVE_SetSerialNumber use this in order
504 * to check, that they are writing on a FAT filesystem !
506 int DRIVE_ReadSuperblock (int drive, char * buff)
508 #define DRIVE_SUPER 96
509 int fd;
510 off_t offs;
512 if (memset(buff,0,DRIVE_SUPER)!=buff) return -1;
513 if ((fd=open(DOSDrives[drive].device,O_RDONLY)) == -1)
515 struct stat st;
516 if (!DOSDrives[drive].device)
517 ERR("No device configured for drive %c: !\n", 'A'+drive);
518 else
519 ERR("Couldn't open device '%s' for drive %c: ! (%s)\n", DOSDrives[drive].device, 'A'+drive,
520 (stat(DOSDrives[drive].device, &st)) ?
521 "not available or symlink not valid ?" : "no permission");
522 ERR("Can't read drive volume info ! Either pre-set it or make sure the device to read it from is accessible !\n");
523 PROFILE_UsageWineIni();
524 return -1;
527 switch(DOSDrives[drive].type)
529 case DRIVE_REMOVABLE:
530 case DRIVE_FIXED:
531 offs = 0;
532 break;
533 case DRIVE_CDROM:
534 offs = CDROM_Data_FindBestVoldesc(fd);
535 break;
536 default:
537 offs = 0;
538 break;
541 if ((offs) && (lseek(fd,offs,SEEK_SET)!=offs)) return -4;
542 if (read(fd,buff,DRIVE_SUPER)!=DRIVE_SUPER) return -2;
544 switch(DOSDrives[drive].type)
546 case DRIVE_REMOVABLE:
547 case DRIVE_FIXED:
548 if ((buff[0x26]!=0x29) || /* Check for FAT present */
549 /* FIXME: do really all FAT have their name beginning with
550 "FAT" ? (At least FAT12, FAT16 and FAT32 have :) */
551 memcmp( buff+0x36,"FAT",3))
553 ERR("The filesystem is not FAT !! (device=%s)\n",
554 DOSDrives[drive].device);
555 return -3;
557 break;
558 case DRIVE_CDROM:
559 if (strncmp(&buff[1],"CD001",5)) /* Check for iso9660 present */
560 return -3;
561 /* FIXME: do we need to check for "CDROM", too ? (high sierra) */
562 break;
563 default:
564 return -3;
565 break;
568 return close(fd);
572 /***********************************************************************
573 * DRIVE_WriteSuperblockEntry
575 * NOTE
576 * We are writing as little as possible (ie. not the whole SuperBlock)
577 * not to interfere with kernel. The drive can be mounted !
579 int DRIVE_WriteSuperblockEntry (int drive, off_t ofs, size_t len, char * buff)
581 int fd;
583 if ((fd=open(DOSDrives[drive].device,O_WRONLY))==-1)
585 ERR("Cannot open the device %s (for writing)\n",
586 DOSDrives[drive].device);
587 return -1;
589 if (lseek(fd,ofs,SEEK_SET)!=ofs)
591 ERR("lseek failed on device %s !\n",
592 DOSDrives[drive].device);
593 close(fd);
594 return -2;
596 if (write(fd,buff,len)!=len)
598 close(fd);
599 ERR("Cannot write on %s !\n",
600 DOSDrives[drive].device);
601 return -3;
603 return close (fd);
606 /******************************************************************
607 * static HANDLE CDROM_Open
611 static HANDLE CDROM_Open(int drive)
613 char root[6];
615 strcpy(root, "\\\\.\\A:");
616 root[4] += drive;
618 return CreateFileA(root, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
621 /**************************************************************************
622 * CDROM_Data_GetLabel [internal]
624 DWORD CDROM_Data_GetLabel(int drive, char *label)
626 #define LABEL_LEN 32+1
627 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
628 WORD offs = CDROM_Data_FindBestVoldesc(dev);
629 WCHAR label_read[LABEL_LEN]; /* Unicode possible, too */
630 DWORD unicode_id = 0;
632 if (offs)
634 if ((lseek(dev, offs+0x58, SEEK_SET) == offs+0x58)
635 && (read(dev, &unicode_id, 3) == 3))
637 int ver = (unicode_id & 0xff0000) >> 16;
639 if ((lseek(dev, offs+0x28, SEEK_SET) != offs+0x28)
640 || (read(dev, &label_read, LABEL_LEN) != LABEL_LEN))
641 goto failure;
643 close(dev);
644 if ((LOWORD(unicode_id) == 0x2f25) /* Unicode ID */
645 && ((ver == 0x40) || (ver == 0x43) || (ver == 0x45)))
646 { /* yippee, unicode */
647 int i;
648 WORD ch;
649 for (i=0; i<LABEL_LEN;i++)
650 { /* Motorola -> Intel Unicode conversion :-\ */
651 ch = label_read[i];
652 label_read[i] = (ch << 8) | (ch >> 8);
654 WideCharToMultiByte( CP_ACP, 0, label_read, -1, label, 12, NULL, NULL );
655 label[11] = 0;
657 else
659 strncpy(label, (LPSTR)label_read, 11);
660 label[11] = '\0';
662 return 1;
665 failure:
666 close(dev);
667 ERR("error reading label !\n");
668 return 0;
671 /**************************************************************************
672 * CDROM_GetLabel [internal]
674 static DWORD CDROM_GetLabel(int drive, char *label)
676 HANDLE h = CDROM_Open(drive);
677 CDROM_DISK_DATA cdd;
678 DWORD br;
679 DWORD ret = 1;
681 if (!h || !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
682 return 0;
684 switch (cdd.DiskData & 0x03)
686 case CDROM_DISK_DATA_TRACK:
687 if (!CDROM_Data_GetLabel(drive, label))
688 ret = 0;
689 break;
690 case CDROM_DISK_AUDIO_TRACK:
691 strcpy(label, "Audio CD ");
692 break;
693 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
694 FIXME("Need to get the label of a mixed mode CD: not implemented yet !\n");
695 /* fall through */
696 case 0:
697 ret = 0;
698 break;
700 TRACE("CD: label is '%s'.\n", label);
702 return ret;
704 /***********************************************************************
705 * DRIVE_GetLabel
707 const char * DRIVE_GetLabel( int drive )
709 int read = 0;
710 char buff[DRIVE_SUPER];
711 int offs = -1;
713 if (!DRIVE_IsValid( drive )) return NULL;
714 if (DOSDrives[drive].type == DRIVE_CDROM)
716 read = CDROM_GetLabel(drive, DOSDrives[drive].label_read);
718 else
719 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
721 if (DRIVE_ReadSuperblock(drive,(char *) buff))
722 ERR("Invalid or unreadable superblock on %s (%c:).\n",
723 DOSDrives[drive].device, (char)(drive+'A'));
724 else {
725 if (DOSDrives[drive].type == DRIVE_REMOVABLE ||
726 DOSDrives[drive].type == DRIVE_FIXED)
727 offs = 0x2b;
729 /* FIXME: ISO9660 uses a 32 bytes long label. Should we do also? */
730 if (offs != -1) memcpy(DOSDrives[drive].label_read,buff+offs,11);
731 DOSDrives[drive].label_read[11]='\0';
732 read = 1;
736 return (read) ?
737 DOSDrives[drive].label_read : DOSDrives[drive].label_conf;
740 #define CDFRAMES_PERSEC 75
741 #define CDFRAMES_PERMIN (CDFRAMES_PERSEC * 60)
742 #define FRAME_OF_ADDR(a) ((a)[0] * CDFRAMES_PERMIN + (a)[1] * CDFRAMES_PERSEC + (a)[2])
743 #define FRAME_OF_TOC(toc, idx) FRAME_OF_ADDR((toc).TrackData[idx - (toc).FirstTrack].Address)
745 /**************************************************************************
746 * CDROM_Audio_GetSerial [internal]
748 static DWORD CDROM_Audio_GetSerial(HANDLE h)
750 unsigned long serial = 0;
751 int i;
752 WORD wMagic;
753 DWORD dwStart, dwEnd, br;
754 CDROM_TOC toc;
756 if (!DeviceIoControl(h, IOCTL_CDROM_READ_TOC, NULL, 0, &toc, sizeof(toc), &br, 0))
757 return 0;
760 * wMagic collects the wFrames from track 1
761 * dwStart, dwEnd collect the beginning and end of the disc respectively, in
762 * frames.
763 * There it is collected for correcting the serial when there are less than
764 * 3 tracks.
766 wMagic = toc.TrackData[0].Address[2];
767 dwStart = FRAME_OF_TOC(toc, toc.FirstTrack);
769 for (i = 0; i <= toc.LastTrack - toc.FirstTrack; i++) {
770 serial += (toc.TrackData[i].Address[0] << 16) |
771 (toc.TrackData[i].Address[1] << 8) | toc.TrackData[i].Address[2];
773 dwEnd = FRAME_OF_TOC(toc, toc.LastTrack + 1);
775 if (toc.LastTrack - toc.FirstTrack + 1 < 3)
776 serial += wMagic + (dwEnd - dwStart);
778 return serial;
781 /**************************************************************************
782 * CDROM_Data_GetSerial [internal]
784 static DWORD CDROM_Data_GetSerial(int drive)
786 int dev = open(DOSDrives[drive].device, O_RDONLY|O_NONBLOCK);
787 WORD offs;
788 union {
789 unsigned long val;
790 unsigned char p[4];
791 } serial;
792 BYTE b0 = 0, b1 = 1, b2 = 2, b3 = 3;
795 if (dev == -1) return 0;
796 offs = CDROM_Data_FindBestVoldesc(dev);
798 serial.val = 0;
799 if (offs)
801 BYTE buf[2048];
802 OSVERSIONINFOA ovi;
803 int i;
805 lseek(dev, offs, SEEK_SET);
806 read(dev, buf, 2048);
808 * OK, another braindead one... argh. Just believe it.
809 * Me$$ysoft chose to reverse the serial number in NT4/W2K.
810 * It's true and nobody will ever be able to change it.
812 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
813 GetVersionExA(&ovi);
814 if ((ovi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (ovi.dwMajorVersion >= 4))
816 b0 = 3; b1 = 2; b2 = 1; b3 = 0;
818 for (i = 0; i < 2048; i += 4)
820 /* DON'T optimize this into DWORD !! (breaks overflow) */
821 serial.p[b0] += buf[i+b0];
822 serial.p[b1] += buf[i+b1];
823 serial.p[b2] += buf[i+b2];
824 serial.p[b3] += buf[i+b3];
827 close(dev);
828 return serial.val;
831 /**************************************************************************
832 * CDROM_GetSerial [internal]
834 static DWORD CDROM_GetSerial(int drive)
836 DWORD serial = 0;
837 HANDLE h = CDROM_Open(drive);
838 CDROM_DISK_DATA cdd;
839 DWORD br;
841 if (!h || ! !DeviceIoControl(h, IOCTL_CDROM_DISK_TYPE, NULL, 0, &cdd, sizeof(cdd), &br, 0))
842 return 0;
844 switch (cdd.DiskData & 0x03)
846 case CDROM_DISK_DATA_TRACK:
847 /* hopefully a data CD */
848 serial = CDROM_Data_GetSerial(drive);
849 break;
850 case CDROM_DISK_AUDIO_TRACK:
851 /* fall thru */
852 case CDROM_DISK_DATA_TRACK|CDROM_DISK_AUDIO_TRACK:
853 serial = CDROM_Audio_GetSerial(h);
854 break;
855 case 0:
856 break;
859 if (serial)
860 TRACE("CD serial number is %04x-%04x.\n", HIWORD(serial), LOWORD(serial));
862 CloseHandle(h);
864 return serial;
867 /***********************************************************************
868 * DRIVE_GetSerialNumber
870 DWORD DRIVE_GetSerialNumber( int drive )
872 DWORD serial = 0;
873 char buff[DRIVE_SUPER];
875 if (!DRIVE_IsValid( drive )) return 0;
877 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
879 switch(DOSDrives[drive].type)
881 case DRIVE_REMOVABLE:
882 case DRIVE_FIXED:
883 if (DRIVE_ReadSuperblock(drive,(char *) buff))
884 MESSAGE("Invalid or unreadable superblock on %s (%c:)."
885 " Maybe not FAT?\n" ,
886 DOSDrives[drive].device, 'A'+drive);
887 else
888 serial = *((DWORD*)(buff+0x27));
889 break;
890 case DRIVE_CDROM:
891 serial = CDROM_GetSerial(drive);
892 break;
893 default:
894 FIXME("Serial number reading from file system on drive %c: not supported yet.\n", drive+'A');
898 return (serial) ? serial : DOSDrives[drive].serial_conf;
902 /***********************************************************************
903 * DRIVE_SetSerialNumber
905 int DRIVE_SetSerialNumber( int drive, DWORD serial )
907 char buff[DRIVE_SUPER];
909 if (!DRIVE_IsValid( drive )) return 0;
911 if (DOSDrives[drive].flags & DRIVE_READ_VOL_INFO)
913 if ((DOSDrives[drive].type != DRIVE_REMOVABLE) &&
914 (DOSDrives[drive].type != DRIVE_FIXED)) return 0;
915 /* check, if the drive has a FAT filesystem */
916 if (DRIVE_ReadSuperblock(drive, buff)) return 0;
917 if (DRIVE_WriteSuperblockEntry(drive, 0x27, 4, (char *) &serial)) return 0;
918 return 1;
921 if (DOSDrives[drive].type == DRIVE_CDROM) return 0;
922 DOSDrives[drive].serial_conf = serial;
923 return 1;
927 /***********************************************************************
928 * DRIVE_GetType
930 static UINT DRIVE_GetType( int drive )
932 if (!DRIVE_IsValid( drive )) return DRIVE_UNKNOWN;
933 return DOSDrives[drive].type;
937 /***********************************************************************
938 * DRIVE_GetFlags
940 UINT DRIVE_GetFlags( int drive )
942 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
943 return DOSDrives[drive].flags;
947 /***********************************************************************
948 * DRIVE_Chdir
950 int DRIVE_Chdir( int drive, const char *path )
952 DOS_FULL_NAME full_name;
953 char buffer[MAX_PATHNAME_LEN];
954 LPSTR unix_cwd;
955 BY_HANDLE_FILE_INFORMATION info;
956 TDB *pTask = TASK_GetCurrent();
958 strcpy( buffer, "A:" );
959 buffer[0] += drive;
960 TRACE("(%s,%s)\n", buffer, path );
961 lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
963 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
964 if (!FILE_Stat( full_name.long_name, &info )) return 0;
965 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
967 SetLastError( ERROR_FILE_NOT_FOUND );
968 return 0;
970 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
971 while (*unix_cwd == '/') unix_cwd++;
973 TRACE("(%c:): unix_cwd=%s dos_cwd=%s\n",
974 'A' + drive, unix_cwd, full_name.short_name + 3 );
976 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].dos_cwd );
977 HeapFree( GetProcessHeap(), 0, DOSDrives[drive].unix_cwd );
978 DOSDrives[drive].dos_cwd = heap_strdup( full_name.short_name + 3 );
979 DOSDrives[drive].unix_cwd = heap_strdup( unix_cwd );
981 if (pTask && (pTask->curdrive & 0x80) &&
982 ((pTask->curdrive & ~0x80) == drive))
984 lstrcpynA( pTask->curdir, full_name.short_name + 2,
985 sizeof(pTask->curdir) );
986 DRIVE_LastTask = GetCurrentTask();
987 chdir(unix_cwd); /* Only change if on current drive */
989 return 1;
993 /***********************************************************************
994 * DRIVE_Disable
996 int DRIVE_Disable( int drive )
998 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1000 SetLastError( ERROR_INVALID_DRIVE );
1001 return 0;
1003 DOSDrives[drive].flags |= DRIVE_DISABLED;
1004 return 1;
1008 /***********************************************************************
1009 * DRIVE_Enable
1011 int DRIVE_Enable( int drive )
1013 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
1015 SetLastError( ERROR_INVALID_DRIVE );
1016 return 0;
1018 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
1019 return 1;
1023 /***********************************************************************
1024 * DRIVE_SetLogicalMapping
1026 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
1028 /* If new_drive is already valid, do nothing and return 0
1029 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
1031 DOSDRIVE *old, *new;
1033 old = DOSDrives + existing_drive;
1034 new = DOSDrives + new_drive;
1036 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
1037 !old->root ||
1038 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
1040 SetLastError( ERROR_INVALID_DRIVE );
1041 return 0;
1044 if ( new->root )
1046 TRACE("Can't map drive %c: to already existing drive %c:\n",
1047 'A' + existing_drive, 'A' + new_drive );
1048 /* it is already mapped there, so return success */
1049 if (!strcmp(old->root,new->root))
1050 return 1;
1051 return 0;
1054 new->root = heap_strdup( old->root );
1055 new->dos_cwd = heap_strdup( old->dos_cwd );
1056 new->unix_cwd = heap_strdup( old->unix_cwd );
1057 new->device = heap_strdup( old->device );
1058 memcpy ( new->label_conf, old->label_conf, 12 );
1059 memcpy ( new->label_read, old->label_read, 12 );
1060 new->serial_conf = old->serial_conf;
1061 new->type = old->type;
1062 new->flags = old->flags;
1063 new->dev = old->dev;
1064 new->ino = old->ino;
1066 TRACE("Drive %c: is now equal to drive %c:\n",
1067 'A' + new_drive, 'A' + existing_drive );
1069 return 1;
1073 /***********************************************************************
1074 * DRIVE_OpenDevice
1076 * Open the drive raw device and return a Unix fd (or -1 on error).
1078 int DRIVE_OpenDevice( int drive, int flags )
1080 if (!DRIVE_IsValid( drive )) return -1;
1081 return open( DOSDrives[drive].device, flags );
1085 /***********************************************************************
1086 * DRIVE_RawRead
1088 * Read raw sectors from a device
1090 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1092 int fd;
1094 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1096 lseek( fd, begin * 512, SEEK_SET );
1097 /* FIXME: check errors */
1098 read( fd, dataptr, nr_sect * 512 );
1099 close( fd );
1101 else
1103 memset(dataptr, 0, nr_sect * 512);
1104 if (fake_success)
1106 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
1107 if (begin == 1) *dataptr = 0xf8;
1109 else
1110 return 0;
1112 return 1;
1116 /***********************************************************************
1117 * DRIVE_RawWrite
1119 * Write raw sectors to a device
1121 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
1123 int fd;
1125 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
1127 lseek( fd, begin * 512, SEEK_SET );
1128 /* FIXME: check errors */
1129 write( fd, dataptr, nr_sect * 512 );
1130 close( fd );
1132 else
1133 if (!(fake_success))
1134 return 0;
1136 return 1;
1140 /***********************************************************************
1141 * DRIVE_GetFreeSpace
1143 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
1144 PULARGE_INTEGER available )
1146 struct statfs info;
1148 if (!DRIVE_IsValid(drive))
1150 SetLastError( ERROR_INVALID_DRIVE );
1151 return 0;
1154 /* FIXME: add autoconf check for this */
1155 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
1156 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
1157 #else
1158 if (statfs( DOSDrives[drive].root, &info) < 0)
1159 #endif
1161 FILE_SetDosError();
1162 WARN("cannot do statfs(%s)\n", DOSDrives[drive].root);
1163 return 0;
1166 size->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bsize, info.f_blocks );
1167 #ifdef STATFS_HAS_BAVAIL
1168 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bavail, info.f_bsize );
1169 #else
1170 # ifdef STATFS_HAS_BFREE
1171 available->QuadPart = RtlEnlargedUnsignedMultiply( info.f_bfree, info.f_bsize );
1172 # else
1173 # error "statfs has no bfree/bavail member!"
1174 # endif
1175 #endif
1176 if (DOSDrives[drive].type == DRIVE_CDROM)
1177 { /* ALWAYS 0, even if no real CD-ROM mounted there !! */
1178 available->QuadPart = 0;
1180 return 1;
1183 /***********************************************************************
1184 * DRIVE_GetCurrentDirectory
1185 * Returns "X:\\path\\etc\\".
1187 * Despite the API description, return required length including the
1188 * terminating null when buffer too small. This is the real behaviour.
1191 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPSTR buf )
1193 UINT ret;
1194 const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
1196 assert(s);
1197 ret = strlen(s) + 3; /* length of WHOLE current directory */
1198 if (ret >= buflen) return ret + 1;
1199 lstrcpynA( buf, "A:\\", min( 4u, buflen ) );
1200 if (buflen) buf[0] += DRIVE_GetCurrentDrive();
1201 if (buflen > 3) lstrcpynA( buf + 3, s, buflen - 3 );
1202 return ret;
1206 /***********************************************************************
1207 * DRIVE_BuildEnv
1209 * Build the environment array containing the drives' current directories.
1210 * Resulting pointer must be freed with HeapFree.
1212 char *DRIVE_BuildEnv(void)
1214 int i, length = 0;
1215 const char *cwd[MAX_DOS_DRIVES];
1216 char *env, *p;
1218 for (i = 0; i < MAX_DOS_DRIVES; i++)
1220 if ((cwd[i] = DRIVE_GetDosCwd(i)) && cwd[i][0]) length += strlen(cwd[i]) + 8;
1222 if (!(env = HeapAlloc( GetProcessHeap(), 0, length+1 ))) return NULL;
1223 for (i = 0, p = env; i < MAX_DOS_DRIVES; i++)
1225 if (cwd[i] && cwd[i][0])
1226 p += sprintf( p, "=%c:=%c:\\%s", 'A'+i, 'A'+i, cwd[i] ) + 1;
1228 *p = 0;
1229 return env;
1233 /***********************************************************************
1234 * GetDiskFreeSpace (KERNEL.422)
1236 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
1237 LPDWORD sector_bytes, LPDWORD free_clusters,
1238 LPDWORD total_clusters )
1240 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
1241 free_clusters, total_clusters );
1245 /***********************************************************************
1246 * GetDiskFreeSpaceA (KERNEL32.@)
1248 * Fails if expression resulting from current drive's dir and "root"
1249 * is not a root dir of the target drive.
1251 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
1252 * if the corresponding info is unneeded.
1254 * FIXME: needs to support UNC names from Win95 OSR2 on.
1256 * Behaviour under Win95a:
1257 * CurrDir root result
1258 * "E:\\TEST" "E:" FALSE
1259 * "E:\\" "E:" TRUE
1260 * "E:\\" "E" FALSE
1261 * "E:\\" "\\" TRUE
1262 * "E:\\TEST" "\\" TRUE
1263 * "E:\\TEST" ":\\" FALSE
1264 * "E:\\TEST" "E:\\" TRUE
1265 * "E:\\TEST" "" FALSE
1266 * "E:\\" "" FALSE (!)
1267 * "E:\\" 0x0 TRUE
1268 * "E:\\TEST" 0x0 TRUE (!)
1269 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
1270 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
1272 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
1273 LPDWORD sector_bytes, LPDWORD free_clusters,
1274 LPDWORD total_clusters )
1276 int drive, sec_size;
1277 ULARGE_INTEGER size,available;
1278 LPCSTR path;
1279 DWORD cluster_sec;
1281 if ((!root) || (strcmp(root,"\\") == 0))
1282 drive = DRIVE_GetCurrentDrive();
1283 else
1284 if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
1286 drive = toupper(root[0]) - 'A';
1287 path = &root[2];
1288 if (path[0] == '\0')
1289 path = DRIVE_GetDosCwd(drive);
1290 else
1291 if (path[0] == '\\')
1292 path++;
1293 if (path[0]) /* oops, we are in a subdir */
1295 SetLastError(ERROR_INVALID_NAME);
1296 return FALSE;
1299 else
1301 SetLastError(ERROR_INVALID_NAME);
1302 return FALSE;
1305 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1307 /* Cap the size and available at 2GB as per specs. */
1308 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
1310 size.s.HighPart = 0;
1311 size.s.LowPart = 0x7fffffff;
1313 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
1315 available.s.HighPart =0;
1316 available.s.LowPart = 0x7fffffff;
1318 sec_size = (DRIVE_GetType(drive)==DRIVE_CDROM) ? 2048 : 512;
1319 size.s.LowPart /= sec_size;
1320 available.s.LowPart /= sec_size;
1321 /* FIXME: probably have to adjust those variables too for CDFS */
1322 cluster_sec = 1;
1323 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
1325 if (cluster_sectors)
1326 *cluster_sectors = cluster_sec;
1327 if (sector_bytes)
1328 *sector_bytes = sec_size;
1329 if (free_clusters)
1330 *free_clusters = available.s.LowPart / cluster_sec;
1331 if (total_clusters)
1332 *total_clusters = size.s.LowPart / cluster_sec;
1333 return TRUE;
1337 /***********************************************************************
1338 * GetDiskFreeSpaceW (KERNEL32.@)
1340 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
1341 LPDWORD sector_bytes, LPDWORD free_clusters,
1342 LPDWORD total_clusters )
1344 LPSTR xroot;
1345 BOOL ret;
1347 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1348 ret = GetDiskFreeSpaceA( xroot,cluster_sectors, sector_bytes,
1349 free_clusters, total_clusters );
1350 HeapFree( GetProcessHeap(), 0, xroot );
1351 return ret;
1355 /***********************************************************************
1356 * GetDiskFreeSpaceExA (KERNEL32.@)
1358 * This function is used to acquire the size of the available and
1359 * total space on a logical volume.
1361 * RETURNS
1363 * Zero on failure, nonzero upon success. Use GetLastError to obtain
1364 * detailed error information.
1367 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
1368 PULARGE_INTEGER avail,
1369 PULARGE_INTEGER total,
1370 PULARGE_INTEGER totalfree)
1372 int drive;
1373 ULARGE_INTEGER size,available;
1375 if (!root) drive = DRIVE_GetCurrentDrive();
1376 else
1377 { /* C: always works for GetDiskFreeSpaceEx */
1378 if ((root[1]) && ((root[1] != ':') || (root[2] && root[2] != '\\')))
1380 FIXME("there are valid root names which are not supported yet\n");
1381 /* ..like UNC names, for instance. */
1383 WARN("invalid root '%s'\n", root );
1384 return FALSE;
1386 drive = toupper(root[0]) - 'A';
1389 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
1391 if (total)
1393 total->s.HighPart = size.s.HighPart;
1394 total->s.LowPart = size.s.LowPart;
1397 if (totalfree)
1399 totalfree->s.HighPart = available.s.HighPart;
1400 totalfree->s.LowPart = available.s.LowPart;
1403 if (avail)
1405 if (FIXME_ON(dosfs))
1407 /* On Windows2000, we need to check the disk quota
1408 allocated for the user owning the calling process. We
1409 don't want to be more obtrusive than necessary with the
1410 FIXME messages, so don't print the FIXME unless Wine is
1411 actually masquerading as Windows2000. */
1413 OSVERSIONINFOA ovi;
1414 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
1415 if (GetVersionExA(&ovi))
1417 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
1418 FIXME("no per-user quota support yet\n");
1422 /* Quick hack, should eventually be fixed to work 100% with
1423 Windows2000 (see comment above). */
1424 avail->s.HighPart = available.s.HighPart;
1425 avail->s.LowPart = available.s.LowPart;
1428 return TRUE;
1431 /***********************************************************************
1432 * GetDiskFreeSpaceExW (KERNEL32.@)
1434 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
1435 PULARGE_INTEGER total,
1436 PULARGE_INTEGER totalfree)
1438 LPSTR xroot;
1439 BOOL ret;
1441 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
1442 ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
1443 HeapFree( GetProcessHeap(), 0, xroot );
1444 return ret;
1447 /***********************************************************************
1448 * GetDriveType (KERNEL.136)
1449 * This function returns the type of a drive in Win16.
1450 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
1451 * remote drive API. The return value DRIVE_REMOTE for CD-ROMs has been
1452 * verified on Win 3.11 and Windows 95. Some programs rely on it, so don't
1453 * do any pseudo-clever changes.
1455 * RETURNS
1456 * drivetype DRIVE_xxx
1458 UINT16 WINAPI GetDriveType16( UINT16 drive ) /* [in] number (NOT letter) of drive */
1460 UINT type = DRIVE_GetType(drive);
1461 TRACE("(%c:)\n", 'A' + drive );
1462 if (type == DRIVE_CDROM) type = DRIVE_REMOTE;
1463 return type;
1467 /***********************************************************************
1468 * GetDriveTypeA (KERNEL32.@)
1470 * Returns the type of the disk drive specified. If root is NULL the
1471 * root of the current directory is used.
1473 * RETURNS
1475 * Type of drive (from Win32 SDK):
1477 * DRIVE_UNKNOWN unable to find out anything about the drive
1478 * DRIVE_NO_ROOT_DIR nonexistent root dir
1479 * DRIVE_REMOVABLE the disk can be removed from the machine
1480 * DRIVE_FIXED the disk can not be removed from the machine
1481 * DRIVE_REMOTE network disk
1482 * DRIVE_CDROM CDROM drive
1483 * DRIVE_RAMDISK virtual disk in RAM
1485 UINT WINAPI GetDriveTypeA(LPCSTR root) /* [in] String describing drive */
1487 int drive;
1488 TRACE("(%s)\n", debugstr_a(root));
1490 if (NULL == root) drive = DRIVE_GetCurrentDrive();
1491 else
1493 if ((root[1]) && (root[1] != ':'))
1495 WARN("invalid root %s\n", debugstr_a(root));
1496 return DRIVE_NO_ROOT_DIR;
1498 drive = toupper(root[0]) - 'A';
1500 return DRIVE_GetType(drive);
1504 /***********************************************************************
1505 * GetDriveTypeW (KERNEL32.@)
1507 UINT WINAPI GetDriveTypeW( LPCWSTR root )
1509 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1510 UINT ret = GetDriveTypeA( xpath );
1511 HeapFree( GetProcessHeap(), 0, xpath );
1512 return ret;
1516 /***********************************************************************
1517 * GetCurrentDirectory (KERNEL.411)
1519 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1521 return (UINT16)DRIVE_GetCurrentDirectory(buflen, buf);
1525 /***********************************************************************
1526 * GetCurrentDirectoryA (KERNEL32.@)
1528 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1530 UINT ret;
1531 char longname[MAX_PATHNAME_LEN];
1532 char shortname[MAX_PATHNAME_LEN];
1533 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1534 if ( ret > MAX_PATHNAME_LEN ) {
1535 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1536 return ret;
1538 GetLongPathNameA(shortname, longname, MAX_PATHNAME_LEN);
1539 ret = strlen( longname ) + 1;
1540 if (ret > buflen) return ret;
1541 strcpy(buf, longname);
1542 return ret - 1;
1545 /***********************************************************************
1546 * GetCurrentDirectoryW (KERNEL32.@)
1548 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1550 LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
1551 UINT ret = GetCurrentDirectoryA( buflen, xpath );
1552 if (ret < buflen) ret = MultiByteToWideChar( CP_ACP, 0, xpath, -1, buf, buflen ) - 1;
1553 HeapFree( GetProcessHeap(), 0, xpath );
1554 return ret;
1558 /***********************************************************************
1559 * SetCurrentDirectory (KERNEL.412)
1561 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1563 return SetCurrentDirectoryA( dir );
1567 /***********************************************************************
1568 * SetCurrentDirectoryA (KERNEL32.@)
1570 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1572 int drive, olddrive = DRIVE_GetCurrentDrive();
1574 if (!dir) {
1575 ERR_(file)("(NULL)!\n");
1576 return FALSE;
1578 if (dir[0] && (dir[1]==':'))
1580 drive = toupper( *dir ) - 'A';
1581 dir += 2;
1583 else
1584 drive = olddrive;
1586 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1587 sets pTask->curdir only if pTask->curdrive is drive */
1588 if (!(DRIVE_SetCurrentDrive( drive )))
1589 return FALSE;
1590 /* FIXME: what about empty strings? Add a \\ ? */
1591 if (!DRIVE_Chdir( drive, dir )) {
1592 DRIVE_SetCurrentDrive(olddrive);
1593 return FALSE;
1595 return TRUE;
1599 /***********************************************************************
1600 * SetCurrentDirectoryW (KERNEL32.@)
1602 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1604 LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1605 BOOL res = SetCurrentDirectoryA( dir );
1606 HeapFree( GetProcessHeap(), 0, dir );
1607 return res;
1611 /***********************************************************************
1612 * GetLogicalDriveStringsA (KERNEL32.@)
1614 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1616 int drive, count;
1618 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1619 if (DRIVE_IsValid(drive)) count++;
1620 if ((count * 4) + 1 <= len)
1622 LPSTR p = buffer;
1623 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1624 if (DRIVE_IsValid(drive))
1626 *p++ = 'a' + drive;
1627 *p++ = ':';
1628 *p++ = '\\';
1629 *p++ = '\0';
1631 *p = '\0';
1632 return count * 4;
1634 else
1635 return (count * 4) + 1; /* account for terminating null */
1636 /* The API tells about these different return values */
1640 /***********************************************************************
1641 * GetLogicalDriveStringsW (KERNEL32.@)
1643 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1645 int drive, count;
1647 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1648 if (DRIVE_IsValid(drive)) count++;
1649 if (count * 4 * sizeof(WCHAR) <= len)
1651 LPWSTR p = buffer;
1652 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1653 if (DRIVE_IsValid(drive))
1655 *p++ = (WCHAR)('a' + drive);
1656 *p++ = (WCHAR)':';
1657 *p++ = (WCHAR)'\\';
1658 *p++ = (WCHAR)'\0';
1660 *p = (WCHAR)'\0';
1662 return count * 4 * sizeof(WCHAR);
1666 /***********************************************************************
1667 * GetLogicalDrives (KERNEL32.@)
1669 DWORD WINAPI GetLogicalDrives(void)
1671 DWORD ret = 0;
1672 int drive;
1674 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1676 if ( (DRIVE_IsValid(drive)) ||
1677 (DOSDrives[drive].type == DRIVE_CDROM)) /* audio CD is also valid */
1678 ret |= (1 << drive);
1680 return ret;
1684 /***********************************************************************
1685 * GetVolumeInformationA (KERNEL32.@)
1687 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1688 DWORD label_len, DWORD *serial,
1689 DWORD *filename_len, DWORD *flags,
1690 LPSTR fsname, DWORD fsname_len )
1692 int drive;
1693 char *cp;
1695 /* FIXME, SetLastError()s missing */
1697 if (!root) drive = DRIVE_GetCurrentDrive();
1698 else
1700 if ((root[1]) && (root[1] != ':'))
1702 WARN("invalid root '%s'\n",root);
1703 return FALSE;
1705 drive = toupper(root[0]) - 'A';
1707 if (!DRIVE_IsValid( drive )) return FALSE;
1708 if (label)
1710 lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1711 cp = label + strlen(label);
1712 while (cp != label && *(cp-1) == ' ') cp--;
1713 *cp = '\0';
1715 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1717 /* Set the filesystem information */
1718 /* Note: we only emulate a FAT fs at present */
1720 if (filename_len) {
1721 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1722 *filename_len = 12;
1723 else
1724 *filename_len = 255;
1726 if (flags)
1728 *flags=0;
1729 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1730 *flags|=FS_CASE_SENSITIVE;
1731 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1732 *flags|=FS_CASE_IS_PRESERVED;
1734 if (fsname) {
1735 /* Diablo checks that return code ... */
1736 if (DOSDrives[drive].type == DRIVE_CDROM)
1737 lstrcpynA( fsname, "CDFS", fsname_len );
1738 else
1739 lstrcpynA( fsname, "FAT", fsname_len );
1741 return TRUE;
1745 /***********************************************************************
1746 * GetVolumeInformationW (KERNEL32.@)
1748 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1749 DWORD label_len, DWORD *serial,
1750 DWORD *filename_len, DWORD *flags,
1751 LPWSTR fsname, DWORD fsname_len )
1753 LPSTR xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1754 LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1755 LPSTR xfsname = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1756 BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1757 filename_len, flags, xfsname,
1758 fsname_len );
1759 if (ret)
1761 if (label) MultiByteToWideChar( CP_ACP, 0, xvolname, -1, label, label_len );
1762 if (fsname) MultiByteToWideChar( CP_ACP, 0, xfsname, -1, fsname, fsname_len );
1764 HeapFree( GetProcessHeap(), 0, xroot );
1765 HeapFree( GetProcessHeap(), 0, xvolname );
1766 HeapFree( GetProcessHeap(), 0, xfsname );
1767 return ret;
1770 /***********************************************************************
1771 * SetVolumeLabelA (KERNEL32.@)
1773 BOOL WINAPI SetVolumeLabelA( LPCSTR root, LPCSTR volname )
1775 int drive;
1777 /* FIXME, SetLastErrors missing */
1779 if (!root) drive = DRIVE_GetCurrentDrive();
1780 else
1782 if ((root[1]) && (root[1] != ':'))
1784 WARN("invalid root '%s'\n",root);
1785 return FALSE;
1787 drive = toupper(root[0]) - 'A';
1789 if (!DRIVE_IsValid( drive )) return FALSE;
1791 /* some copy protection stuff check this */
1792 if (DOSDrives[drive].type == DRIVE_CDROM) return FALSE;
1794 FIXME("(%s,%s),stub!\n", root, volname);
1795 return TRUE;
1798 /***********************************************************************
1799 * SetVolumeLabelW (KERNEL32.@)
1801 BOOL WINAPI SetVolumeLabelW(LPCWSTR rootpath,LPCWSTR volname)
1803 LPSTR xroot, xvol;
1804 BOOL ret;
1806 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath);
1807 xvol = HEAP_strdupWtoA( GetProcessHeap(), 0, volname);
1808 ret = SetVolumeLabelA( xroot, xvol );
1809 HeapFree( GetProcessHeap(), 0, xroot );
1810 HeapFree( GetProcessHeap(), 0, xvol );
1811 return ret;