include/windows.foundation: Add double reference.
[wine.git] / dlls / mountmgr.sys / unixlib.c
blob13f6fbecf09c9a3caf306b46cf9f60054ca0550e
1 /*
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
21 #if 0
22 #pragma makedep unix
23 #endif
25 #include "config.h"
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <sys/stat.h>
33 #ifdef HAVE_SYS_STATFS_H
34 #include <sys/statfs.h>
35 #endif
36 #ifdef HAVE_SYS_STATVFS_H
37 # include <sys/statvfs.h>
38 #endif
39 #include <unistd.h>
41 #include "unixlib.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 );
51 switch (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;
57 case EPERM:
58 case EROFS:
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;
63 case EMFILE:
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;
69 #ifdef ENOMEDIUM
70 case ENOMEDIUM: return STATUS_NO_MEDIA_IN_DEVICE;
71 #endif
72 case ENXIO: return STATUS_NO_SUCH_DEVICE;
73 case ENOTTY:
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;
81 #endif
82 case ENOEXEC: /* ?? */
83 case EEXIST: /* ?? */
84 default:
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 );
97 if (path)
99 if (prefix) strcpy( path, prefix );
100 else
102 strcpy( path, home );
103 strcat( path, "/.wine" );
105 strcat( path, "/dosdevices/" );
106 strcat( path, dev );
108 return path;
111 static BOOL is_valid_device( struct stat *st )
113 #if defined(linux) || defined(__sun__)
114 return S_ISBLK( st->st_mode );
115 #else
116 /* disks are char devices on *BSD */
117 return S_ISCHR( st->st_mode );
118 #endif
121 static void detect_devices( const char **paths, char *names, ULONG size )
123 while (*paths)
125 char unix_path[32];
126 unsigned int i = 0;
128 for (;;)
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 );
134 names += len + 1;
135 size -= len + 1;
137 paths++;
139 *names = 0;
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;
147 char *str, *end;
149 info = calloc( 1, sizeof(*info) );
150 str = info->str_buffer;
151 end = info->str_buffer + sizeof(info->str_buffer);
152 info->op = op;
153 info->type = type;
154 #define ADD_STR(s) if (s && str + strlen(s) + 1 <= end) \
156 info->s = strcpy( str, s ); \
157 str += strlen(str) + 1; \
159 ADD_STR(udi);
160 ADD_STR(device);
161 ADD_STR(mount_point);
162 ADD_STR(serial);
163 #undef ADD_STR
164 if (guid)
166 info->guid_buffer = *guid;
167 info->guid = &info->guid_buffer;
169 if (scsi_info)
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();
183 run_dbus_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 */
194 *dst = *src;
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;
202 free( src );
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;
210 char *path, *p;
211 char in_use[26];
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)
224 case DEVICE_FLOPPY:
225 first = 0;
226 last = 2;
227 break;
228 case DEVICE_CDROM:
229 case DEVICE_DVD:
230 first = 3;
231 last = 26;
232 break;
233 default:
234 first = 2;
235 last = 26;
236 break;
239 while (avail != -1)
241 avail = -1;
242 for (drive = first; drive < last; drive++)
244 if (in_use[drive]) continue; /* already checked */
245 *p = 'a' + drive;
246 if (stat( path, &drive_st ) == -1)
248 if (lstat( path, &drive_st ) == -1 && errno == ENOENT) /* this is a candidate */
250 if (avail == -1)
252 p[2] = 0;
253 /* if mount point symlink doesn't exist either, it's available */
254 if (lstat( path, &drive_st ) == -1 && errno == ENOENT) avail = drive;
255 p[2] = ':';
258 else in_use[drive] = 1;
260 else
262 in_use[drive] = 1;
263 if (!is_valid_device( &drive_st )) continue;
264 if (dev_st.st_rdev == drive_st.st_rdev) goto done;
267 if (avail != -1)
269 /* try to use the one we found */
270 drive = avail;
271 *p = 'a' + drive;
272 if (symlink( params->device, path ) != -1) goto done;
273 /* failed, retry the search */
276 free( path );
277 return STATUS_OBJECT_NAME_COLLISION;
279 done:
280 free( path );
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;
288 char *path;
289 int ret;
291 if (!(path = get_dosdevices_path( params->dev ))) return STATUS_NO_MEMORY;
293 ret = readlink( path, params->dest, params->size );
294 free( path );
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;
304 char *path;
305 NTSTATUS status = STATUS_SUCCESS;
307 if (!(path = get_dosdevices_path( params->dev ))) return STATUS_NO_MEMORY;
309 if (params->dest && params->dest[0])
311 unlink( path );
312 if (symlink( params->dest, path ) == -1) status = STATUS_ACCESS_DENIED;
314 else unlink( path );
316 free( path );
317 return status;
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;
326 struct stat st;
327 ULONGLONG bsize;
328 NTSTATUS status;
329 int fd = -1;
331 #if !defined(linux) || !defined(HAVE_FSTATFS)
332 struct statvfs stfs;
333 #else
334 struct statfs stfs;
335 #endif
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 );
343 free( path );
345 else fd = open( unix_mount, O_RDONLY );
347 if (fstat( fd, &st ) < 0)
349 status = errno_to_status( errno );
350 goto done;
352 if (!S_ISREG(st.st_mode) && !S_ISDIR(st.st_mode))
354 status = STATUS_INVALID_DEVICE_REQUEST;
355 goto done;
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 );
363 goto done;
365 bsize = stfs.f_frsize;
366 #else
367 if (fstatfs( fd, &stfs ) < 0)
369 status = errno_to_status( errno );
370 goto done;
372 bsize = stfs.f_bsize;
373 #endif
374 if (bsize == 2048) /* assume CD-ROM */
376 info->bytes_per_sector = 2048;
377 info->sectors_per_allocation_unit = 1;
379 else
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;
394 done:
395 close( fd );
396 return status;
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;
403 char *path;
404 int i;
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;
409 *params->dosdev = 0;
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;
415 free( path );
416 return STATUS_SUCCESS;
419 static NTSTATUS read_volume_file( void *args )
421 const struct read_volume_file_params *params = args;
422 int ret, fd = -1;
423 char *name = malloc( strlen(params->volume) + strlen(params->file) + 2 );
425 sprintf( name, "%s/%s", params->volume, params->file );
427 if (name[0] != '/')
429 char *path = get_dosdevices_path( name );
430 if (path) fd = open( path, O_RDONLY );
431 free( path );
433 else fd = open( name, O_RDONLY );
435 free( name );
436 if (fd == -1) return STATUS_NO_SUCH_FILE;
437 ret = read( fd, params->buffer, *params->size );
438 close( fd );
439 if (ret == -1) return STATUS_NO_SUCH_FILE;
440 *params->size = ret;
441 return STATUS_SUCCESS;
444 static NTSTATUS match_unixdev( void *args )
446 const struct match_unixdev_params *params = args;
447 struct stat st;
449 return !stat( params->device, &st ) && st.st_rdev == params->unix_dev;
452 static NTSTATUS check_device_access( void *args )
454 #ifdef __APPLE__
455 const char *unix_device = args;
456 if (access( unix_device, R_OK )) return STATUS_ACCESS_DENIED;
457 #endif
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[] =
466 #ifdef linux
467 "/dev/ttyS%u",
468 "/dev/ttyUSB%u",
469 "/dev/ttyACM%u",
470 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
471 "/dev/cuau%u",
472 #elif defined(__DragonFly__)
473 "/dev/cuaa%u",
474 #endif
475 NULL
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[] =
487 #ifdef linux
488 "/dev/lp%u",
489 #endif
490 NULL
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;
503 struct stat st;
504 const char *home;
505 char *homelink = NULL;
506 NTSTATUS status = STATUS_SUCCESS;
508 if (link && (!strcmp( link, "$HOME" ) || !strncmp( link, "$HOME/", 6 )) && (home = getenv( "HOME" )))
510 link += 5;
511 homelink = malloc( strlen(home) + strlen(link) + 1 );
512 strcpy( homelink, home );
513 strcat( homelink, link );
514 link = homelink;
517 /* ignore nonexistent link targets */
518 if (link && (stat( link, &st ) || !S_ISDIR( st.st_mode )))
520 status = STATUS_OBJECT_NAME_NOT_FOUND;
521 goto done;
524 if (!lstat( folder, &st )) /* move old folder/link out of the way */
526 if (S_ISLNK( st.st_mode ))
528 unlink( folder );
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;
537 goto done;
541 else goto done; /* nothing to do, folder already exists */
544 if (link) symlink( link, folder );
545 else
547 if (backup && !lstat( backup, &st ) && S_ISDIR( st.st_mode )) rename( backup, folder );
548 else mkdir( folder, 0777 );
551 done:
552 free( homelink );
553 return status;
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[] =
568 run_loop,
569 dequeue_device_op,
570 add_drive,
571 get_dosdev_symlink,
572 set_dosdev_symlink,
573 get_volume_size_info,
574 get_volume_dos_devices,
575 read_volume_file,
576 match_unixdev,
577 check_device_access,
578 detect_serial_ports,
579 detect_parallel_ports,
580 set_shell_folder,
581 get_shell_folder,
582 dhcp_request,
583 query_symbol_file,
584 read_credential,
585 write_credential,
586 delete_credential,
587 enumerate_credentials,