From a2fb7c1644f7fc3b97cd8c235332ac6e1228cfc8 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Fri, 28 Oct 2005 16:45:24 +0000 Subject: [PATCH] Added an implementation of the FSCTL_DISMOUNT_VOLUME ioctl that attempts to unmount the Unix device. --- dlls/ntdll/directory.c | 119 ++++++++++++++++++++++++++++++++++++++++ dlls/ntdll/file.c | 5 ++ dlls/ntdll/ntdll_misc.h | 1 + 3 files changed, 125 insertions(+) diff --git a/dlls/ntdll/directory.c b/dlls/ntdll/directory.c index a43bd7e8b5e..ff9bca27640 100644 --- a/dlls/ntdll/directory.c +++ b/dlls/ntdll/directory.c @@ -43,6 +43,9 @@ #ifdef HAVE_LINUX_IOCTL_H #include #endif +#ifdef HAVE_LINUX_MAJOR_H +# include +#endif #ifdef HAVE_SYS_PARAM_H #include #endif @@ -497,6 +500,70 @@ static char *get_default_drive_device( const char *root ) } +/*********************************************************************** + * get_device_mount_point + * + * Return the current mount point for a device. + */ +static char *get_device_mount_point( dev_t dev ) +{ + char *ret = NULL; + +#ifdef linux + FILE *f; + + RtlEnterCriticalSection( &dir_section ); + + if ((f = fopen( "/etc/mtab", "r" ))) + { + struct mntent *entry; + struct stat st; + char *p, *device; + + while ((entry = getmntent( f ))) + { + /* don't even bother stat'ing network mounts, there's no meaningful device anyway */ + if (!strcmp( entry->mnt_type, "nfs" ) || + !strcmp( entry->mnt_type, "smbfs" ) || + !strcmp( entry->mnt_type, "ncpfs" )) continue; + + if (!strcmp( entry->mnt_type, "supermount" )) + { + if ((device = strstr( entry->mnt_opts, "dev=" ))) + { + device += 4; + if ((p = strchr( device, ',' ))) *p = 0; + } + } + else if (!stat( entry->mnt_fsname, &st ) && S_ISREG(st.st_mode)) + { + /* if device is a regular file check for a loop mount */ + if ((device = strstr( entry->mnt_opts, "loop=" ))) + { + device += 5; + if ((p = strchr( device, ',' ))) *p = 0; + } + } + else device = entry->mnt_fsname; + + if (device && !stat( device, &st ) && S_ISBLK(st.st_mode) && st.st_rdev == dev) + { + ret = RtlAllocateHeap( GetProcessHeap(), 0, strlen(entry->mnt_dir) + 1 ); + if (ret) strcpy( ret, entry->mnt_dir ); + break; + } + } + endmntent( f ); + } + RtlLeaveCriticalSection( &dir_section ); +#else + static int warned; + if (!warned++) FIXME( "unmounting devices not supported on this platform\n" ); +#endif + return ret; +} + + /*********************************************************************** * init_options * @@ -1576,6 +1643,58 @@ BOOLEAN WINAPI RtlDoesFileExists_U(LPCWSTR file_name) } +/*********************************************************************** + * DIR_unmount_device + * + * Unmount the specified device. + */ +NTSTATUS DIR_unmount_device( HANDLE handle ) +{ + NTSTATUS status; + int unix_fd; + + SERVER_START_REQ( unmount_device ) + { + req->handle = handle; + status = wine_server_call( req ); + } + SERVER_END_REQ; + if (status) return status; + + if (!(status = wine_server_handle_to_fd( handle, 0, &unix_fd, NULL ))) + { + struct stat st; + char *mount_point = NULL; + + if (fstat( unix_fd, &st ) == -1 || !S_ISBLK(st.st_mode)) + status = STATUS_INVALID_PARAMETER; + else + { + if ((mount_point = get_device_mount_point( st.st_rdev ))) + { + static const char umount[] = "umount >/dev/null 2>&1 "; + char *cmd = RtlAllocateHeap( GetProcessHeap(), 0, strlen(mount_point)+sizeof(umount)); + if (cmd) + { + strcpy( cmd, umount ); + strcat( cmd, mount_point ); + system( cmd ); + RtlFreeHeap( GetProcessHeap(), 0, cmd ); +#ifdef linux + /* umount will fail to release the loop device since we still have + a handle to it, so we release it here */ + if (major(st.st_rdev) == LOOP_MAJOR) ioctl( unix_fd, 0x4c01 /*LOOP_CLR_FD*/, 0 ); +#endif + } + RtlFreeHeap( GetProcessHeap(), 0, mount_point ); + } + } + wine_server_release_fd( handle, unix_fd ); + } + return status; +} + + /****************************************************************************** * DIR_get_unix_cwd * diff --git a/dlls/ntdll/file.c b/dlls/ntdll/file.c index 54250abd40f..cd73aeff52e 100644 --- a/dlls/ntdll/file.c +++ b/dlls/ntdll/file.c @@ -328,6 +328,7 @@ NTSTATUS FILE_GetNtStatus(void) { case EAGAIN: return STATUS_SHARING_VIOLATION; case EBADF: return STATUS_INVALID_HANDLE; + case EBUSY: return STATUS_DEVICE_BUSY; case ENOSPC: return STATUS_DISK_FULL; case EPERM: case EROFS: @@ -911,6 +912,10 @@ NTSTATUS WINAPI NtFsControlFile(HANDLE DeviceHandle, HANDLE Event OPTIONAL, PIO_ switch(FsControlCode) { + case FSCTL_DISMOUNT_VOLUME: + ret = DIR_unmount_device( DeviceHandle ); + break; + case FSCTL_PIPE_LISTEN : { HANDLE internal_event; diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h index e1534e68799..ea6a00dd31f 100644 --- a/dlls/ntdll/ntdll_misc.h +++ b/dlls/ntdll/ntdll_misc.h @@ -84,6 +84,7 @@ extern NTSTATUS CDROM_DeviceIoControl(HANDLE hDevice, extern NTSTATUS FILE_GetNtStatus(void); extern NTSTATUS FILE_GetDeviceInfo( int fd, FILE_FS_DEVICE_INFORMATION *info ); extern BOOL DIR_is_hidden_file( const UNICODE_STRING *name ); +extern NTSTATUS DIR_unmount_device( HANDLE handle ); extern NTSTATUS DIR_get_unix_cwd( char **cwd ); /* virtual memory */