Fix problem with EN_UPDATE notification sent by edit control.
[wine.git] / files / drive.c
blobac5fc5fc3c7aef9572824c950525e1159f548610
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 "wine/port.h"
45 #include "task.h"
46 #include "debugtools.h"
48 DECLARE_DEBUG_CHANNEL(dosfs)
49 DECLARE_DEBUG_CHANNEL(file)
51 typedef struct
53 char *root; /* root dir in Unix format without trailing / */
54 char *dos_cwd; /* cwd in DOS format without leading or trailing \ */
55 char *unix_cwd; /* cwd in Unix format without leading or trailing / */
56 char *device; /* raw device path */
57 char label[12]; /* drive label */
58 DWORD serial; /* drive serial number */
59 DRIVETYPE type; /* drive type */
60 UINT flags; /* drive flags */
61 dev_t dev; /* unix device number */
62 ino_t ino; /* unix inode number */
63 } DOSDRIVE;
66 static const char * const DRIVE_Types[] =
68 "floppy", /* TYPE_FLOPPY */
69 "hd", /* TYPE_HD */
70 "cdrom", /* TYPE_CDROM */
71 "network" /* TYPE_NETWORK */
75 /* Known filesystem types */
77 typedef struct
79 const char *name;
80 UINT flags;
81 } FS_DESCR;
83 static const FS_DESCR DRIVE_Filesystems[] =
85 { "unix", DRIVE_CASE_SENSITIVE | DRIVE_CASE_PRESERVING },
86 { "msdos", DRIVE_SHORT_NAMES },
87 { "dos", DRIVE_SHORT_NAMES },
88 { "fat", DRIVE_SHORT_NAMES },
89 { "vfat", DRIVE_CASE_PRESERVING },
90 { "win95", DRIVE_CASE_PRESERVING },
91 { NULL, 0 }
95 static DOSDRIVE DOSDrives[MAX_DOS_DRIVES];
96 static int DRIVE_CurDrive = -1;
98 static HTASK16 DRIVE_LastTask = 0;
101 /***********************************************************************
102 * DRIVE_GetDriveType
104 static DRIVETYPE DRIVE_GetDriveType( const char *name )
106 char buffer[20];
107 int i;
109 PROFILE_GetWineIniString( name, "Type", "hd", buffer, sizeof(buffer) );
110 for (i = 0; i < sizeof(DRIVE_Types)/sizeof(DRIVE_Types[0]); i++)
112 if (!strcasecmp( buffer, DRIVE_Types[i] )) return (DRIVETYPE)i;
114 MESSAGE("%s: unknown type '%s', defaulting to 'hd'.\n", name, buffer );
115 return TYPE_HD;
119 /***********************************************************************
120 * DRIVE_GetFSFlags
122 static UINT DRIVE_GetFSFlags( const char *name, const char *value )
124 const FS_DESCR *descr;
126 for (descr = DRIVE_Filesystems; descr->name; descr++)
127 if (!strcasecmp( value, descr->name )) return descr->flags;
128 MESSAGE("%s: unknown filesystem type '%s', defaulting to 'win95'.\n",
129 name, value );
130 return DRIVE_CASE_PRESERVING;
134 /***********************************************************************
135 * DRIVE_Init
137 int DRIVE_Init(void)
139 int i, len, count = 0;
140 char name[] = "Drive A";
141 char path[MAX_PATHNAME_LEN];
142 char buffer[80];
143 struct stat drive_stat_buffer;
144 char *p;
145 DOSDRIVE *drive;
147 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, name[6]++, drive++)
149 PROFILE_GetWineIniString( name, "Path", "", path, sizeof(path)-1 );
150 if (path[0])
152 p = path + strlen(path) - 1;
153 while ((p > path) && ((*p == '/') || (*p == '\\'))) *p-- = '\0';
154 if (!path[0]) strcpy( path, "/" );
156 if (stat( path, &drive_stat_buffer ))
158 MESSAGE("Could not stat %s, ignoring drive %c:\n", path, 'A' + i );
159 continue;
161 if (!S_ISDIR(drive_stat_buffer.st_mode))
163 MESSAGE("%s is not a directory, ignoring drive %c:\n",
164 path, 'A' + i );
165 continue;
168 drive->root = HEAP_strdupA( SystemHeap, 0, path );
169 drive->dos_cwd = HEAP_strdupA( SystemHeap, 0, "" );
170 drive->unix_cwd = HEAP_strdupA( SystemHeap, 0, "" );
171 drive->type = DRIVE_GetDriveType( name );
172 drive->device = NULL;
173 drive->flags = 0;
174 drive->dev = drive_stat_buffer.st_dev;
175 drive->ino = drive_stat_buffer.st_ino;
177 /* Get the drive label */
178 PROFILE_GetWineIniString( name, "Label", name, drive->label, 12 );
179 if ((len = strlen(drive->label)) < 11)
181 /* Pad label with spaces */
182 memset( drive->label + len, ' ', 11 - len );
183 drive->label[12] = '\0';
186 /* Get the serial number */
187 PROFILE_GetWineIniString( name, "Serial", "12345678",
188 buffer, sizeof(buffer) );
189 drive->serial = strtoul( buffer, NULL, 16 );
191 /* Get the filesystem type */
192 PROFILE_GetWineIniString( name, "Filesystem", "win95",
193 buffer, sizeof(buffer) );
194 drive->flags = DRIVE_GetFSFlags( name, buffer );
196 /* Get the device */
197 PROFILE_GetWineIniString( name, "Device", "",
198 buffer, sizeof(buffer) );
199 if (buffer[0])
200 drive->device = HEAP_strdupA( SystemHeap, 0, buffer );
202 /* Make the first hard disk the current drive */
203 if ((DRIVE_CurDrive == -1) && (drive->type == TYPE_HD))
204 DRIVE_CurDrive = i;
206 count++;
207 TRACE_(dosfs)("%s: path=%s type=%s label='%s' serial=%08lx flags=%08x dev=%x ino=%x\n",
208 name, path, DRIVE_Types[drive->type],
209 drive->label, drive->serial, drive->flags,
210 (int)drive->dev, (int)drive->ino );
212 else WARN_(dosfs)("%s: not defined\n", name );
215 if (!count)
217 MESSAGE("Warning: no valid DOS drive found, check your configuration file.\n" );
218 /* Create a C drive pointing to Unix root dir */
219 DOSDrives[2].root = HEAP_strdupA( SystemHeap, 0, "/" );
220 DOSDrives[2].dos_cwd = HEAP_strdupA( SystemHeap, 0, "" );
221 DOSDrives[2].unix_cwd = HEAP_strdupA( SystemHeap, 0, "" );
222 strcpy( DOSDrives[2].label, "Drive C " );
223 DOSDrives[2].serial = 0x12345678;
224 DOSDrives[2].type = TYPE_HD;
225 DOSDrives[2].flags = 0;
226 DRIVE_CurDrive = 2;
229 /* Make sure the current drive is valid */
230 if (DRIVE_CurDrive == -1)
232 for (i = 0, drive = DOSDrives; i < MAX_DOS_DRIVES; i++, drive++)
234 if (drive->root && !(drive->flags & DRIVE_DISABLED))
236 DRIVE_CurDrive = i;
237 break;
242 return 1;
246 /***********************************************************************
247 * DRIVE_IsValid
249 int DRIVE_IsValid( int drive )
251 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
252 return (DOSDrives[drive].root &&
253 !(DOSDrives[drive].flags & DRIVE_DISABLED));
257 /***********************************************************************
258 * DRIVE_GetCurrentDrive
260 int DRIVE_GetCurrentDrive(void)
262 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
263 if (pTask && (pTask->curdrive & 0x80)) return pTask->curdrive & ~0x80;
264 return DRIVE_CurDrive;
268 /***********************************************************************
269 * DRIVE_SetCurrentDrive
271 int DRIVE_SetCurrentDrive( int drive )
273 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
274 if (!DRIVE_IsValid( drive ))
276 SetLastError( ERROR_INVALID_DRIVE );
277 return 0;
279 TRACE_(dosfs)("%c:\n", 'A' + drive );
280 DRIVE_CurDrive = drive;
281 if (pTask) pTask->curdrive = drive | 0x80;
282 return 1;
286 /***********************************************************************
287 * DRIVE_FindDriveRoot
289 * Find a drive for which the root matches the beginning of the given path.
290 * This can be used to translate a Unix path into a drive + DOS path.
291 * Return value is the drive, or -1 on error. On success, path is modified
292 * to point to the beginning of the DOS path.
294 int DRIVE_FindDriveRoot( const char **path )
296 /* idea: check at all '/' positions.
297 * If the device and inode of that path is identical with the
298 * device and inode of the current drive then we found a solution.
299 * If there is another drive pointing to a deeper position in
300 * the file tree, we want to find that one, not the earlier solution.
302 int drive, rootdrive = -1;
303 char buffer[MAX_PATHNAME_LEN];
304 char *next = buffer;
305 const char *p = *path;
306 struct stat st;
308 strcpy( buffer, "/" );
309 for (;;)
311 if (stat( buffer, &st ) || !S_ISDIR( st.st_mode )) break;
313 /* Find the drive */
315 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
317 if (!DOSDrives[drive].root ||
318 (DOSDrives[drive].flags & DRIVE_DISABLED)) continue;
320 if ((DOSDrives[drive].dev == st.st_dev) &&
321 (DOSDrives[drive].ino == st.st_ino))
323 rootdrive = drive;
324 *path = p;
328 /* Get the next path component */
330 *next++ = '/';
331 while ((*p == '/') || (*p == '\\')) p++;
332 if (!*p) break;
333 while (!IS_END_OF_NAME(*p)) *next++ = *p++;
334 *next = 0;
336 *next = 0;
338 if (rootdrive != -1)
339 TRACE_(dosfs)("%s -> drive %c:, root='%s', name='%s'\n",
340 buffer, 'A' + rootdrive,
341 DOSDrives[rootdrive].root, *path );
342 return rootdrive;
346 /***********************************************************************
347 * DRIVE_GetRoot
349 const char * DRIVE_GetRoot( int drive )
351 if (!DRIVE_IsValid( drive )) return NULL;
352 return DOSDrives[drive].root;
356 /***********************************************************************
357 * DRIVE_GetDosCwd
359 const char * DRIVE_GetDosCwd( int drive )
361 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
362 if (!DRIVE_IsValid( drive )) return NULL;
364 /* Check if we need to change the directory to the new task. */
365 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
366 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
367 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
369 /* Perform the task-switch */
370 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
371 DRIVE_LastTask = GetCurrentTask();
373 return DOSDrives[drive].dos_cwd;
377 /***********************************************************************
378 * DRIVE_GetUnixCwd
380 const char * DRIVE_GetUnixCwd( int drive )
382 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
383 if (!DRIVE_IsValid( drive )) return NULL;
385 /* Check if we need to change the directory to the new task. */
386 if (pTask && (pTask->curdrive & 0x80) && /* The task drive is valid */
387 ((pTask->curdrive & ~0x80) == drive) && /* and it's the one we want */
388 (DRIVE_LastTask != GetCurrentTask())) /* and the task changed */
390 /* Perform the task-switch */
391 if (!DRIVE_Chdir( drive, pTask->curdir )) DRIVE_Chdir( drive, "\\" );
392 DRIVE_LastTask = GetCurrentTask();
394 return DOSDrives[drive].unix_cwd;
398 /***********************************************************************
399 * DRIVE_GetLabel
401 const char * DRIVE_GetLabel( int drive )
403 if (!DRIVE_IsValid( drive )) return NULL;
404 return DOSDrives[drive].label;
408 /***********************************************************************
409 * DRIVE_GetSerialNumber
411 DWORD DRIVE_GetSerialNumber( int drive )
413 if (!DRIVE_IsValid( drive )) return 0;
414 return DOSDrives[drive].serial;
418 /***********************************************************************
419 * DRIVE_SetSerialNumber
421 int DRIVE_SetSerialNumber( int drive, DWORD serial )
423 if (!DRIVE_IsValid( drive )) return 0;
424 DOSDrives[drive].serial = serial;
425 return 1;
429 /***********************************************************************
430 * DRIVE_GetType
432 DRIVETYPE DRIVE_GetType( int drive )
434 if (!DRIVE_IsValid( drive )) return TYPE_INVALID;
435 return DOSDrives[drive].type;
439 /***********************************************************************
440 * DRIVE_GetFlags
442 UINT DRIVE_GetFlags( int drive )
444 if ((drive < 0) || (drive >= MAX_DOS_DRIVES)) return 0;
445 return DOSDrives[drive].flags;
449 /***********************************************************************
450 * DRIVE_Chdir
452 int DRIVE_Chdir( int drive, const char *path )
454 DOS_FULL_NAME full_name;
455 char buffer[MAX_PATHNAME_LEN];
456 LPSTR unix_cwd;
457 BY_HANDLE_FILE_INFORMATION info;
458 TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
460 strcpy( buffer, "A:" );
461 buffer[0] += drive;
462 TRACE_(dosfs)("(%c:,%s)\n", buffer[0], path );
463 lstrcpynA( buffer + 2, path, sizeof(buffer) - 2 );
465 if (!DOSFS_GetFullName( buffer, TRUE, &full_name )) return 0;
466 if (!FILE_Stat( full_name.long_name, &info )) return 0;
467 if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
469 SetLastError( ERROR_FILE_NOT_FOUND );
470 return 0;
472 unix_cwd = full_name.long_name + strlen( DOSDrives[drive].root );
473 while (*unix_cwd == '/') unix_cwd++;
475 TRACE_(dosfs)("(%c:): unix_cwd=%s dos_cwd=%s\n",
476 'A' + drive, unix_cwd, full_name.short_name + 3 );
478 HeapFree( SystemHeap, 0, DOSDrives[drive].dos_cwd );
479 HeapFree( SystemHeap, 0, DOSDrives[drive].unix_cwd );
480 DOSDrives[drive].dos_cwd = HEAP_strdupA( SystemHeap, 0,
481 full_name.short_name + 3 );
482 DOSDrives[drive].unix_cwd = HEAP_strdupA( SystemHeap, 0, unix_cwd );
484 if (pTask && (pTask->curdrive & 0x80) &&
485 ((pTask->curdrive & ~0x80) == drive))
487 lstrcpynA( pTask->curdir, full_name.short_name + 2,
488 sizeof(pTask->curdir) );
489 DRIVE_LastTask = GetCurrentTask();
491 return 1;
495 /***********************************************************************
496 * DRIVE_Disable
498 int DRIVE_Disable( int drive )
500 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
502 SetLastError( ERROR_INVALID_DRIVE );
503 return 0;
505 DOSDrives[drive].flags |= DRIVE_DISABLED;
506 return 1;
510 /***********************************************************************
511 * DRIVE_Enable
513 int DRIVE_Enable( int drive )
515 if ((drive < 0) || (drive >= MAX_DOS_DRIVES) || !DOSDrives[drive].root)
517 SetLastError( ERROR_INVALID_DRIVE );
518 return 0;
520 DOSDrives[drive].flags &= ~DRIVE_DISABLED;
521 return 1;
525 /***********************************************************************
526 * DRIVE_SetLogicalMapping
528 int DRIVE_SetLogicalMapping ( int existing_drive, int new_drive )
530 /* If new_drive is already valid, do nothing and return 0
531 otherwise, copy DOSDrives[existing_drive] to DOSDrives[new_drive] */
533 DOSDRIVE *old, *new;
535 old = DOSDrives + existing_drive;
536 new = DOSDrives + new_drive;
538 if ((existing_drive < 0) || (existing_drive >= MAX_DOS_DRIVES) ||
539 !old->root ||
540 (new_drive < 0) || (new_drive >= MAX_DOS_DRIVES))
542 SetLastError( ERROR_INVALID_DRIVE );
543 return 0;
546 if ( new->root )
548 TRACE_(dosfs)("Can\'t map drive %c to drive %c - "
549 "drive %c already exists\n",
550 'A' + existing_drive, 'A' + new_drive,
551 'A' + new_drive );
552 /* it is already mapped there, so return success */
553 if (!strcmp(old->root,new->root))
554 return 1;
555 return 0;
558 new->root = HEAP_strdupA( SystemHeap, 0, old->root );
559 new->dos_cwd = HEAP_strdupA( SystemHeap, 0, old->dos_cwd );
560 new->unix_cwd = HEAP_strdupA( SystemHeap, 0, old->unix_cwd );
561 memcpy ( new->label, old->label, 12 );
562 new->serial = old->serial;
563 new->type = old->type;
564 new->flags = old->flags;
565 new->dev = old->dev;
566 new->ino = old->ino;
568 TRACE_(dosfs)("Drive %c is now equal to drive %c\n",
569 'A' + new_drive, 'A' + existing_drive );
571 return 1;
575 /***********************************************************************
576 * DRIVE_OpenDevice
578 * Open the drive raw device and return a Unix fd (or -1 on error).
580 int DRIVE_OpenDevice( int drive, int flags )
582 if (!DRIVE_IsValid( drive )) return -1;
583 return open( DOSDrives[drive].device, flags );
587 /***********************************************************************
588 * DRIVE_RawRead
590 * Read raw sectors from a device
592 int DRIVE_RawRead(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
594 int fd;
596 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
598 lseek( fd, begin * 512, SEEK_SET );
599 /* FIXME: check errors */
600 read( fd, dataptr, nr_sect * 512 );
601 close( fd );
603 else
605 memset(dataptr, 0, nr_sect * 512);
606 if (fake_success)
608 if (begin == 0 && nr_sect > 1) *(dataptr + 512) = 0xf8;
609 if (begin == 1) *dataptr = 0xf8;
611 else
612 return 0;
614 return 1;
618 /***********************************************************************
619 * DRIVE_RawWrite
621 * Write raw sectors to a device
623 int DRIVE_RawWrite(BYTE drive, DWORD begin, DWORD nr_sect, BYTE *dataptr, BOOL fake_success)
625 int fd;
627 if ((fd = DRIVE_OpenDevice( drive, O_RDONLY )) != -1)
629 lseek( fd, begin * 512, SEEK_SET );
630 /* FIXME: check errors */
631 write( fd, dataptr, nr_sect * 512 );
632 close( fd );
634 else
635 if (!(fake_success))
636 return 0;
638 return 1;
642 /***********************************************************************
643 * DRIVE_GetFreeSpace
645 static int DRIVE_GetFreeSpace( int drive, PULARGE_INTEGER size,
646 PULARGE_INTEGER available )
648 struct statfs info;
649 unsigned long long bigsize,bigavail=0;
651 if (!DRIVE_IsValid(drive))
653 SetLastError( ERROR_INVALID_DRIVE );
654 return 0;
657 /* FIXME: add autoconf check for this */
658 #if defined(__svr4__) || defined(_SCO_DS) || defined(__sun)
659 if (statfs( DOSDrives[drive].root, &info, 0, 0) < 0)
660 #else
661 if (statfs( DOSDrives[drive].root, &info) < 0)
662 #endif
664 FILE_SetDosError();
665 WARN_(dosfs)("cannot do statfs(%s)\n", DOSDrives[drive].root);
666 return 0;
669 bigsize = (unsigned long long)info.f_bsize
670 * (unsigned long long)info.f_blocks;
671 #ifdef STATFS_HAS_BAVAIL
672 bigavail = (unsigned long long)info.f_bavail
673 * (unsigned long long)info.f_bsize;
674 #else
675 # ifdef STATFS_HAS_BFREE
676 bigavail = (unsigned long long)info.f_bfree
677 * (unsigned long long)info.f_bsize;
678 # else
679 # error "statfs has no bfree/bavail member!"
680 # endif
681 #endif
682 size->s.LowPart = (DWORD)bigsize;
683 size->s.HighPart = (DWORD)(bigsize>>32);
684 available->s.LowPart = (DWORD)bigavail;
685 available->s.HighPart = (DWORD)(bigavail>>32);
686 return 1;
689 /***********************************************************************
690 * DRIVE_GetCurrentDirectory
691 * Returns "X:\\path\\etc\\".
693 * Despite the API description, return required length including the
694 * terminating null when buffer too small. This is the real behaviour.
697 static UINT DRIVE_GetCurrentDirectory( UINT buflen, LPSTR buf )
699 UINT ret;
700 const char *s = DRIVE_GetDosCwd( DRIVE_GetCurrentDrive() );
702 assert(s);
703 ret = strlen(s) + 3; /* length of WHOLE current directory */
704 if (ret >= buflen) return ret + 1;
705 lstrcpynA( buf, "A:\\", MIN( 4, buflen ) );
706 if (buflen) buf[0] += DRIVE_GetCurrentDrive();
707 if (buflen > 3) lstrcpynA( buf + 3, s, buflen - 3 );
708 return ret;
711 /***********************************************************************
712 * GetDiskFreeSpace16 (KERNEL.422)
714 BOOL16 WINAPI GetDiskFreeSpace16( LPCSTR root, LPDWORD cluster_sectors,
715 LPDWORD sector_bytes, LPDWORD free_clusters,
716 LPDWORD total_clusters )
718 return GetDiskFreeSpaceA( root, cluster_sectors, sector_bytes,
719 free_clusters, total_clusters );
723 /***********************************************************************
724 * GetDiskFreeSpace32A (KERNEL32.206)
726 * Fails if expression resulting from current drive's dir and "root"
727 * is not a root dir of the target drive.
729 * UNDOC: setting some LPDWORDs to NULL is perfectly possible
730 * if the corresponding info is unneeded.
732 * FIXME: needs to support UNC names from Win95 OSR2 on.
734 * Behaviour under Win95a:
735 * CurrDir root result
736 * "E:\\TEST" "E:" FALSE
737 * "E:\\" "E:" TRUE
738 * "E:\\" "E" FALSE
739 * "E:\\" "\\" TRUE
740 * "E:\\TEST" "\\" TRUE
741 * "E:\\TEST" ":\\" FALSE
742 * "E:\\TEST" "E:\\" TRUE
743 * "E:\\TEST" "" FALSE
744 * "E:\\" "" FALSE (!)
745 * "E:\\" 0x0 TRUE
746 * "E:\\TEST" 0x0 TRUE (!)
747 * "E:\\TEST" "C:" TRUE (when CurrDir of "C:" set to "\\")
748 * "E:\\TEST" "C:" FALSE (when CurrDir of "C:" set to "\\TEST")
750 BOOL WINAPI GetDiskFreeSpaceA( LPCSTR root, LPDWORD cluster_sectors,
751 LPDWORD sector_bytes, LPDWORD free_clusters,
752 LPDWORD total_clusters )
754 int drive;
755 ULARGE_INTEGER size,available;
756 LPCSTR path;
757 DWORD cluster_sec;
759 if ((!root) || (strcmp(root,"\\") == 0))
760 drive = DRIVE_GetCurrentDrive();
761 else
762 if ( (strlen(root) >= 2) && (root[1] == ':')) /* root contains drive tag */
764 drive = toupper(root[0]) - 'A';
765 path = &root[2];
766 if (path[0] == '\0')
767 path = DRIVE_GetDosCwd(drive);
768 else
769 if (path[0] == '\\')
770 path++;
771 if (strlen(path)) /* oops, we are in a subdir */
772 return FALSE;
774 else
775 return FALSE;
777 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
779 /* Cap the size and available at 2GB as per specs. */
780 if ((size.s.HighPart) ||(size.s.LowPart > 0x7fffffff))
782 size.s.HighPart = 0;
783 size.s.LowPart = 0x7fffffff;
785 if ((available.s.HighPart) ||(available.s.LowPart > 0x7fffffff))
787 available.s.HighPart =0;
788 available.s.LowPart = 0x7fffffff;
790 if (DRIVE_GetType(drive)==TYPE_CDROM) {
791 if (sector_bytes)
792 *sector_bytes = 2048;
793 size.s.LowPart /= 2048;
794 available.s.LowPart /= 2048;
795 } else {
796 if (sector_bytes)
797 *sector_bytes = 512;
798 size.s.LowPart /= 512;
799 available.s.LowPart /= 512;
801 /* fixme: probably have to adjust those variables too for CDFS */
802 cluster_sec = 1;
803 while (cluster_sec * 65536 < size.s.LowPart) cluster_sec *= 2;
805 if (cluster_sectors)
806 *cluster_sectors = cluster_sec;
807 if (free_clusters)
808 *free_clusters = available.s.LowPart / cluster_sec;
809 if (total_clusters)
810 *total_clusters = size.s.LowPart / cluster_sec;
811 return TRUE;
815 /***********************************************************************
816 * GetDiskFreeSpace32W (KERNEL32.207)
818 BOOL WINAPI GetDiskFreeSpaceW( LPCWSTR root, LPDWORD cluster_sectors,
819 LPDWORD sector_bytes, LPDWORD free_clusters,
820 LPDWORD total_clusters )
822 LPSTR xroot;
823 BOOL ret;
825 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
826 ret = GetDiskFreeSpaceA( xroot,cluster_sectors, sector_bytes,
827 free_clusters, total_clusters );
828 HeapFree( GetProcessHeap(), 0, xroot );
829 return ret;
833 /***********************************************************************
834 * GetDiskFreeSpaceEx32A (KERNEL32.871)
836 * This function is used to aquire the size of the available and
837 * total space on a logical volume.
839 * RETURNS
841 * Zero on failure, nonzero upon success. Use GetLastError to obtain
842 * detailed error information.
845 BOOL WINAPI GetDiskFreeSpaceExA( LPCSTR root,
846 PULARGE_INTEGER avail,
847 PULARGE_INTEGER total,
848 PULARGE_INTEGER totalfree)
850 int drive;
851 ULARGE_INTEGER size,available;
853 if (!root) drive = DRIVE_GetCurrentDrive();
854 else
856 if ((root[1]) && ((root[1] != ':') || (root[2] != '\\')))
858 FIXME_(dosfs)("there are valid root names which are not supported yet\n");
859 /* ..like UNC names, for instance. */
861 WARN_(dosfs)("invalid root '%s'\n", root );
862 return FALSE;
864 drive = toupper(root[0]) - 'A';
867 if (!DRIVE_GetFreeSpace(drive, &size, &available)) return FALSE;
869 if (total)
871 total->s.HighPart = size.s.HighPart;
872 total->s.LowPart = size.s.LowPart ;
875 if (totalfree)
877 totalfree->s.HighPart = available.s.HighPart;
878 totalfree->s.LowPart = available.s.LowPart ;
881 if (avail)
883 if (FIXME_ON(dosfs))
885 /* On Windows2000, we need to check the disk quota
886 allocated for the user owning the calling process. We
887 don't want to be more obtrusive than necessary with the
888 FIXME messages, so don't print the FIXME unless Wine is
889 actually masquerading as Windows2000. */
891 OSVERSIONINFOA ovi;
892 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
893 if (GetVersionExA(&ovi))
895 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4)
896 FIXME_(dosfs)("no per-user quota support yet\n");
900 /* Quick hack, should eventually be fixed to work 100% with
901 Windows2000 (see comment above). */
902 avail->s.HighPart = available.s.HighPart;
903 avail->s.LowPart = available.s.LowPart ;
906 return TRUE;
909 /***********************************************************************
910 * GetDiskFreeSpaceEx32W (KERNEL32.873)
912 BOOL WINAPI GetDiskFreeSpaceExW( LPCWSTR root, PULARGE_INTEGER avail,
913 PULARGE_INTEGER total,
914 PULARGE_INTEGER totalfree)
916 LPSTR xroot;
917 BOOL ret;
919 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root);
920 ret = GetDiskFreeSpaceExA( xroot, avail, total, totalfree);
921 HeapFree( GetProcessHeap(), 0, xroot );
922 return ret;
925 /***********************************************************************
926 * GetDriveType16 (KERNEL.136)
927 * This functions returns the drivetype of a drive in Win16.
928 * Note that it returns DRIVE_REMOTE for CD-ROMs, since MSCDEX uses the
929 * remote drive API. The returnvalue DRIVE_REMOTE for CD-ROMs has been
930 * verified on Win3.11 and Windows 95. Some programs rely on it, so don't
931 * do any pseudo-clever changes.
933 * RETURNS
934 * drivetype DRIVE_xxx
936 UINT16 WINAPI GetDriveType16(
937 UINT16 drive /* [in] number (NOT letter) of drive */
939 TRACE_(dosfs)("(%c:)\n", 'A' + drive );
940 switch(DRIVE_GetType(drive))
942 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
943 case TYPE_HD: return DRIVE_FIXED;
944 case TYPE_CDROM: return DRIVE_REMOTE;
945 case TYPE_NETWORK: return DRIVE_REMOTE;
946 case TYPE_INVALID:
947 default: return DRIVE_CANNOTDETERMINE;
952 /***********************************************************************
953 * GetDriveType32A (KERNEL32.208)
955 * Returns the type of the disk drive specified. If root is NULL the
956 * root of the current directory is used.
958 * RETURNS
960 * Type of drive (from Win32 SDK):
962 * DRIVE_UNKNOWN unable to find out anything about the drive
963 * DRIVE_NO_ROOT_DIR nonexistand root dir
964 * DRIVE_REMOVABLE the disk can be removed from the machine
965 * DRIVE_FIXED the disk can not be removed from the machine
966 * DRIVE_REMOTE network disk
967 * DRIVE_CDROM CDROM drive
968 * DRIVE_RAMDISK virtual disk in ram
970 * DRIVE_DOESNOTEXIST XXX Not valid return value
971 * DRIVE_CANNOTDETERMINE XXX Not valid return value
973 * BUGS
975 * Currently returns DRIVE_DOESNOTEXIST and DRIVE_CANNOTDETERMINE
976 * when it really should return DRIVE_NO_ROOT_DIR and DRIVE_UNKNOWN.
977 * Why where the former defines used?
979 * DRIVE_RAMDISK is unsupported.
981 UINT WINAPI GetDriveTypeA(LPCSTR root /* String describing drive */)
983 int drive;
984 TRACE_(dosfs)("(%s)\n", debugstr_a(root));
986 if (NULL == root) drive = DRIVE_GetCurrentDrive();
987 else
989 if ((root[1]) && (root[1] != ':'))
991 WARN_(dosfs)("invalid root '%s'\n", debugstr_a(root));
992 return DRIVE_DOESNOTEXIST;
994 drive = toupper(root[0]) - 'A';
996 switch(DRIVE_GetType(drive))
998 case TYPE_FLOPPY: return DRIVE_REMOVABLE;
999 case TYPE_HD: return DRIVE_FIXED;
1000 case TYPE_CDROM: return DRIVE_CDROM;
1001 case TYPE_NETWORK: return DRIVE_REMOTE;
1002 case TYPE_INVALID: return DRIVE_DOESNOTEXIST;
1003 default: return DRIVE_CANNOTDETERMINE;
1008 /***********************************************************************
1009 * GetDriveType32W (KERNEL32.209)
1011 UINT WINAPI GetDriveTypeW( LPCWSTR root )
1013 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1014 UINT ret = GetDriveTypeA( xpath );
1015 HeapFree( GetProcessHeap(), 0, xpath );
1016 return ret;
1020 /***********************************************************************
1021 * GetCurrentDirectory16 (KERNEL.411)
1023 UINT16 WINAPI GetCurrentDirectory16( UINT16 buflen, LPSTR buf )
1025 return (UINT16)DRIVE_GetCurrentDirectory(buflen, buf);
1029 /***********************************************************************
1030 * GetCurrentDirectory32A (KERNEL32.196)
1032 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1034 UINT ret;
1035 char longname[MAX_PATHNAME_LEN];
1036 char shortname[MAX_PATHNAME_LEN];
1037 ret = DRIVE_GetCurrentDirectory(MAX_PATHNAME_LEN, shortname);
1038 if ( ret > MAX_PATHNAME_LEN ) {
1039 ERR_(file)("pathnamelength (%d) > MAX_PATHNAME_LEN!\n", ret );
1040 return ret;
1042 GetLongPathNameA(shortname, longname, MAX_PATHNAME_LEN);
1043 ret = lstrlenA( longname ) + 1;
1044 if (ret > buflen) return ret;
1045 lstrcpyA(buf, longname);
1046 return ret - 1;
1049 /***********************************************************************
1050 * GetCurrentDirectory32W (KERNEL32.197)
1052 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1054 LPSTR xpath = HeapAlloc( GetProcessHeap(), 0, buflen+1 );
1055 UINT ret = GetCurrentDirectoryA( buflen, xpath );
1056 if (ret < buflen) lstrcpyAtoW ( buf, xpath );
1057 HeapFree( GetProcessHeap(), 0, xpath );
1058 return ret;
1062 /***********************************************************************
1063 * SetCurrentDirectory (KERNEL.412)
1065 BOOL16 WINAPI SetCurrentDirectory16( LPCSTR dir )
1067 return SetCurrentDirectoryA( dir );
1071 /***********************************************************************
1072 * SetCurrentDirectory32A (KERNEL32.479)
1074 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1076 int olddrive, drive = DRIVE_GetCurrentDrive();
1078 if (!dir) {
1079 ERR_(file)("(NULL)!\n");
1080 return FALSE;
1082 if (dir[0] && (dir[1]==':'))
1084 drive = tolower( *dir ) - 'a';
1085 dir += 2;
1088 /* WARNING: we need to set the drive before the dir, as DRIVE_Chdir
1089 sets pTask->curdir only if pTask->curdrive is drive */
1090 olddrive = drive; /* in case DRIVE_Chdir fails */
1091 if (!(DRIVE_SetCurrentDrive( drive )))
1092 return FALSE;
1093 /* FIXME: what about empty strings? Add a \\ ? */
1094 if (!DRIVE_Chdir( drive, dir )) {
1095 DRIVE_SetCurrentDrive(olddrive);
1096 return FALSE;
1098 return TRUE;
1102 /***********************************************************************
1103 * SetCurrentDirectory32W (KERNEL32.480)
1105 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dirW )
1107 LPSTR dir = HEAP_strdupWtoA( GetProcessHeap(), 0, dirW );
1108 BOOL res = SetCurrentDirectoryA( dir );
1109 HeapFree( GetProcessHeap(), 0, dir );
1110 return res;
1114 /***********************************************************************
1115 * GetLogicalDriveStrings32A (KERNEL32.231)
1117 UINT WINAPI GetLogicalDriveStringsA( UINT len, LPSTR buffer )
1119 int drive, count;
1121 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1122 if (DRIVE_IsValid(drive)) count++;
1123 if ((count * 4) + 1 <= len)
1125 LPSTR p = buffer;
1126 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1127 if (DRIVE_IsValid(drive))
1129 *p++ = 'a' + drive;
1130 *p++ = ':';
1131 *p++ = '\\';
1132 *p++ = '\0';
1134 *p = '\0';
1135 return count * 4;
1137 else
1138 return (count * 4) + 1;/* account for terminating null */
1139 /* The API tells about these different return values */
1143 /***********************************************************************
1144 * GetLogicalDriveStrings32W (KERNEL32.232)
1146 UINT WINAPI GetLogicalDriveStringsW( UINT len, LPWSTR buffer )
1148 int drive, count;
1150 for (drive = count = 0; drive < MAX_DOS_DRIVES; drive++)
1151 if (DRIVE_IsValid(drive)) count++;
1152 if (count * 4 * sizeof(WCHAR) <= len)
1154 LPWSTR p = buffer;
1155 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1156 if (DRIVE_IsValid(drive))
1158 *p++ = (WCHAR)('a' + drive);
1159 *p++ = (WCHAR)':';
1160 *p++ = (WCHAR)'\\';
1161 *p++ = (WCHAR)'\0';
1163 *p = (WCHAR)'\0';
1165 return count * 4 * sizeof(WCHAR);
1169 /***********************************************************************
1170 * GetLogicalDrives (KERNEL32.233)
1172 DWORD WINAPI GetLogicalDrives(void)
1174 DWORD ret = 0;
1175 int drive;
1177 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
1178 if (DRIVE_IsValid(drive)) ret |= (1 << drive);
1179 return ret;
1183 /***********************************************************************
1184 * GetVolumeInformation32A (KERNEL32.309)
1186 BOOL WINAPI GetVolumeInformationA( LPCSTR root, LPSTR label,
1187 DWORD label_len, DWORD *serial,
1188 DWORD *filename_len, DWORD *flags,
1189 LPSTR fsname, DWORD fsname_len )
1191 int drive;
1192 char *cp;
1194 /* FIXME, SetLastErrors missing */
1196 if (!root) drive = DRIVE_GetCurrentDrive();
1197 else
1199 if ((root[1]) && (root[1] != ':'))
1201 WARN_(dosfs)("invalid root '%s'\n",root);
1202 return FALSE;
1204 drive = toupper(root[0]) - 'A';
1206 if (!DRIVE_IsValid( drive )) return FALSE;
1207 if (label)
1209 lstrcpynA( label, DRIVE_GetLabel(drive), label_len );
1210 for (cp = label; *cp; cp++);
1211 while (cp != label && *(cp-1) == ' ') cp--;
1212 *cp = '\0';
1214 if (serial) *serial = DRIVE_GetSerialNumber(drive);
1216 /* Set the filesystem information */
1217 /* Note: we only emulate a FAT fs at the present */
1219 if (filename_len) {
1220 if (DOSDrives[drive].flags & DRIVE_SHORT_NAMES)
1221 *filename_len = 12;
1222 else
1223 *filename_len = 255;
1225 if (flags)
1227 *flags=0;
1228 if (DOSDrives[drive].flags & DRIVE_CASE_SENSITIVE)
1229 *flags|=FS_CASE_SENSITIVE;
1230 if (DOSDrives[drive].flags & DRIVE_CASE_PRESERVING)
1231 *flags|=FS_CASE_IS_PRESERVED ;
1233 if (fsname) {
1234 /* Diablo checks that return code ... */
1235 if (DRIVE_GetType(drive)==TYPE_CDROM)
1236 lstrcpynA( fsname, "CDFS", fsname_len );
1237 else
1238 lstrcpynA( fsname, "FAT", fsname_len );
1240 return TRUE;
1244 /***********************************************************************
1245 * GetVolumeInformation32W (KERNEL32.310)
1247 BOOL WINAPI GetVolumeInformationW( LPCWSTR root, LPWSTR label,
1248 DWORD label_len, DWORD *serial,
1249 DWORD *filename_len, DWORD *flags,
1250 LPWSTR fsname, DWORD fsname_len )
1252 LPSTR xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, root );
1253 LPSTR xvolname = label ? HeapAlloc(GetProcessHeap(),0,label_len) : NULL;
1254 LPSTR xfsname = fsname ? HeapAlloc(GetProcessHeap(),0,fsname_len) : NULL;
1255 BOOL ret = GetVolumeInformationA( xroot, xvolname, label_len, serial,
1256 filename_len, flags, xfsname,
1257 fsname_len );
1258 if (ret)
1260 if (label) lstrcpyAtoW( label, xvolname );
1261 if (fsname) lstrcpyAtoW( fsname, xfsname );
1263 HeapFree( GetProcessHeap(), 0, xroot );
1264 HeapFree( GetProcessHeap(), 0, xvolname );
1265 HeapFree( GetProcessHeap(), 0, xfsname );
1266 return ret;
1269 /***********************************************************************
1270 * SetVolumeLabelA (KERNEL32.675)
1272 BOOL WINAPI SetVolumeLabelA(LPCSTR rootpath,LPCSTR volname)
1274 FIXME_(dosfs)("(%s,%s),stub!\n",rootpath,volname);
1275 return TRUE;
1278 /***********************************************************************
1279 * SetVolumeLabelW (KERNEL32.676)
1281 BOOL WINAPI SetVolumeLabelW(LPCWSTR rootpath,LPCWSTR volname)
1283 LPSTR xroot, xvol;
1284 BOOL ret;
1286 xroot = HEAP_strdupWtoA( GetProcessHeap(), 0, rootpath);
1287 xvol = HEAP_strdupWtoA( GetProcessHeap(), 0, volname);
1288 ret = SetVolumeLabelA( xroot, xvol );
1289 HeapFree( GetProcessHeap(), 0, xroot );
1290 HeapFree( GetProcessHeap(), 0, xvol );
1291 return ret;