Account for the trailing NULL in GetLogicalDriveStringsA.
[wine/multimedia.git] / files / drive.c
blob3e969a5bc3ad4eb0aef624bc0688c1ea9924c83d
1 /*
2 * DOS drives handling functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
6 */
8 #include "config.h"
10 #include <assert.h>
11 #include <ctype.h>
12 #include <string.h>
13 #include <stdlib.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <fcntl.h>
17 #include <errno.h>
18 #include <unistd.h>
20 #ifdef HAVE_SYS_PARAM_H
21 # include <sys/param.h>
22 #endif
23 #ifdef STATFS_DEFINED_BY_SYS_VFS
24 # include <sys/vfs.h>
25 #else
26 # ifdef STATFS_DEFINED_BY_SYS_MOUNT
27 # include <sys/mount.h>
28 # else
29 # ifdef STATFS_DEFINED_BY_SYS_STATFS
30 # include <sys/statfs.h>
31 # endif
32 # endif
33 #endif
35 #include "winbase.h"
36 #include "wine/winbase16.h" /* for GetCurrentTask */
37 #include "wine/winestring.h" /* for lstrcpyAtoW */
38 #include "winerror.h"
39 #include "drive.h"
40 #include "file.h"
41 #include "heap.h"
42 #include "msdos.h"
43 #include "options.h"
44 #include "task.h"
45 #include "debugtools.h"
47 DECLARE_DEBUG_CHANNEL(dosfs)
48 DECLARE_DEBUG_CHANNEL(file)
50 typedef struct
52 char *root; /* root dir in Unix format without trailing / */
53 char *dos_cwd; /* cwd in DOS format without leading or trailing \ */
54 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
55 char *device; /* raw device path */
56 char label[12]; /* drive label */
57 DWORD serial; /* drive serial number */
58 DRIVETYPE type; /* drive type */
59 UINT flags; /* drive flags */
60 dev_t dev; /* unix device number */
61 ino_t ino; /* unix inode number */
62 } DOSDRIVE;
65 static const char * const DRIVE_Types[] =
67 "floppy", /* TYPE_FLOPPY */
68 "hd", /* TYPE_HD */
69 "cdrom", /* TYPE_CDROM */
70 "network" /* TYPE_NETWORK */
74 /* Known filesystem types */
76 typedef struct
78 const char *name;
79 UINT flags;
80 } FS_DESCR;
82 static const FS_DESCR DRIVE_Filesystems[] =
84 { "unix", DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
85 { "msdos", DRIVE_SHORT_NAMES },
86 { "dos", DRIVE_SHORT_NAMES },
87 { "fat", DRIVE_SHORT_NAMES },
88 { "vfat", DRIVE_CASE_PRESERVING },
89 { "win95", DRIVE_CASE_PRESERVING },
90 { NULL, 0 }
94 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
95 static int DRIVE_CurDrive = -1;
97 static HTASK16 DRIVE_LastTask = 0;
100 /***********************************************************************
101 * DRIVE_GetDriveType
103 static DRIVETYPE DRIVE_GetDriveType( const char *name )
105 char buffer[20];
106 int i;
108 PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
109 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
111 if (!strcasecmp( buffer, DRIVE_Types[i] )) return (DRIVETYPE)i;
113 MESSAGE("%s: unknown type '%s', defaulting to 'hd'.\n", name, buffer );
114 return TYPE_HD;
118 /***********************************************************************
119 * DRIVE_GetFSFlags
121 static UINT DRIVE_GetFSFlags( const char *name, const char *value )
123 const FS_DESCR *descr;
125 for (descr = DRIVE_Filesystems; descr->name; descr++)
126 if (!strcasecmp( value, descr->name )) return descr->flags;
127 MESSAGE("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
128 name, value );
129 return DRIVE_CASE_PRESERVING;
133 /***********************************************************************
134 * DRIVE_Init
136 int DRIVE_Init(void)
138 int i, len, count = 0;
139 char name[] = "Drive A";
140 char path[MAX_PATHNAME_LEN];
141 char buffer[80];
142 struct stat drive_stat_buffer;
143 char *p;
144 DOSDRIVE *drive;
146 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
148 PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
149 if (path[0])
151 p = path + strlen(path) - 1;
152 while ((p > path) && ((*p == '/') || (*p == '\\'))) *p-- = '\0';
153 if (!path[0]) strcpy( path, "/" );
155 if (stat( path, &drive_stat_buffer ))
157 MESSAGE("Could not stat %s, ignoring drive %c:\n", path, 'A' + i );
158 continue;
160 if (!S_ISDIR(drive_stat_buffer.st_mode))
162 MESSAGE("%s is not a directory, ignoring drive %c:\n",
163 path, 'A' + i );
164 continue;
167 drive->root = HEAP_strdupA( SystemHeap, 0, path );
168 drive->dos_cwd = HEAP_strdupA( SystemHeap, 0, "" );
169 drive->unix_cwd = HEAP_strdupA( SystemHeap, 0, "" );
170 drive->type = DRIVE_GetDriveType( name );
171 drive->device = NULL;
172 drive->flags = 0;
173 drive->dev = drive_stat_buffer.st_dev;
174 drive->ino = drive_stat_buffer.st_ino;
176 /* Get the drive label */
177 PROFILE_GetWineIniString( name, "Label", name, drive->label, 12 );
178 if ((len = strlen(drive->label)) < 11)
180 /* Pad label with spaces */
181 memset( drive->label + len, ' ', 11 - len );
182 drive->label[12] = '\0';
185 /* Get the serial number */
186 PROFILE_GetWineIniString( name, "Serial", "12345678",
187 buffer, sizeof(buffer) );
188 drive->serial = strtoul( buffer, NULL, 16 );
190 /* Get the filesystem type */
191 PROFILE_GetWineIniString( name, "Filesystem", "win95",
192 buffer, sizeof(buffer) );
193 drive->flags = DRIVE_GetFSFlags( name, buffer );
195 /* Get the device */
196 PROFILE_GetWineIniString( name, "Device", "",
197 buffer, sizeof(buffer) );
198 if (buffer[0])
199 drive->device = HEAP_strdupA( SystemHeap, 0, buffer );
201 /* Make the first hard disk the current drive */
202 if ((DRIVE_CurDrive == -1) && (drive->type == TYPE_HD))
203 DRIVE_CurDrive = i;
205 count++;
206 TRACE_(dosfs)("%s: path=%s type=%s label='%s' serial=%08lx flags=%08x dev=%x ino=%x\n",
207 name, path, DRIVE_Types[drive->type],
208 drive->label, drive->serial, drive->flags,
209 (int)drive->dev, (int)drive->ino );
211 else WARN_(dosfs)("%s: not defined\n", name );
214 if (!count)
216 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
217 /* Create a C drive pointing to Unix root dir */
218 DOSDrives[2].root = HEAP_strdupA( SystemHeap, 0, "/" );
219 DOSDrives[2].dos_cwd = HEAP_strdupA( SystemHeap, 0, "" );
220 DOSDrives[2].unix_cwd = HEAP_strdupA( SystemHeap, 0, "" );
221 strcpy( DOSDrives[2].label, "Drive C " );
222 DOSDrives[2].serial = 0x12345678;
223 DOSDrives[2].type = TYPE_HD;
224 DOSDrives[2].flags = 0;
225 DRIVE_CurDrive = 2;
228 /* Make sure the current drive is valid */
229 if (DRIVE_CurDrive == -1)
231 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
233 if (drive->root && !(drive->flags & DRIVE_DISABLED))
235 DRIVE_CurDrive = i;
236 break;
241 return 1;
245 /***********************************************************************
246 * DRIVE_IsValid
248 int DRIVE_IsValid( int drive )
250 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
251 return (DOSDrives[drive].root &&
252 !(DOSDrives[drive].flags & DRIVE_DISABLED));
256 /***********************************************************************
257 * DRIVE_GetCurrentDrive
259 int DRIVE_GetCurrentDrive(void)
261 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
262 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
263 return DRIVE_CurDrive;
267 /***********************************************************************
268 * DRIVE_SetCurrentDrive
270 int DRIVE_SetCurrentDrive( int drive )
272 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
273 if (!DRIVE_IsValid( drive ))
275 SetLastError( ERROR_INVALID_DRIVE );
276 return 0;
278 TRACE_(dosfs)("%c:\n", 'A' + drive );
279 DRIVE_CurDrive = drive;
280 if (pTask) pTask->curdrive = drive | 0x80;
281 return 1;
285 /***********************************************************************
286 * DRIVE_FindDriveRoot
288 * Find a drive for which the root matches the begginning of the given path.
289 * This can be used to translate a Unix path into a drive + DOS path.
290 * Return value is the drive, or -1 on error. On success, path is modified
291 * to point to the beginning of the DOS path.
293 int DRIVE_FindDriveRoot( const char **path )
295 /* idea: check at all '/' positions.
296 * If the device and inode of that path is identical with the
297 * device and inode of the current drive then we found a solution.
298 * If there is another drive pointing to a deeper position in
299 * the file tree, we want to find that one, not the earlier solution.
301 int drive, rootdrive = -1;
302 char buffer[MAX_PATHNAME_LEN];
303 char *next = buffer;
304 const char *p = *path;
305 struct stat st;
307 strcpy( buffer, "/" );
308 for (;;)
310 if (stat( buffer, &st ) || !S_ISDIR( st.st_mode )) break;
312 /* Find the drive */
314 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
316 if (!DOSDrives[drive].root ||
317 (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
319 if ((DOSDrives[drive].dev == st.st_dev) &&
320 (DOSDrives[drive].ino == st.st_ino))
322 rootdrive = drive;
323 *path = p;
327 /* Get the next path component */
329 *next++ = '/';
330 while ((*p == '/') || (*p == '\\')) p++;
331 if (!*p) break;
332 while (!IS_END_OF_NAME(*p)) *next++ = *p++;
333 *next = 0;
335 *next = 0;
337 if (rootdrive != -1)
338 TRACE_(dosfs)("%s -> drive %c:, root='%s', name='%s'\n",
339 buffer, 'A' + rootdrive,
340 DOSDrives[rootdrive].root, *path );
341 return rootdrive;
345 /***********************************************************************
346 * DRIVE_GetRoot
348 const char * DRIVE_GetRoot( int drive )
350 if (!DRIVE_IsValid( drive )) return NULL;
351 return DOSDrives[drive].root;
355 /***********************************************************************
356 * DRIVE_GetDosCwd
358 const char * DRIVE_GetDosCwd( int drive )
360 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
361 if (!DRIVE_IsValid( drive )) return NULL;
363 /* Check if we need to change the directory to the new task. */
364 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
365 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
366 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
368 /* Perform the task-switch */
369 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
370 DRIVE_LastTask = GetCurrentTask();
372 return DOSDrives[drive].dos_cwd;
376 /***********************************************************************
377 * DRIVE_GetUnixCwd
379 const char * DRIVE_GetUnixCwd( int drive )
381 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
382 if (!DRIVE_IsValid( drive )) return NULL;
384 /* Check if we need to change the directory to the new task. */
385 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
386 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
387 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
389 /* Perform the task-switch */
390 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
391 DRIVE_LastTask = GetCurrentTask();
393 return DOSDrives[drive].unix_cwd;
397 /***********************************************************************
398 * DRIVE_GetLabel
400 const char * DRIVE_GetLabel( int drive )
402 if (!DRIVE_IsValid( drive )) return NULL;
403 return DOSDrives[drive].label;
407 /***********************************************************************
408 * DRIVE_GetSerialNumber
410 DWORD DRIVE_GetSerialNumber( int drive )
412 if (!DRIVE_IsValid( drive )) return 0;
413 return DOSDrives[drive].serial;
417 /***********************************************************************
418 * DRIVE_SetSerialNumber
420 int DRIVE_SetSerialNumber( int drive, DWORD serial )
422 if (!DRIVE_IsValid( drive )) return 0;
423 DOSDrives[drive].serial = serial;
424 return 1;
428 /***********************************************************************
429 * DRIVE_GetType
431 DRIVETYPE DRIVE_GetType( int drive )
433 if (!DRIVE_IsValid( drive )) return TYPE_INVALID;
434 return DOSDrives[drive].type;
438 /***********************************************************************
439 * DRIVE_GetFlags
441 UINT DRIVE_GetFlags( int drive )
443 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
444 return DOSDrives[drive].flags;
448 /***********************************************************************
449 * DRIVE_Chdir
451 int DRIVE_Chdir( int drive, const char *path )
453 DOS_FULL_NAME full_name;
454 char buffer[MAX_PATHNAME_LEN];
455 LPSTR unix_cwd;
456 BY_HANDLE_FILE_INFORMATION info;
457 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
459 strcpy( buffer, "A:" );
460 buffer[0] += drive;
461 TRACE_(dosfs)("(%c:,%s)\n", buffer[0], path );
462 lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
464 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
465 if (!FILE_Stat( full_name.long_name, &info )) return 0;
466 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
468 SetLastError( ERROR_FILE_NOT_FOUND );
469 return 0;
471 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
472 while (*unix_cwd == '/') unix_cwd++;
474 TRACE_(dosfs)("(%c:): unix_cwd=%s dos_cwd=%s\n",
475 'A' + drive, unix_cwd, full_name.short_name + 3 );
477 HeapFree( SystemHeap, 0, DOSDrives[drive].dos_cwd );
478 HeapFree( SystemHeap, 0, DOSDrives[drive].unix_cwd );
479 DOSDrives[drive].dos_cwd = HEAP_strdupA( SystemHeap, 0,
480 full_name.short_name + 3 );
481 DOSDrives[drive].unix_cwd = HEAP_strdupA( SystemHeap, 0, unix_cwd );
483 if (pTask && (pTask->curdrive & 0x80) &&
484 ((pTask->curdrive & ~0x80) == drive))
486 lstrcpynA( pTask->curdir, full_name.short_name + 2,
487 sizeof(pTask->curdir) );
488 DRIVE_LastTask = GetCurrentTask();
490 return 1;
494 /***********************************************************************
495 * DRIVE_Disable
497 int DRIVE_Disable( int drive )
499 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
501 SetLastError( ERROR_INVALID_DRIVE );
502 return 0;
504 DOSDrives[drive].flags |= DRIVE_DISABLED;
505 return 1;
509 /***********************************************************************
510 * DRIVE_Enable
512 int DRIVE_Enable( int drive )
514 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
516 SetLastError( ERROR_INVALID_DRIVE );
517 return 0;
519 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
520 return 1;
524 /***********************************************************************
525 * DRIVE_SetLogicalMapping
527 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
529 /* If new_drive is already valid, do nothing and return 0
530 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
532 DOSDRIVE *old, *new;
534 old = DOSDrives + existing_drive;
535 new = DOSDrives + new_drive;
537 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
538 !old->root ||
539 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
541 SetLastError( ERROR_INVALID_DRIVE );
542 return 0;
545 if ( new->root )
547 TRACE_(dosfs)("Can\'t map drive %c to drive %c - "
548 "drive %c already exists\n",
549 'A' + existing_drive, 'A' + new_drive,
550 'A' + new_drive );
551 /* it is already mapped there, so return success */
552 if (!strcmp(old->root,new->root))
553 return 1;
554 return 0;
557 new->root = HEAP_strdupA( SystemHeap, 0, old->root );
558 new->dos_cwd = HEAP_strdupA( SystemHeap, 0, old->dos_cwd );
559 new->unix_cwd = HEAP_strdupA( SystemHeap, 0, old->unix_cwd );
560 memcpy ( new->label, old->label, 12 );
561 new->serial = old->serial;
562 new->type = old->type;
563 new->flags = old->flags;
564 new->dev = old->dev;
565 new->ino = old->ino;
567 TRACE_(dosfs)("Drive %c is now equal to drive %c\n",
568 'A' + new_drive, 'A' + existing_drive );
570 return 1;
574 /***********************************************************************
575 * DRIVE_OpenDevice
577 * Open the drive raw device and return a Unix fd (or -1 on error).
579 int DRIVE_OpenDevice( int drive, int flags )
581 if (!DRIVE_IsValid( drive )) return -1;
582 return open( DOSDrives[drive].device, flags );
586 /***********************************************************************
587 * DRIVE_RawRead
589 * Read raw sectors from a device
591 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
593 int fd;
595 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
597 lseek( fd, begin * 512, SEEK_SET );
598 /* FIXME: check errors */
599 read( fd, dataptr, nr_sect * 512 );
600 close( fd );
602 else
604 memset(dataptr, 0, nr_sect * 512);
605 if (fake_success)
607 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
608 if (begin == 1) *dataptr = 0xf8;
610 else
611 return 0;
613 return 1;
617 /***********************************************************************
618 * DRIVE_RawWrite
620 * Write raw sectors to a device
622 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
624 int fd;
626 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
628 lseek( fd, begin * 512, SEEK_SET );
629 /* FIXME: check errors */
630 write( fd, dataptr, nr_sect * 512 );
631 close( fd );
633 else
634 if (!(fake_success))
635 return 0;
637 return 1;
641 /***********************************************************************
642 * DRIVE_GetFreeSpace
644 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
645 PULARGE_INTEGER available )
647 struct statfs info;
648 unsigned long long bigsize,bigavail=0;
650 if (!DRIVE_IsValid(drive))
652 SetLastError( ERROR_INVALID_DRIVE );
653 return 0;
656 /* FIXME: add autoconf check for this */
657 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
658 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
659 #else
660 if (statfs( DOSDrives[drive].root, &info) < 0)
661 #endif
663 FILE_SetDosError();
664 WARN_(dosfs)("cannot do statfs(%s)\n", DOSDrives[drive].root);
665 return 0;
668 bigsize = (unsigned long long)info.f_bsize
669 * (unsigned long long)info.f_blocks;
670 #ifdef STATFS_HAS_BAVAIL
671 bigavail = (unsigned long long)info.f_bavail
672 * (unsigned long long)info.f_bsize;
673 #else
674 # ifdef STATFS_HAS_BFREE
675 bigavail = (unsigned long long)info.f_bfree
676 * (unsigned long long)info.f_bsize;
677 # else
678 # error "statfs has no bfree/bavail member!"
679 # endif
680 #endif
681 size->s.LowPart = (DWORD)bigsize;
682 size->s.HighPart = (DWORD)(bigsize>>32);
683 available->s.LowPart = (DWORD)bigavail;
684 available->s.HighPart = (DWORD)(bigavail>>32);
685 return 1;
689 * DRIVE_GetCurrentDirectory
690 * Returns "X:\\path\\etc\\".
692 * Despite the API description, return required length including the
693 * terminating null when buffer too small. This is the real behaviour.
696 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPSTR buf )
698 UINT ret;
699 const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
701 assert(s);
702 ret = strlen(s) + 3; /* length of WHOLE current directory */
703 if (ret >= buflen) return ret + 1;
704 lstrcpynA( buf, "A:\\", MIN( 4, buflen ) );
705 if (buflen) buf[0] += DRIVE_GetCurrentDrive();
706 if (buflen > 3) lstrcpynA( buf + 3, s, buflen - 3 );
707 return ret;
710 /***********************************************************************
711 * GetDiskFreeSpace16 (KERNEL.422)
713 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
714 LPDWORD sector_bytes, LPDWORD free_clusters,
715 LPDWORD total_clusters )
717 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
718 free_clusters, total_clusters );
722 /***********************************************************************
723 * GetDiskFreeSpace32A (KERNEL32.206)
725 * Fails if expression resulting from current drive's dir and "root"
726 * is not a root dir of the target drive.
728 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
729 * if the corresponding info is unneeded.
731 * FIXME: needs to support UNC names from Win95 OSR2 on.
733 * Behaviour under Win95a:
734 * CurrDir root result
735 * "E:\\TEST" "E:" FALSE
736 * "E:\\" "E:" TRUE
737 * "E:\\" "E" FALSE
738 * "E:\\" "\\" TRUE
739 * "E:\\TEST" "\\" TRUE
740 * "E:\\TEST" ":\\" FALSE
741 * "E:\\TEST" "E:\\" TRUE
742 * "E:\\TEST" "" FALSE
743 * "E:\\" "" FALSE (!)
744 * "E:\\" 0x0 TRUE
745 * "E:\\TEST" 0x0 TRUE (!)
746 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
747 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
749 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
750 LPDWORD sector_bytes, LPDWORD free_clusters,
751 LPDWORD total_clusters )
753 int drive;
754 ULARGE_INTEGER size,available;
755 LPCSTR path;
756 DWORD cluster_sec;
758 if ((!root) || (root == "\\"))
759 drive = DRIVE_GetCurrentDrive();
760 else
761 if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
763 drive = toupper(root[0]) - 'A';
764 path = &root[2];
765 if (path[0] == '\0')
766 path = DRIVE_GetDosCwd(drive);
767 else
768 if (path[0] == '\\')
769 path++;
770 if (strlen(path)) /* oops, we are in a subdir */
771 return FALSE;
773 else
774 return FALSE;
776 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
778 /* Cap the size and available at 2GB as per specs. */
779 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
781 size.s.HighPart = 0;
782 size.s.LowPart = 0x7fffffff;
784 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
786 available.s.HighPart =0;
787 available.s.LowPart = 0x7fffffff;
789 if (DRIVE_GetType(drive)==TYPE_CDROM) {
790 if (sector_bytes)
791 *sector_bytes = 2048;
792 size.s.LowPart /= 2048;
793 available.s.LowPart /= 2048;
794 } else {
795 if (sector_bytes)
796 *sector_bytes = 512;
797 size.s.LowPart /= 512;
798 available.s.LowPart /= 512;
800 /* fixme: probably have to adjust those variables too for CDFS */
801 cluster_sec = 1;
802 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
804 if (cluster_sectors)
805 *cluster_sectors = cluster_sec;
806 if (free_clusters)
807 *free_clusters = available.s.LowPart / cluster_sec;
808 if (total_clusters)
809 *total_clusters = size.s.LowPart / cluster_sec;
810 return TRUE;
814 /***********************************************************************
815 * GetDiskFreeSpace32W (KERNEL32.207)
817 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
818 LPDWORD sector_bytes, LPDWORD free_clusters,
819 LPDWORD total_clusters )
821 LPSTR xroot;
822 BOOL ret;
824 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
825 ret = GetDiskFreeSpaceA( xroot,cluster_sectors, sector_bytes,
826 free_clusters, total_clusters );
827 HeapFree( GetProcessHeap(), 0, xroot );
828 return ret;
832 /***********************************************************************
833 * GetDiskFreeSpaceEx32A (KERNEL32.871)
835 * This function is used to aquire the size of the available and
836 * total space on a logical volume.
838 * RETURNS
840 * Zero on failure, nonzero upon success. Use GetLastError to obtain
841 * detailed error information.
844 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
845 PULARGE_INTEGER avail,
846 PULARGE_INTEGER total,
847 PULARGE_INTEGER totalfree)
849 int drive;
850 ULARGE_INTEGER size,available;
852 if (!root) drive = DRIVE_GetCurrentDrive();
853 else
855 if ((root[1]) && ((root[1] != ':') || (root[2] != '\\')))
857 FIXME_(dosfs)("there are valid root names which are not supported yet\n");
858 /* ..like UNC names, for instance. */
860 WARN_(dosfs)("invalid root '%s'\n", root );
861 return FALSE;
863 drive = toupper(root[0]) - 'A';
866 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
868 if (total)
870 total->s.HighPart = size.s.HighPart;
871 total->s.LowPart = size.s.LowPart ;
874 if (totalfree)
876 totalfree->s.HighPart = available.s.HighPart;
877 totalfree->s.LowPart = available.s.LowPart ;
880 if (avail)
882 if (FIXME_ON(dosfs))
884 /* On Windows2000, we need to check the disk quota
885 allocated for the user owning the calling process. We
886 don't want to be more obtrusive than necessary with the
887 FIXME messages, so don't print the FIXME unless Wine is
888 actually masquerading as Windows2000. */
890 OSVERSIONINFOA ovi;
891 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
892 if (GetVersionExA(&ovi))
894 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
895 FIXME_(dosfs)("no per-user quota support yet\n");
899 /* Quick hack, should eventually be fixed to work 100% with
900 Windows2000 (see comment above). */
901 avail->s.HighPart = available.s.HighPart;
902 avail->s.LowPart = available.s.LowPart ;
905 return TRUE;
908 /***********************************************************************
909 * GetDiskFreeSpaceEx32W (KERNEL32.873)
911 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
912 PULARGE_INTEGER total,
913 PULARGE_INTEGER totalfree)
915 LPSTR xroot;
916 BOOL ret;
918 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
919 ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
920 HeapFree( GetProcessHeap(), 0, xroot );
921 return ret;
924 /***********************************************************************
925 * GetDriveType16 (KERNEL.136)
926 * This functions returns the drivetype of a drive in Win16.
927 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
928 * remote drive API. The returnvalue DRIVE_REMOTE for CD-ROMs has been
929 * verified on Win3.11 and Windows 95. Some programs rely on it, so don't
930 * do any pseudo-clever changes.
932 * RETURNS
933 * drivetype DRIVE_xxx
935 UINT16 WINAPI GetDriveType16(
936 UINT16 drive /* [in] number (NOT letter) of drive */
938 TRACE_(dosfs)("(%c:)\n", 'A' + drive );
939 switch(DRIVE_GetType(drive))
941 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
942 case TYPE_HD: return DRIVE_FIXED;
943 case TYPE_CDROM: return DRIVE_REMOTE;
944 case TYPE_NETWORK: return DRIVE_REMOTE;
945 case TYPE_INVALID:
946 default: return DRIVE_CANNOTDETERMINE;
951 /***********************************************************************
952 * GetDriveType32A (KERNEL32.208)
954 * Returns the type of the disk drive specified. If root is NULL the
955 * root of the current directory is used.
957 * RETURNS
959 * Type of drive (from Win32 SDK):
961 * DRIVE_UNKNOWN unable to find out anything about the drive
962 * DRIVE_NO_ROOT_DIR nonexistand root dir
963 * DRIVE_REMOVABLE the disk can be removed from the machine
964 * DRIVE_FIXED the disk can not be removed from the machine
965 * DRIVE_REMOTE network disk
966 * DRIVE_CDROM CDROM drive
967 * DRIVE_RAMDISK virtual disk in ram
969 * DRIVE_DOESNOTEXIST XXX Not valid return value
970 * DRIVE_CANNOTDETERMINE XXX Not valid return value
972 * BUGS
974 * Currently returns DRIVE_DOESNOTEXIST and DRIVE_CANNOTDETERMINE
975 * when it really should return DRIVE_NO_ROOT_DIR and DRIVE_UNKNOWN.
976 * Why where the former defines used?
978 * DRIVE_RAMDISK is unsupported.
980 UINT WINAPI GetDriveTypeA(LPCSTR root /* String describing drive */)
982 int drive;
983 TRACE_(dosfs)("(%s)\n", debugstr_a(root));
985 if (NULL == root) drive = DRIVE_GetCurrentDrive();
986 else
988 if ((root[1]) && (root[1] != ':'))
990 WARN_(dosfs)("invalid root '%s'\n", debugstr_a(root));
991 return DRIVE_DOESNOTEXIST;
993 drive = toupper(root[0]) - 'A';
995 switch(DRIVE_GetType(drive))
997 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
998 case TYPE_HD: return DRIVE_FIXED;
999 case TYPE_CDROM: return DRIVE_CDROM;
1000 case TYPE_NETWORK: return DRIVE_REMOTE;
1001 case TYPE_INVALID: return DRIVE_DOESNOTEXIST;
1002 default: return DRIVE_CANNOTDETERMINE;
1007 /***********************************************************************
1008 * GetDriveType32W (KERNEL32.209)
1010 UINT WINAPI GetDriveTypeW( LPCWSTR root )
1012 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1013 UINT ret = GetDriveTypeA( xpath );
1014 HeapFree( GetProcessHeap(), 0, xpath );
1015 return ret;
1019 /***********************************************************************
1020 * GetCurrentDirectory16 (KERNEL.411)
1022 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1024 return (UINT16)DRIVE_GetCurrentDirectory(buflen, buf);
1028 /***********************************************************************
1029 * GetCurrentDirectory32A (KERNEL32.196)
1031 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1033 UINT ret;
1034 char longname[MAX_PATHNAME_LEN];
1036 ret = DRIVE_GetCurrentDirectory(buflen, buf);
1037 GetLongPathNameA(buf, longname, buflen);
1038 lstrcpyA(buf, longname);
1040 return ret;
1043 /***********************************************************************
1044 * GetCurrentDirectory32W (KERNEL32.197)
1046 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1048 LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
1049 UINT ret = GetCurrentDirectoryA( buflen, xpath );
1050 lstrcpyAtoW( buf, xpath );
1051 HeapFree( GetProcessHeap(), 0, xpath );
1052 return ret;
1056 /***********************************************************************
1057 * SetCurrentDirectory (KERNEL.412)
1059 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1061 return SetCurrentDirectoryA( dir );
1065 /***********************************************************************
1066 * SetCurrentDirectory32A (KERNEL32.479)
1068 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1070 int olddrive, drive = DRIVE_GetCurrentDrive();
1072 if (!dir) {
1073 ERR_(file)("(NULL)!\n");
1074 return FALSE;
1076 if (dir[0] && (dir[1]==':'))
1078 drive = tolower( *dir ) - 'a';
1079 dir += 2;
1082 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1083 sets pTask->curdir only if pTask->curdrive is drive */
1084 olddrive = drive; /* in case DRIVE_Chdir fails */
1085 if (!(DRIVE_SetCurrentDrive( drive )))
1086 return FALSE;
1087 /* FIXME: what about empty strings? Add a \\ ? */
1088 if (!DRIVE_Chdir( drive, dir )) {
1089 DRIVE_SetCurrentDrive(olddrive);
1090 return FALSE;
1092 return TRUE;
1096 /***********************************************************************
1097 * SetCurrentDirectory32W (KERNEL32.480)
1099 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1101 LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1102 BOOL res = SetCurrentDirectoryA( dir );
1103 HeapFree( GetProcessHeap(), 0, dir );
1104 return res;
1108 /***********************************************************************
1109 * GetLogicalDriveStrings32A (KERNEL32.231)
1111 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1113 int drive, count;
1115 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1116 if (DRIVE_IsValid(drive)) count++;
1117 if ((count * 4) + 1 <= len)
1119 LPSTR p = buffer;
1120 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1121 if (DRIVE_IsValid(drive))
1123 *p++ = 'a' + drive;
1124 *p++ = ':';
1125 *p++ = '\\';
1126 *p++ = '\0';
1128 *p = '\0';
1129 return count * 4;
1131 else
1132 return (count * 4) + 1;/* account for terminating null */
1133 /* The API tells about these different return values */
1137 /***********************************************************************
1138 * GetLogicalDriveStrings32W (KERNEL32.232)
1140 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1142 int drive, count;
1144 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1145 if (DRIVE_IsValid(drive)) count++;
1146 if (count * 4 * sizeof(WCHAR) <= len)
1148 LPWSTR p = buffer;
1149 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1150 if (DRIVE_IsValid(drive))
1152 *p++ = (WCHAR)('a' + drive);
1153 *p++ = (WCHAR)':';
1154 *p++ = (WCHAR)'\\';
1155 *p++ = (WCHAR)'\0';
1157 *p = (WCHAR)'\0';
1159 return count * 4 * sizeof(WCHAR);
1163 /***********************************************************************
1164 * GetLogicalDrives (KERNEL32.233)
1166 DWORD WINAPI GetLogicalDrives(void)
1168 DWORD ret = 0;
1169 int drive;
1171 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1172 if (DRIVE_IsValid(drive)) ret |= (1 << drive);
1173 return ret;
1177 /***********************************************************************
1178 * GetVolumeInformation32A (KERNEL32.309)
1180 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1181 DWORD label_len, DWORD *serial,
1182 DWORD *filename_len, DWORD *flags,
1183 LPSTR fsname, DWORD fsname_len )
1185 int drive;
1186 char *cp;
1188 /* FIXME, SetLastErrors missing */
1190 if (!root) drive = DRIVE_GetCurrentDrive();
1191 else
1193 if ((root[1]) && (root[1] != ':'))
1195 WARN_(dosfs)("invalid root '%s'\n",root);
1196 return FALSE;
1198 drive = toupper(root[0]) - 'A';
1200 if (!DRIVE_IsValid( drive )) return FALSE;
1201 if (label)
1203 lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1204 for (cp = label; *cp; cp++);
1205 while (cp != label && *(cp-1) == ' ') cp--;
1206 *cp = '\0';
1208 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1210 /* Set the filesystem information */
1211 /* Note: we only emulate a FAT fs at the present */
1213 if (filename_len) {
1214 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1215 *filename_len = 12;
1216 else
1217 *filename_len = 255;
1219 if (flags)
1221 *flags=0;
1222 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1223 *flags|=FS_CASE_SENSITIVE;
1224 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1225 *flags|=FS_CASE_IS_PRESERVED ;
1227 if (fsname) {
1228 /* Diablo checks that return code ... */
1229 if (DRIVE_GetType(drive)==TYPE_CDROM)
1230 lstrcpynA( fsname, "CDFS", fsname_len );
1231 else
1232 lstrcpynA( fsname, "FAT", fsname_len );
1234 return TRUE;
1238 /***********************************************************************
1239 * GetVolumeInformation32W (KERNEL32.310)
1241 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1242 DWORD label_len, DWORD *serial,
1243 DWORD *filename_len, DWORD *flags,
1244 LPWSTR fsname, DWORD fsname_len )
1246 LPSTR xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1247 LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1248 LPSTR xfsname = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1249 BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1250 filename_len, flags, xfsname,
1251 fsname_len );
1252 if (ret)
1254 if (label) lstrcpyAtoW( label, xvolname );
1255 if (fsname) lstrcpyAtoW( fsname, xfsname );
1257 HeapFree( GetProcessHeap(), 0, xroot );
1258 HeapFree( GetProcessHeap(), 0, xvolname );
1259 HeapFree( GetProcessHeap(), 0, xfsname );
1260 return ret;
1263 /***********************************************************************
1264 * SetVolumeLabelA (KERNEL32.675)
1266 BOOL WINAPI SetVolumeLabelA(LPCSTR rootpath,LPCSTR volname)
1268 FIXME_(dosfs)("(%s,%s),stub!\n",rootpath,volname);
1269 return TRUE;
1272 /***********************************************************************
1273 * SetVolumeLabelW (KERNEL32.676)
1275 BOOL WINAPI SetVolumeLabelW(LPCWSTR rootpath,LPCWSTR volname)
1277 LPSTR xroot, xvol;
1278 BOOL ret;
1280 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath);
1281 xvol = HEAP_strdupWtoA( GetProcessHeap(), 0, volname);
1282 ret = SetVolumeLabelA( xroot, xvol );
1283 HeapFree( GetProcessHeap(), 0, xroot );
1284 HeapFree( GetProcessHeap(), 0, xvol );
1285 return ret;