source: mainline/uspace/lib/device/src/vol.c

Last change on this file was 629b480, checked in by Jiri Svoboda <jiri@…>, 2 months ago

Assorted installer fixes.

  • Property mode set to 100644
File size: 14.5 KB
Line 
1/*
2 * Copyright (c) 2025 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libdevice
30 * @{
31 */
32/** @file Volume service API
33 */
34
35#include <abi/ipc/interfaces.h>
36#include <errno.h>
37#include <ipc/services.h>
38#include <ipc/vol.h>
39#include <loc.h>
40#include <stdlib.h>
41#include <str.h>
42#include <vfs/vfs.h>
43#include <vol.h>
44
45/** Create Volume service session.
46 *
47 * @param rvol Place to store pointer to volume service session.
48 * @return EOK on success, ENOMEM if out of memory, EIO if service
49 * cannot be contacted.
50 */
51errno_t vol_create(vol_t **rvol)
52{
53 vol_t *vol;
54 service_id_t vol_svcid;
55 errno_t rc;
56
57 vol = calloc(1, sizeof(vol_t));
58 if (vol == NULL) {
59 rc = ENOMEM;
60 goto error;
61 }
62
63 rc = loc_service_get_id(SERVICE_NAME_VOLSRV, &vol_svcid, 0);
64 if (rc != EOK) {
65 rc = ENOENT;
66 goto error;
67 }
68
69 vol->sess = loc_service_connect(vol_svcid, INTERFACE_VOL, 0);
70 if (vol->sess == NULL) {
71 rc = EIO;
72 goto error;
73 }
74
75 *rvol = vol;
76 return EOK;
77error:
78 free(vol);
79 return rc;
80}
81
82/** Destroy volume service session.
83 *
84 * @param vol Volume service session
85 */
86void vol_destroy(vol_t *vol)
87{
88 if (vol == NULL)
89 return;
90
91 async_hangup(vol->sess);
92 free(vol);
93}
94
95/** Get list of IDs into a buffer of fixed size.
96 *
97 * @param vol Volume service
98 * @param method IPC method
99 * @param arg1 First argument
100 * @param id_buf Buffer to store IDs
101 * @param buf_size Buffer size
102 * @param act_size Place to store actual size of complete data.
103 *
104 * @return EOK on success or an error code.
105 */
106static errno_t vol_get_ids_once(vol_t *vol, sysarg_t method, sysarg_t arg1,
107 sysarg_t *id_buf, size_t buf_size, size_t *act_size)
108{
109 async_exch_t *exch = async_exchange_begin(vol->sess);
110
111 ipc_call_t answer;
112 aid_t req = async_send_1(exch, method, arg1, &answer);
113 errno_t rc = async_data_read_start(exch, id_buf, buf_size);
114
115 async_exchange_end(exch);
116
117 if (rc != EOK) {
118 async_forget(req);
119 return rc;
120 }
121
122 errno_t retval;
123 async_wait_for(req, &retval);
124
125 if (retval != EOK) {
126 return retval;
127 }
128
129 *act_size = ipc_get_arg1(&answer);
130 return EOK;
131}
132
133/** Get list of IDs.
134 *
135 * Returns an allocated array of service IDs.
136 *
137 * @param vol Volume service
138 * @param method IPC method
139 * @param arg1 IPC argument 1
140 * @param data Place to store pointer to array of IDs
141 * @param count Place to store number of IDs
142 * @return EOK on success or an error code
143 */
144static errno_t vol_get_ids_internal(vol_t *vol, sysarg_t method, sysarg_t arg1,
145 sysarg_t **data, size_t *count)
146{
147 *data = NULL;
148 *count = 0;
149
150 size_t act_size = 0;
151 errno_t rc = vol_get_ids_once(vol, method, arg1, NULL, 0, &act_size);
152 if (rc != EOK)
153 return rc;
154
155 size_t alloc_size = act_size;
156 service_id_t *ids = malloc(alloc_size);
157 if (ids == NULL)
158 return ENOMEM;
159
160 while (true) {
161 rc = vol_get_ids_once(vol, method, arg1, ids, alloc_size,
162 &act_size);
163 if (rc != EOK)
164 return rc;
165
166 if (act_size <= alloc_size)
167 break;
168
169 alloc_size = act_size;
170 service_id_t *temp = realloc(ids, alloc_size);
171 if (temp == NULL) {
172 free(ids);
173 return ENOMEM;
174 }
175 ids = temp;
176 }
177
178 *count = act_size / sizeof(service_id_t);
179 *data = ids;
180 return EOK;
181}
182
183/** Get list of partitions as array of service IDs.
184 *
185 * @param vol Volume service
186 * @param data Place to store pointer to array
187 * @param count Place to store length of array (number of entries)
188 *
189 * @return EOK on success or an error code
190 */
191errno_t vol_get_parts(vol_t *vol, service_id_t **data, size_t *count)
192{
193 return vol_get_ids_internal(vol, VOL_GET_PARTS, 0, data, count);
194}
195
196/** Add partition.
197 *
198 * After a partition is created (e.g. as a result of deleting a label
199 * the dummy partition is created), it can take some (unknown) time
200 * until it is discovered.
201 *
202 * @param vol Volume service
203 * @param sid Service ID of the partition
204 * @return EOK on success or an error code
205 */
206errno_t vol_part_add(vol_t *vol, service_id_t sid)
207{
208 async_exch_t *exch;
209 errno_t retval;
210
211 exch = async_exchange_begin(vol->sess);
212 retval = async_req_1_0(exch, VOL_PART_ADD, sid);
213 async_exchange_end(exch);
214
215 if (retval != EOK)
216 return retval;
217
218 return EOK;
219}
220
221/** Get partition information.
222 *
223 * @param vol Volume service
224 * @param sid Service ID of the partition
225 * @param vinfo Place to store partition information
226 * @return EOK on success or an error code
227 */
228errno_t vol_part_info(vol_t *vol, service_id_t sid, vol_part_info_t *vinfo)
229{
230 async_exch_t *exch;
231 errno_t retval;
232 ipc_call_t answer;
233
234 exch = async_exchange_begin(vol->sess);
235 aid_t req = async_send_1(exch, VOL_PART_INFO, sid, &answer);
236
237 errno_t rc = async_data_read_start(exch, vinfo, sizeof(vol_part_info_t));
238 async_exchange_end(exch);
239 if (rc != EOK) {
240 async_forget(req);
241 return EIO;
242 }
243
244 async_wait_for(req, &retval);
245 if (retval != EOK)
246 return EIO;
247
248 return EOK;
249}
250
251/** Unmount partition (and possibly eject the media).
252 *
253 * @param vol Volume service
254 * @param sid Service ID of the partition
255 * @param flags Ejection flags
256 * @return EOK on success or an error code
257 */
258errno_t vol_part_eject(vol_t *vol, service_id_t sid, vol_eject_flags_t flags)
259{
260 async_exch_t *exch;
261 errno_t retval;
262
263 exch = async_exchange_begin(vol->sess);
264 retval = async_req_2_0(exch, VOL_PART_EJECT, sid, flags);
265 async_exchange_end(exch);
266
267 if (retval != EOK)
268 return retval;
269
270 return EOK;
271}
272
273/** Erase partition (to the extent where we will consider it not containing
274 * a file system.
275 *
276 * @param vol Volume service
277 * @param sid Service ID of the partition
278 * @return EOK on success or an error code
279 */
280errno_t vol_part_empty(vol_t *vol, service_id_t sid)
281{
282 async_exch_t *exch;
283 errno_t retval;
284
285 exch = async_exchange_begin(vol->sess);
286 retval = async_req_1_0(exch, VOL_PART_EMPTY, sid);
287 async_exchange_end(exch);
288
289 if (retval != EOK)
290 return retval;
291
292 return EOK;
293}
294
295/** Insert volume.
296 *
297 * This will re-mount the volume if it has been ejected previously.
298 *
299 * @param vol Volume service
300 * @param sid Service ID of the partition
301 * @return EOK on success or an error code
302 */
303errno_t vol_part_insert(vol_t *vol, service_id_t sid)
304{
305 async_exch_t *exch;
306 errno_t retval;
307
308 exch = async_exchange_begin(vol->sess);
309 retval = async_req_1_0(exch, VOL_PART_INSERT, sid);
310 async_exchange_end(exch);
311
312 if (retval != EOK)
313 return retval;
314
315 return EOK;
316}
317
318/** Insert volume by path.
319 *
320 * @param vol Volume service
321 * @param path Filesystem path
322 *
323 * @return EOK on success or an error code
324 */
325errno_t vol_part_insert_by_path(vol_t *vol, const char *path)
326{
327 async_exch_t *exch;
328 ipc_call_t answer;
329 errno_t retval;
330
331 exch = async_exchange_begin(vol->sess);
332 aid_t req = async_send_0(exch, VOL_PART_INSERT_BY_PATH, &answer);
333
334 retval = async_data_write_start(exch, path, str_size(path));
335 if (retval != EOK) {
336 async_exchange_end(exch);
337 async_forget(req);
338 return retval;
339 }
340
341 async_exchange_end(exch);
342 async_wait_for(req, &retval);
343
344 if (retval != EOK)
345 return retval;
346
347 return EOK;
348}
349
350/** Get volume label support.
351 *
352 * @param vol Volume service
353 * @param fstype File system type
354 * @param vlsupp Place to store volume label support information
355 * @return EOK on success or an error code
356 */
357errno_t vol_part_get_lsupp(vol_t *vol, vol_fstype_t fstype,
358 vol_label_supp_t *vlsupp)
359{
360 async_exch_t *exch;
361 errno_t retval;
362 ipc_call_t answer;
363
364 exch = async_exchange_begin(vol->sess);
365 aid_t req = async_send_1(exch, VOL_PART_LSUPP, fstype, &answer);
366 errno_t rc = async_data_read_start(exch, vlsupp, sizeof(vol_label_supp_t));
367 async_exchange_end(exch);
368
369 if (rc != EOK) {
370 async_forget(req);
371 return EIO;
372 }
373
374 async_wait_for(req, &retval);
375 if (retval != EOK)
376 return EIO;
377
378 return EOK;
379}
380
381/** Create file system.
382 *
383 * @param vol Volume service
384 * @param sid Partition service ID
385 * @param fstype File system type
386 * @param label Volume label
387 * @param mountp Mount point
388 *
389 * @return EOK on success or an error code
390 */
391errno_t vol_part_mkfs(vol_t *vol, service_id_t sid, vol_fstype_t fstype,
392 const char *label, const char *mountp)
393{
394 async_exch_t *exch;
395 ipc_call_t answer;
396 errno_t retval;
397
398 exch = async_exchange_begin(vol->sess);
399 aid_t req = async_send_2(exch, VOL_PART_MKFS, sid, fstype, &answer);
400
401 retval = async_data_write_start(exch, label, str_size(label));
402 if (retval != EOK) {
403 async_exchange_end(exch);
404 async_forget(req);
405 return retval;
406 }
407
408 retval = async_data_write_start(exch, mountp, str_size(mountp));
409 if (retval != EOK) {
410 async_exchange_end(exch);
411 async_forget(req);
412 return retval;
413 }
414
415 async_exchange_end(exch);
416 async_wait_for(req, &retval);
417
418 if (retval != EOK)
419 return retval;
420
421 return EOK;
422}
423
424/** Set mount point for partition.
425 *
426 * @param vol Volume service
427 * @param sid Partition service ID
428 * @param mountp Mount point
429 *
430 * @return EOK on success or an error code
431 */
432errno_t vol_part_set_mountp(vol_t *vol, service_id_t sid,
433 const char *mountp)
434{
435 async_exch_t *exch;
436 ipc_call_t answer;
437 errno_t retval;
438
439 exch = async_exchange_begin(vol->sess);
440 aid_t req = async_send_1(exch, VOL_PART_SET_MOUNTP, sid,
441 &answer);
442
443 retval = async_data_write_start(exch, mountp, str_size(mountp));
444 if (retval != EOK) {
445 async_exchange_end(exch);
446 async_forget(req);
447 return retval;
448 }
449
450 async_exchange_end(exch);
451 async_wait_for(req, &retval);
452
453 if (retval != EOK)
454 return retval;
455
456 return EOK;
457}
458
459/** Format file system type as string.
460 *
461 * @param fstype File system type
462 * @param rstr Place to store pointer to newly allocated string
463 * @return EOK on success, ENOMEM if out of memory
464 */
465errno_t vol_fstype_format(vol_fstype_t fstype, char **rstr)
466{
467 const char *sfstype;
468 char *s;
469
470 sfstype = NULL;
471 switch (fstype) {
472 case fs_exfat:
473 sfstype = "ExFAT";
474 break;
475 case fs_fat:
476 sfstype = "FAT";
477 break;
478 case fs_minix:
479 sfstype = "MINIX";
480 break;
481 case fs_ext4:
482 sfstype = "Ext4";
483 break;
484 case fs_cdfs:
485 sfstype = "ISO 9660";
486 break;
487 }
488
489 s = str_dup(sfstype);
490 if (s == NULL)
491 return ENOMEM;
492
493 *rstr = s;
494 return EOK;
495}
496
497/** Format partition content / file system type as string.
498 *
499 * @param pcnt Partition content
500 * @param fstype File system type
501 * @param rstr Place to store pointer to newly allocated string
502 * @return EOK on success, ENOMEM if out of memory
503 */
504errno_t vol_pcnt_fs_format(vol_part_cnt_t pcnt, vol_fstype_t fstype,
505 char **rstr)
506{
507 int rc;
508 char *s = NULL;
509
510 switch (pcnt) {
511 case vpc_empty:
512 s = str_dup("Empty");
513 if (s == NULL)
514 return ENOMEM;
515 break;
516 case vpc_fs:
517 rc = vol_fstype_format(fstype, &s);
518 if (rc != EOK)
519 return ENOMEM;
520 break;
521 case vpc_unknown:
522 s = str_dup("Unknown");
523 if (s == NULL)
524 return ENOMEM;
525 break;
526 }
527
528 assert(s != NULL);
529 *rstr = s;
530 return EOK;
531}
532
533/** Validate mount point.
534 *
535 * Verify that mount point is valid. A valid mount point is
536 * one of:
537 * - 'Auto'
538 * - 'None'
539 * - /path (string beginning with '/') to an existing directory
540 *
541 * @return EOK if mount point is in valid, EINVAL if the format is invalid,
542 * ENOENT if the directory does not exist
543 */
544errno_t vol_mountp_validate(const char *mountp)
545{
546 errno_t rc;
547 vfs_stat_t stat;
548
549 if (str_cmp(mountp, "Auto") == 0 || str_cmp(mountp, "auto") == 0)
550 return EOK;
551
552 if (str_casecmp(mountp, "None") == 0 || str_cmp(mountp, "none") == 0)
553 return EOK;
554
555 if (mountp[0] == '/') {
556 rc = vfs_stat_path(mountp, &stat);
557 if (rc != EOK || !stat.is_directory)
558 return ENOENT;
559
560 return EOK;
561 }
562
563 return EINVAL;
564}
565
566/** Get list of volumes as array of volume IDs.
567 *
568 * @param vol Volume service
569 * @param data Place to store pointer to array
570 * @param count Place to store length of array (number of entries)
571 *
572 * @return EOK on success or an error code
573 */
574errno_t vol_get_volumes(vol_t *vol, volume_id_t **data, size_t *count)
575{
576 return vol_get_ids_internal(vol, VOL_GET_VOLUMES, 0,
577 (sysarg_t **) data, count);
578}
579
580/** Get volume configuration information.
581 *
582 * @param vol Volume service
583 * @param vid Volume ID
584 * @param vinfo Place to store volume configuration information
585 * @return EOK on success or an error code
586 */
587errno_t vol_info(vol_t *vol, volume_id_t vid, vol_info_t *vinfo)
588{
589 async_exch_t *exch;
590 errno_t retval;
591 ipc_call_t answer;
592
593 exch = async_exchange_begin(vol->sess);
594 aid_t req = async_send_1(exch, VOL_INFO, vid.id, &answer);
595
596 errno_t rc = async_data_read_start(exch, vinfo, sizeof(vol_info_t));
597 async_exchange_end(exch);
598 if (rc != EOK) {
599 async_forget(req);
600 return EIO;
601 }
602
603 async_wait_for(req, &retval);
604 if (retval != EOK)
605 return EIO;
606
607 return EOK;
608}
609
610/** Find volume by current mount point.
611 *
612 * @param vol Volume service
613 * @param mp Mount point
614 * @param rid Place to store partition service ID
615 * @return EOK on success or an error code
616 */
617errno_t vol_part_by_mp(vol_t *vol, const char *mp, service_id_t *rid)
618{
619 vol_part_info_t vinfo;
620 service_id_t *part_ids = NULL;
621 char *canon_mp_buf = NULL;
622 char *canon_mp;
623 size_t nparts;
624 size_t i;
625 errno_t rc;
626
627 canon_mp_buf = str_dup(mp);
628 if (canon_mp_buf == NULL) {
629 rc = ENOMEM;
630 goto out;
631 }
632
633 canon_mp = vfs_absolutize(canon_mp_buf, NULL);
634 if (canon_mp == NULL) {
635 rc = EINVAL;
636 goto out;
637 }
638
639 rc = vol_get_parts(vol, &part_ids, &nparts);
640 if (rc != EOK)
641 goto out;
642
643 for (i = 0; i < nparts; i++) {
644 rc = vol_part_info(vol, part_ids[i], &vinfo);
645 if (rc != EOK) {
646 rc = EIO;
647 goto out;
648 }
649
650 if (str_cmp(vinfo.cur_mp, canon_mp) == 0) {
651 *rid = part_ids[i];
652 rc = EOK;
653 goto out;
654 }
655 }
656
657 rc = ENOENT;
658out:
659 free(part_ids);
660 free(canon_mp_buf);
661 return rc;
662}
663
664/** @}
665 */
Note: See TracBrowser for help on using the repository browser.