2 * MountMgr Unix interface
4 * Copyright 2021 Alexandre Julliard
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
33 #ifdef HAVE_SYS_STATFS_H
34 #include <sys/statfs.h>
36 #ifdef HAVE_SYS_STATVFS_H
37 # include <sys/statvfs.h>
42 #include "wine/debug.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(mountmgr
);
46 static struct run_loop_params run_loop_params
;
48 static NTSTATUS
errno_to_status( int err
)
50 TRACE( "errno = %d\n", err
);
53 case EAGAIN
: return STATUS_SHARING_VIOLATION
;
54 case EBADF
: return STATUS_INVALID_HANDLE
;
55 case EBUSY
: return STATUS_DEVICE_BUSY
;
56 case ENOSPC
: return STATUS_DISK_FULL
;
59 case EACCES
: return STATUS_ACCESS_DENIED
;
60 case ENOTDIR
: return STATUS_OBJECT_PATH_NOT_FOUND
;
61 case ENOENT
: return STATUS_OBJECT_NAME_NOT_FOUND
;
62 case EISDIR
: return STATUS_INVALID_DEVICE_REQUEST
;
64 case ENFILE
: return STATUS_TOO_MANY_OPENED_FILES
;
65 case EINVAL
: return STATUS_INVALID_PARAMETER
;
66 case ENOTEMPTY
: return STATUS_DIRECTORY_NOT_EMPTY
;
67 case EPIPE
: return STATUS_PIPE_DISCONNECTED
;
68 case EIO
: return STATUS_DEVICE_NOT_READY
;
70 case ENOMEDIUM
: return STATUS_NO_MEDIA_IN_DEVICE
;
72 case ENXIO
: return STATUS_NO_SUCH_DEVICE
;
74 case EOPNOTSUPP
:return STATUS_NOT_SUPPORTED
;
75 case ECONNRESET
:return STATUS_PIPE_DISCONNECTED
;
76 case EFAULT
: return STATUS_ACCESS_VIOLATION
;
77 case ESPIPE
: return STATUS_ILLEGAL_FUNCTION
;
78 case ELOOP
: return STATUS_REPARSE_POINT_NOT_RESOLVED
;
79 #ifdef ETIME /* Missing on FreeBSD */
80 case ETIME
: return STATUS_IO_TIMEOUT
;
82 case ENOEXEC
: /* ?? */
85 FIXME( "Converting errno %d to STATUS_UNSUCCESSFUL\n", err
);
86 return STATUS_UNSUCCESSFUL
;
90 static char *get_dosdevices_path( const char *dev
)
92 const char *home
= getenv( "HOME" );
93 const char *prefix
= getenv( "WINEPREFIX" );
94 size_t len
= (prefix
? strlen(prefix
) : strlen(home
) + strlen("/.wine")) + sizeof("/dosdevices/") + strlen(dev
);
95 char *path
= malloc( len
);
99 if (prefix
) strcpy( path
, prefix
);
102 strcpy( path
, home
);
103 strcat( path
, "/.wine" );
105 strcat( path
, "/dosdevices/" );
111 static BOOL
is_valid_device( struct stat
*st
)
113 #if defined(linux) || defined(__sun__)
114 return S_ISBLK( st
->st_mode
);
116 /* disks are char devices on *BSD */
117 return S_ISCHR( st
->st_mode
);
121 static void detect_devices( const char **paths
, char *names
, ULONG size
)
130 int len
= sprintf( unix_path
, *paths
, i
++ );
131 if (len
+ 2 > size
) break;
132 if (access( unix_path
, F_OK
) != 0) break;
133 strcpy( names
, unix_path
);
142 void queue_device_op( enum device_op op
, const char *udi
, const char *device
,
143 const char *mount_point
, enum device_type type
, const GUID
*guid
,
144 const char *serial
, const struct scsi_info
*scsi_info
)
146 struct device_info
*info
;
149 info
= calloc( 1, sizeof(*info
) );
150 str
= info
->str_buffer
;
151 end
= info
->str_buffer
+ sizeof(info
->str_buffer
);
154 #define ADD_STR(s) if (s && str + strlen(s) + 1 <= end) \
156 info->s = strcpy( str, s ); \
157 str += strlen(str) + 1; \
161 ADD_STR(mount_point
);
166 info
->guid_buffer
= *guid
;
167 info
->guid
= &info
->guid_buffer
;
171 info
->scsi_buffer
= *scsi_info
;
172 info
->scsi_info
= &info
->scsi_buffer
;
174 NtQueueApcThread( run_loop_params
.op_thread
, run_loop_params
.op_apc
, (ULONG_PTR
)info
, 0, 0 );
177 static NTSTATUS
run_loop( void *args
)
179 const struct run_loop_params
*params
= args
;
181 run_loop_params
= *params
;
182 run_diskarbitration_loop();
184 return STATUS_SUCCESS
;
187 static NTSTATUS
dequeue_device_op( void *args
)
189 const struct dequeue_device_op_params
*params
= args
;
190 struct device_info
*src
= (struct device_info
*)params
->arg
;
191 struct device_info
*dst
= params
->info
;
193 /* copy info to client address space and fix up pointers */
195 if (dst
->udi
) dst
->udi
= (char *)dst
+ (src
->udi
- (char *)src
);
196 if (dst
->device
) dst
->device
= (char *)dst
+ (src
->device
- (char *)src
);
197 if (dst
->mount_point
) dst
->mount_point
= (char *)dst
+ (src
->mount_point
- (char *)src
);
198 if (dst
->serial
) dst
->serial
= (char *)dst
+ (src
->serial
- (char *)src
);
199 if (dst
->guid
) dst
->guid
= &dst
->guid_buffer
;
200 if (dst
->scsi_info
) dst
->scsi_info
= &dst
->scsi_buffer
;
203 return STATUS_SUCCESS
;
206 /* find or create a DOS drive for the corresponding Unix device */
207 static NTSTATUS
add_drive( void *args
)
209 const struct add_drive_params
*params
= args
;
212 struct stat dev_st
, drive_st
;
213 int drive
, first
, last
, avail
= 0;
215 if (stat( params
->device
, &dev_st
) == -1 || !is_valid_device( &dev_st
)) return STATUS_NO_SUCH_DEVICE
;
217 if (!(path
= get_dosdevices_path( "a::" ))) return STATUS_NO_MEMORY
;
218 p
= path
+ strlen(path
) - 3;
220 memset( in_use
, 0, sizeof(in_use
) );
222 switch (params
->type
)
242 for (drive
= first
; drive
< last
; drive
++)
244 if (in_use
[drive
]) continue; /* already checked */
246 if (stat( path
, &drive_st
) == -1)
248 if (lstat( path
, &drive_st
) == -1 && errno
== ENOENT
) /* this is a candidate */
253 /* if mount point symlink doesn't exist either, it's available */
254 if (lstat( path
, &drive_st
) == -1 && errno
== ENOENT
) avail
= drive
;
258 else in_use
[drive
] = 1;
263 if (!is_valid_device( &drive_st
)) continue;
264 if (dev_st
.st_rdev
== drive_st
.st_rdev
) goto done
;
269 /* try to use the one we found */
272 if (symlink( params
->device
, path
) != -1) goto done
;
273 /* failed, retry the search */
277 return STATUS_OBJECT_NAME_COLLISION
;
281 *params
->letter
= drive
;
282 return STATUS_SUCCESS
;
285 static NTSTATUS
get_dosdev_symlink( void *args
)
287 const struct get_dosdev_symlink_params
*params
= args
;
291 if (!(path
= get_dosdevices_path( params
->dev
))) return STATUS_NO_MEMORY
;
293 ret
= readlink( path
, params
->dest
, params
->size
);
295 if (ret
== -1) return STATUS_NO_SUCH_DEVICE
;
296 if (ret
== params
->size
) return STATUS_BUFFER_TOO_SMALL
;
297 params
->dest
[ret
] = 0;
298 return STATUS_SUCCESS
;
301 static NTSTATUS
set_dosdev_symlink( void *args
)
303 const struct set_dosdev_symlink_params
*params
= args
;
305 NTSTATUS status
= STATUS_SUCCESS
;
307 if (!(path
= get_dosdevices_path( params
->dev
))) return STATUS_NO_MEMORY
;
309 if (params
->dest
&& params
->dest
[0])
312 if (symlink( params
->dest
, path
) == -1) status
= STATUS_ACCESS_DENIED
;
320 static NTSTATUS
get_volume_size_info( void *args
)
322 const struct get_volume_size_info_params
*params
= args
;
323 const char *unix_mount
= params
->unix_mount
;
324 struct size_info
*info
= params
->info
;
331 #if !defined(linux) || !defined(HAVE_FSTATFS)
337 if (!unix_mount
) return STATUS_NO_SUCH_DEVICE
;
339 if (unix_mount
[0] != '/')
341 char *path
= get_dosdevices_path( unix_mount
);
342 if (path
) fd
= open( path
, O_RDONLY
);
345 else fd
= open( unix_mount
, O_RDONLY
);
347 if (fstat( fd
, &st
) < 0)
349 status
= errno_to_status( errno
);
352 if (!S_ISREG(st
.st_mode
) && !S_ISDIR(st
.st_mode
))
354 status
= STATUS_INVALID_DEVICE_REQUEST
;
358 /* Linux's fstatvfs is buggy */
359 #if !defined(linux) || !defined(HAVE_FSTATFS)
360 if (fstatvfs( fd
, &stfs
) < 0)
362 status
= errno_to_status( errno
);
365 bsize
= stfs
.f_frsize
;
367 if (fstatfs( fd
, &stfs
) < 0)
369 status
= errno_to_status( errno
);
372 bsize
= stfs
.f_bsize
;
374 if (bsize
== 2048) /* assume CD-ROM */
376 info
->bytes_per_sector
= 2048;
377 info
->sectors_per_allocation_unit
= 1;
381 info
->bytes_per_sector
= 512;
382 info
->sectors_per_allocation_unit
= 8;
385 info
->total_allocation_units
=
386 bsize
* stfs
.f_blocks
/ (info
->bytes_per_sector
* info
->sectors_per_allocation_unit
);
387 info
->caller_available_allocation_units
=
388 bsize
* stfs
.f_bavail
/ (info
->bytes_per_sector
* info
->sectors_per_allocation_unit
);
389 info
->actual_available_allocation_units
=
390 bsize
* stfs
.f_bfree
/ (info
->bytes_per_sector
* info
->sectors_per_allocation_unit
);
392 status
= STATUS_SUCCESS
;
399 static NTSTATUS
get_volume_dos_devices( void *args
)
401 const struct get_volume_dos_devices_params
*params
= args
;
402 struct stat dev_st
, drive_st
;
406 if (stat( params
->mount_point
, &dev_st
) == -1) return STATUS_NO_SUCH_DEVICE
;
407 if (!(path
= get_dosdevices_path( "a:" ))) return STATUS_NO_MEMORY
;
410 for (i
= 0; i
< 26; i
++)
412 path
[strlen(path
) - 2] = 'a' + i
;
413 if (stat( path
, &drive_st
) != -1 && drive_st
.st_rdev
== dev_st
.st_rdev
) *params
->dosdev
|= 1 << i
;
416 return STATUS_SUCCESS
;
419 static NTSTATUS
read_volume_file( void *args
)
421 const struct read_volume_file_params
*params
= args
;
423 char *name
= malloc( strlen(params
->volume
) + strlen(params
->file
) + 2 );
425 sprintf( name
, "%s/%s", params
->volume
, params
->file
);
429 char *path
= get_dosdevices_path( name
);
430 if (path
) fd
= open( path
, O_RDONLY
);
433 else fd
= open( name
, O_RDONLY
);
436 if (fd
== -1) return STATUS_NO_SUCH_FILE
;
437 ret
= read( fd
, params
->buffer
, *params
->size
);
439 if (ret
== -1) return STATUS_NO_SUCH_FILE
;
441 return STATUS_SUCCESS
;
444 static NTSTATUS
match_unixdev( void *args
)
446 const struct match_unixdev_params
*params
= args
;
449 return !stat( params
->device
, &st
) && st
.st_rdev
== params
->unix_dev
;
452 static NTSTATUS
check_device_access( void *args
)
455 const char *unix_device
= args
;
456 if (access( unix_device
, R_OK
)) return STATUS_ACCESS_DENIED
;
458 return STATUS_SUCCESS
;
461 static NTSTATUS
detect_serial_ports( void *args
)
463 const struct detect_ports_params
*params
= args
;
464 static const char *paths
[] =
470 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
472 #elif defined(__DragonFly__)
478 detect_devices( paths
, params
->names
, params
->size
);
479 return STATUS_SUCCESS
;
482 static NTSTATUS
detect_parallel_ports( void *args
)
484 const struct detect_ports_params
*params
= args
;
485 static const char *paths
[] =
493 detect_devices( paths
, params
->names
, params
->size
);
494 return STATUS_SUCCESS
;
497 static NTSTATUS
set_shell_folder( void *args
)
499 const struct set_shell_folder_params
*params
= args
;
500 const char *folder
= params
->folder
;
501 const char *backup
= params
->backup
;
502 const char *link
= params
->link
;
505 char *homelink
= NULL
;
506 NTSTATUS status
= STATUS_SUCCESS
;
508 if (link
&& (!strcmp( link
, "$HOME" ) || !strncmp( link
, "$HOME/", 6 )) && (home
= getenv( "HOME" )))
511 homelink
= malloc( strlen(home
) + strlen(link
) + 1 );
512 strcpy( homelink
, home
);
513 strcat( homelink
, link
);
517 /* ignore nonexistent link targets */
518 if (link
&& (stat( link
, &st
) || !S_ISDIR( st
.st_mode
)))
520 status
= STATUS_OBJECT_NAME_NOT_FOUND
;
524 if (!lstat( folder
, &st
)) /* move old folder/link out of the way */
526 if (S_ISLNK( st
.st_mode
))
530 else if (link
&& S_ISDIR( st
.st_mode
))
532 if (rmdir( folder
)) /* non-empty dir, try to make a backup */
534 if (!backup
|| rename( folder
, backup
))
536 status
= STATUS_OBJECT_NAME_COLLISION
;
541 else goto done
; /* nothing to do, folder already exists */
544 if (link
) symlink( link
, folder
);
547 if (backup
&& !lstat( backup
, &st
) && S_ISDIR( st
.st_mode
)) rename( backup
, folder
);
548 else mkdir( folder
, 0777 );
556 static NTSTATUS
get_shell_folder( void *args
)
558 const struct get_shell_folder_params
*params
= args
;
559 int ret
= readlink( params
->folder
, params
->buffer
, params
->size
- 1 );
561 if (ret
< 0) return STATUS_OBJECT_NAME_NOT_FOUND
;
562 params
->buffer
[ret
] = 0;
563 return STATUS_SUCCESS
;
566 const unixlib_entry_t __wine_unix_call_funcs
[] =
573 get_volume_size_info
,
574 get_volume_dos_devices
,
579 detect_parallel_ports
,
587 enumerate_credentials
,