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
37 static struct run_loop_params run_loop_params
;
39 static char *get_dosdevices_path( const char *dev
)
41 const char *home
= getenv( "HOME" );
42 const char *prefix
= getenv( "WINEPREFIX" );
43 size_t len
= (prefix
? strlen(prefix
) : strlen(home
) + strlen("/.wine")) + sizeof("/dosdevices/") + strlen(dev
);
44 char *path
= malloc( len
);
48 if (prefix
) strcpy( path
, prefix
);
52 strcat( path
, "/.wine" );
54 strcat( path
, "/dosdevices/" );
60 static BOOL
is_valid_device( struct stat
*st
)
62 #if defined(linux) || defined(__sun__)
63 return S_ISBLK( st
->st_mode
);
65 /* disks are char devices on *BSD */
66 return S_ISCHR( st
->st_mode
);
70 static void detect_devices( const char **paths
, char *names
, ULONG size
)
79 int len
= sprintf( unix_path
, *paths
, i
++ );
80 if (len
+ 2 > size
) break;
81 if (access( unix_path
, F_OK
) != 0) break;
82 strcpy( names
, unix_path
);
91 void queue_device_op( enum device_op op
, const char *udi
, const char *device
,
92 const char *mount_point
, enum device_type type
, const GUID
*guid
,
93 const char *serial
, const struct scsi_info
*scsi_info
)
95 struct device_info
*info
;
98 info
= calloc( 1, sizeof(*info
) );
99 str
= info
->str_buffer
;
100 end
= info
->str_buffer
+ sizeof(info
->str_buffer
);
103 #define ADD_STR(s) if (s && str + strlen(s) + 1 <= end) \
105 info->s = strcpy( str, s ); \
106 str += strlen(str) + 1; \
110 ADD_STR(mount_point
);
115 info
->guid_buffer
= *guid
;
116 info
->guid
= &info
->guid_buffer
;
120 info
->scsi_buffer
= *scsi_info
;
121 info
->scsi_info
= &info
->scsi_buffer
;
123 NtQueueApcThread( run_loop_params
.op_thread
, run_loop_params
.op_apc
, (ULONG_PTR
)info
, 0, 0 );
126 static NTSTATUS
run_loop( void *args
)
128 const struct run_loop_params
*params
= args
;
130 run_loop_params
= *params
;
131 run_diskarbitration_loop();
133 return STATUS_SUCCESS
;
136 static NTSTATUS
dequeue_device_op( void *args
)
138 const struct dequeue_device_op_params
*params
= args
;
139 struct device_info
*src
= (struct device_info
*)params
->arg
;
140 struct device_info
*dst
= params
->info
;
142 /* copy info to client address space and fix up pointers */
144 if (dst
->udi
) dst
->udi
= (char *)dst
+ (src
->udi
- (char *)src
);
145 if (dst
->device
) dst
->device
= (char *)dst
+ (src
->device
- (char *)src
);
146 if (dst
->mount_point
) dst
->mount_point
= (char *)dst
+ (src
->mount_point
- (char *)src
);
147 if (dst
->serial
) dst
->serial
= (char *)dst
+ (src
->serial
- (char *)src
);
148 if (dst
->guid
) dst
->guid
= &dst
->guid_buffer
;
149 if (dst
->scsi_info
) dst
->scsi_info
= &dst
->scsi_buffer
;
152 return STATUS_SUCCESS
;
155 /* find or create a DOS drive for the corresponding Unix device */
156 static NTSTATUS
add_drive( void *args
)
158 const struct add_drive_params
*params
= args
;
161 struct stat dev_st
, drive_st
;
162 int drive
, first
, last
, avail
= 0;
164 if (stat( params
->device
, &dev_st
) == -1 || !is_valid_device( &dev_st
)) return STATUS_NO_SUCH_DEVICE
;
166 if (!(path
= get_dosdevices_path( "a::" ))) return STATUS_NO_MEMORY
;
167 p
= path
+ strlen(path
) - 3;
169 memset( in_use
, 0, sizeof(in_use
) );
171 switch (params
->type
)
191 for (drive
= first
; drive
< last
; drive
++)
193 if (in_use
[drive
]) continue; /* already checked */
195 if (stat( path
, &drive_st
) == -1)
197 if (lstat( path
, &drive_st
) == -1 && errno
== ENOENT
) /* this is a candidate */
202 /* if mount point symlink doesn't exist either, it's available */
203 if (lstat( path
, &drive_st
) == -1 && errno
== ENOENT
) avail
= drive
;
207 else in_use
[drive
] = 1;
212 if (!is_valid_device( &drive_st
)) continue;
213 if (dev_st
.st_rdev
== drive_st
.st_rdev
) goto done
;
218 /* try to use the one we found */
221 if (symlink( params
->device
, path
) != -1) goto done
;
222 /* failed, retry the search */
226 return STATUS_OBJECT_NAME_COLLISION
;
230 *params
->letter
= drive
;
231 return STATUS_SUCCESS
;
234 static NTSTATUS
get_dosdev_symlink( void *args
)
236 const struct get_dosdev_symlink_params
*params
= args
;
240 if (!(path
= get_dosdevices_path( params
->dev
))) return STATUS_NO_MEMORY
;
242 ret
= readlink( path
, params
->dest
, params
->size
);
244 if (ret
== -1) return STATUS_NO_SUCH_DEVICE
;
245 if (ret
== params
->size
) return STATUS_BUFFER_TOO_SMALL
;
246 params
->dest
[ret
] = 0;
247 return STATUS_SUCCESS
;
250 static NTSTATUS
set_dosdev_symlink( void *args
)
252 const struct set_dosdev_symlink_params
*params
= args
;
254 NTSTATUS status
= STATUS_SUCCESS
;
256 if (!(path
= get_dosdevices_path( params
->dev
))) return STATUS_NO_MEMORY
;
258 if (params
->dest
&& params
->dest
[0])
261 if (symlink( params
->dest
, path
) == -1) status
= STATUS_ACCESS_DENIED
;
269 static NTSTATUS
get_volume_dos_devices( void *args
)
271 const struct get_volume_dos_devices_params
*params
= args
;
272 struct stat dev_st
, drive_st
;
276 if (stat( params
->mount_point
, &dev_st
) == -1) return STATUS_NO_SUCH_DEVICE
;
277 if (!(path
= get_dosdevices_path( "a:" ))) return STATUS_NO_MEMORY
;
280 for (i
= 0; i
< 26; i
++)
282 path
[strlen(path
) - 2] = 'a' + i
;
283 if (stat( path
, &drive_st
) != -1 && drive_st
.st_rdev
== dev_st
.st_rdev
) *params
->dosdev
|= 1 << i
;
286 return STATUS_SUCCESS
;
289 static NTSTATUS
read_volume_file( void *args
)
291 const struct read_volume_file_params
*params
= args
;
293 char *name
= malloc( strlen(params
->volume
) + strlen(params
->file
) + 2 );
295 sprintf( name
, "%s/%s", params
->volume
, params
->file
);
299 char *path
= get_dosdevices_path( name
);
300 if (path
) fd
= open( path
, O_RDONLY
);
303 else fd
= open( name
, O_RDONLY
);
306 if (fd
== -1) return STATUS_NO_SUCH_FILE
;
307 ret
= read( fd
, params
->buffer
, *params
->size
);
309 if (ret
== -1) return STATUS_NO_SUCH_FILE
;
311 return STATUS_SUCCESS
;
314 static NTSTATUS
match_unixdev( void *args
)
316 const struct match_unixdev_params
*params
= args
;
319 return !stat( params
->device
, &st
) && st
.st_rdev
== params
->unix_dev
;
322 static NTSTATUS
check_device_access( void *args
)
325 const char *unix_device
= args
;
326 if (access( unix_device
, R_OK
)) return STATUS_ACCESS_DENIED
;
328 return STATUS_SUCCESS
;
331 static NTSTATUS
detect_serial_ports( void *args
)
333 const struct detect_ports_params
*params
= args
;
334 static const char *paths
[] =
340 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
342 #elif defined(__DragonFly__)
348 detect_devices( paths
, params
->names
, params
->size
);
349 return STATUS_SUCCESS
;
352 static NTSTATUS
detect_parallel_ports( void *args
)
354 const struct detect_ports_params
*params
= args
;
355 static const char *paths
[] =
363 detect_devices( paths
, params
->names
, params
->size
);
364 return STATUS_SUCCESS
;
367 static NTSTATUS
set_shell_folder( void *args
)
369 const struct set_shell_folder_params
*params
= args
;
370 const char *folder
= params
->folder
;
371 const char *backup
= params
->backup
;
372 const char *link
= params
->link
;
375 char *homelink
= NULL
;
376 NTSTATUS status
= STATUS_SUCCESS
;
378 if (link
&& (!strcmp( link
, "$HOME" ) || !strncmp( link
, "$HOME/", 6 )) && (home
= getenv( "HOME" )))
381 homelink
= malloc( strlen(home
) + strlen(link
) + 1 );
382 strcpy( homelink
, home
);
383 strcat( homelink
, link
);
387 /* ignore nonexistent link targets */
388 if (link
&& (stat( link
, &st
) || !S_ISDIR( st
.st_mode
)))
390 status
= STATUS_OBJECT_NAME_NOT_FOUND
;
394 if (!lstat( folder
, &st
)) /* move old folder/link out of the way */
396 if (S_ISLNK( st
.st_mode
))
400 else if (link
&& S_ISDIR( st
.st_mode
))
402 if (rmdir( folder
)) /* non-empty dir, try to make a backup */
404 if (!backup
|| rename( folder
, backup
))
406 status
= STATUS_OBJECT_NAME_COLLISION
;
411 else goto done
; /* nothing to do, folder already exists */
414 if (link
) symlink( link
, folder
);
417 if (backup
&& !lstat( backup
, &st
) && S_ISDIR( st
.st_mode
)) rename( backup
, folder
);
418 else mkdir( folder
, 0777 );
426 static NTSTATUS
get_shell_folder( void *args
)
428 const struct get_shell_folder_params
*params
= args
;
429 int ret
= readlink( params
->folder
, params
->buffer
, params
->size
- 1 );
431 if (ret
< 0) return STATUS_OBJECT_NAME_NOT_FOUND
;
432 params
->buffer
[ret
] = 0;
433 return STATUS_SUCCESS
;
436 const unixlib_entry_t __wine_unix_call_funcs
[] =
443 get_volume_dos_devices
,
448 detect_parallel_ports
,
456 enumerate_credentials
,