source: mainline/uspace/srv/volsrv/part.c@ 2d78d88

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 2d78d88 was 2d78d88, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Modifying mount point for a partition.

  • Property mode set to 100644
File size: 16.1 KB
Line 
1/*
2 * Copyright (c) 2015 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 volsrv
30 * @{
31 */
32/**
33 * @file Partition handling
34 * @brief
35 */
36
37#include <adt/list.h>
38#include <errno.h>
39#include <fibril_synch.h>
40#include <io/log.h>
41#include <loc.h>
42#include <stdbool.h>
43#include <stdlib.h>
44#include <str.h>
45#include <str_error.h>
46#include <vfs/vfs.h>
47#include <vol.h>
48
49#include "empty.h"
50#include "mkfs.h"
51#include "part.h"
52#include "types/part.h"
53#include "volume.h"
54
55static errno_t vol_part_add_locked(vol_parts_t *, service_id_t);
56static void vol_part_remove_locked(vol_part_t *);
57static errno_t vol_part_find_by_id_ref_locked(vol_parts_t *, service_id_t,
58 vol_part_t **);
59
60struct fsname_type {
61 const char *name;
62 vol_fstype_t fstype;
63};
64
65static struct fsname_type fstab[] = {
66 { "ext4fs", fs_ext4 },
67 { "cdfs", fs_cdfs },
68 { "exfat", fs_exfat },
69 { "fat", fs_fat },
70 { "mfs", fs_minix },
71 { NULL, 0 }
72};
73
74static const char *fstype_str(vol_fstype_t fstype)
75{
76 struct fsname_type *fst;
77
78 fst = &fstab[0];
79 while (fst->name != NULL) {
80 if (fst->fstype == fstype)
81 return fst->name;
82 ++fst;
83 }
84
85 assert(false);
86 return NULL;
87}
88
89/** Check for new and removed partitions */
90static errno_t vol_part_check_new(vol_parts_t *parts)
91{
92 bool already_known;
93 bool still_exists;
94 category_id_t part_cat;
95 service_id_t *svcs;
96 size_t count, i;
97 link_t *cur, *next;
98 vol_part_t *part;
99 errno_t rc;
100
101 fibril_mutex_lock(&parts->lock);
102
103 rc = loc_category_get_id("partition", &part_cat, IPC_FLAG_BLOCKING);
104 if (rc != EOK) {
105 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed resolving category 'partition'.");
106 fibril_mutex_unlock(&parts->lock);
107 return ENOENT;
108 }
109
110 rc = loc_category_get_svcs(part_cat, &svcs, &count);
111 if (rc != EOK) {
112 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed getting list of partition "
113 "devices.");
114 fibril_mutex_unlock(&parts->lock);
115 return EIO;
116 }
117
118 /* Check for new partitions */
119 for (i = 0; i < count; i++) {
120 already_known = false;
121
122 // XXX Make this faster
123 list_foreach(parts->parts, lparts, vol_part_t, part) {
124 if (part->svc_id == svcs[i]) {
125 already_known = true;
126 break;
127 }
128 }
129
130 if (!already_known) {
131 log_msg(LOG_DEFAULT, LVL_NOTE, "Found partition '%lu'",
132 (unsigned long) svcs[i]);
133 rc = vol_part_add_locked(parts, svcs[i]);
134 if (rc != EOK) {
135 log_msg(LOG_DEFAULT, LVL_ERROR, "Could not add "
136 "partition.");
137 }
138 }
139 }
140
141 /* Check for removed partitions */
142 cur = list_first(&parts->parts);
143 while (cur != NULL) {
144 next = list_next(cur, &parts->parts);
145 part = list_get_instance(cur, vol_part_t, lparts);
146
147 still_exists = false;
148 // XXX Make this faster
149 for (i = 0; i < count; i++) {
150 if (part->svc_id == svcs[i]) {
151 still_exists = true;
152 break;
153 }
154 }
155
156 if (!still_exists) {
157 log_msg(LOG_DEFAULT, LVL_NOTE, "Partition '%zu' is gone",
158 part->svc_id);
159 vol_part_remove_locked(part);
160 }
161
162 cur = next;
163 }
164
165 free(svcs);
166
167 fibril_mutex_unlock(&parts->lock);
168 return EOK;
169}
170
171static vol_part_t *vol_part_new(void)
172{
173 vol_part_t *part = calloc(1, sizeof(vol_part_t));
174
175 if (part == NULL) {
176 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed allocating partition "
177 "structure. Out of memory.");
178 return NULL;
179 }
180
181 atomic_set(&part->refcnt, 1);
182 link_initialize(&part->lparts);
183 part->parts = NULL;
184 part->pcnt = vpc_empty;
185
186 return part;
187}
188
189static void vol_part_delete(vol_part_t *part)
190{
191 log_msg(LOG_DEFAULT, LVL_ERROR, "Freeing partition %p", part);
192 if (part == NULL)
193 return;
194
195 if (part->volume != NULL)
196 vol_volume_del_ref(part->volume);
197
198 free(part->cur_mp);
199 free(part->svc_name);
200 free(part);
201}
202
203static errno_t vol_part_probe(vol_part_t *part)
204{
205 bool empty;
206 vfs_fs_probe_info_t info;
207 struct fsname_type *fst;
208 char *label;
209 vol_volume_t *volume;
210 errno_t rc;
211
212 log_msg(LOG_DEFAULT, LVL_NOTE, "Probe partition %s", part->svc_name);
213
214 assert(fibril_mutex_is_locked(&part->parts->lock));
215
216 fst = &fstab[0];
217 while (fst->name != NULL) {
218 rc = vfs_fsprobe(fst->name, part->svc_id, &info);
219 if (rc == EOK)
220 break;
221 ++fst;
222 }
223
224 if (fst->name != NULL) {
225 log_msg(LOG_DEFAULT, LVL_NOTE, "Found %s, label '%s'",
226 fst->name, info.label);
227 label = str_dup(info.label);
228 if (label == NULL) {
229 rc = ENOMEM;
230 goto error;
231 }
232
233 part->pcnt = vpc_fs;
234 part->fstype = fst->fstype;
235 part->label = label;
236 } else {
237 log_msg(LOG_DEFAULT, LVL_NOTE, "Partition does not contain "
238 "a recognized file system.");
239
240 rc = volsrv_part_is_empty(part->svc_id, &empty);
241 if (rc != EOK) {
242 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed determining if "
243 "partition is empty.");
244 rc = EIO;
245 goto error;
246 }
247
248 label = str_dup("");
249 if (label == NULL) {
250 rc = ENOMEM;
251 goto error;
252 }
253
254 part->pcnt = empty ? vpc_empty : vpc_unknown;
255 part->label = label;
256 }
257
258 /* Look up new or existing volume. */
259 rc = vol_volume_lookup_ref(part->parts->volumes, part->label, &volume);
260 if (rc != EOK)
261 goto error;
262
263 part->volume = volume;
264 return EOK;
265
266error:
267 return rc;
268}
269
270/** Determine if partition is allowed to be mounted by default.
271 *
272 * @param part Partition
273 * @return @c true iff partition is allowed to be mounted by default
274 */
275static bool vol_part_allow_mount_by_def(vol_part_t *part)
276{
277 /* CDFS is safe to mount (after all, it is read-only) */
278 if (part->pcnt == vpc_fs && part->fstype == fs_cdfs)
279 return true;
280
281 /* For other file systems disallow mounting from ATA hard drive */
282 if (str_str(part->svc_name, "\\ata-c") != NULL)
283 return false;
284
285 /* Allow otherwise (e.g. USB mass storage) */
286 return true;
287}
288
289/** Determine the default mount point for a partition.
290 *
291 * @param part Partition
292 * @return Pointer to the constant string "Auto" or "None"
293 */
294static const char *vol_part_def_mountp(vol_part_t *part)
295{
296 return vol_part_allow_mount_by_def(part) ? "Auto" : "None";
297}
298
299/** Mount partition.
300 *
301 * @param part Partition
302 */
303static errno_t vol_part_mount(vol_part_t *part)
304{
305 const char *cfg_mp;
306 char *mp;
307 int err;
308 bool mp_auto;
309 errno_t rc;
310
311 /* Get configured mount point */
312 if (str_size(part->volume->mountp) > 0) {
313 cfg_mp = part->volume->mountp;
314 log_msg(LOG_DEFAULT, LVL_NOTE, "Configured mount point '%s'",
315 cfg_mp);
316 } else {
317 cfg_mp = vol_part_def_mountp(part);
318 log_msg(LOG_DEFAULT, LVL_NOTE, "Default mount point '%s'",
319 cfg_mp);
320 }
321
322 if (str_cmp(cfg_mp, "Auto") == 0 || str_cmp(cfg_mp, "auto") == 0) {
323
324 if (str_size(part->label) < 1) {
325 /* Don't mount nameless volumes */
326 log_msg(LOG_DEFAULT, LVL_NOTE, "Not mounting nameless volume.");
327 return EOK;
328 }
329
330 log_msg(LOG_DEFAULT, LVL_NOTE, "Determine MP label='%s'", part->label);
331 err = asprintf(&mp, "/vol/%s", part->label);
332 if (err < 0) {
333 log_msg(LOG_DEFAULT, LVL_ERROR, "Out of memory");
334 return ENOMEM;
335 }
336
337 log_msg(LOG_DEFAULT, LVL_NOTE, "Create mount point '%s'", mp);
338 rc = vfs_link_path(mp, KIND_DIRECTORY, NULL);
339 if (rc != EOK) {
340 log_msg(LOG_DEFAULT, LVL_ERROR, "Error creating mount point '%s'",
341 mp);
342 free(mp);
343 return EIO;
344 }
345
346 mp_auto = true;
347 } else if (str_cmp(cfg_mp, "None") == 0 || str_cmp(cfg_mp, "none") == 0) {
348 log_msg(LOG_DEFAULT, LVL_NOTE, "Not mounting volume.");
349 return EOK;
350 } else {
351 mp = str_dup(cfg_mp);
352 mp_auto = false;
353 }
354
355 log_msg(LOG_DEFAULT, LVL_NOTE, "Call vfs_mount_path mp='%s' fstype='%s' svc_name='%s'",
356 mp, fstype_str(part->fstype), part->svc_name);
357 rc = vfs_mount_path(mp, fstype_str(part->fstype),
358 part->svc_name, "", 0, 0);
359 if (rc != EOK) {
360 log_msg(LOG_DEFAULT, LVL_NOTE, "Failed mounting to %s", mp);
361 }
362 log_msg(LOG_DEFAULT, LVL_NOTE, "Mount to %s -> %d\n", mp, rc);
363
364 part->cur_mp = mp;
365 part->cur_mp_auto = mp_auto;
366
367 return rc;
368}
369
370static errno_t vol_part_add_locked(vol_parts_t *parts, service_id_t sid)
371{
372 vol_part_t *part;
373 errno_t rc;
374
375 assert(fibril_mutex_is_locked(&parts->lock));
376 log_msg(LOG_DEFAULT, LVL_NOTE, "vol_part_add_locked(%zu)", sid);
377
378 /* Check for duplicates */
379 rc = vol_part_find_by_id_ref_locked(parts, sid, &part);
380 if (rc == EOK) {
381 vol_part_del_ref(part);
382 return EEXIST;
383 }
384
385 log_msg(LOG_DEFAULT, LVL_NOTE, "partition %zu is new", sid);
386
387 part = vol_part_new();
388 if (part == NULL)
389 return ENOMEM;
390
391 part->svc_id = sid;
392 part->parts = parts;
393
394 rc = loc_service_get_name(sid, &part->svc_name);
395 if (rc != EOK) {
396 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed getting service name.");
397 goto error;
398 }
399
400 rc = vol_part_probe(part);
401 if (rc != EOK)
402 goto error;
403
404 rc = vol_part_mount(part);
405 if (rc != EOK)
406 goto error;
407
408 list_append(&part->lparts, &parts->parts);
409
410 log_msg(LOG_DEFAULT, LVL_NOTE, "Added partition %zu", part->svc_id);
411
412 return EOK;
413
414error:
415 vol_part_delete(part);
416 return rc;
417}
418
419static void vol_part_remove_locked(vol_part_t *part)
420{
421 assert(fibril_mutex_is_locked(&part->parts->lock));
422 log_msg(LOG_DEFAULT, LVL_NOTE, "vol_part_remove_locked(%zu)",
423 part->svc_id);
424
425 list_remove(&part->lparts);
426
427 log_msg(LOG_DEFAULT, LVL_NOTE, "Removed partition.");
428 vol_part_del_ref(part);
429}
430
431errno_t vol_part_add_part(vol_parts_t *parts, service_id_t sid)
432{
433 errno_t rc;
434
435 fibril_mutex_lock(&parts->lock);
436 rc = vol_part_add_locked(parts, sid);
437 fibril_mutex_unlock(&parts->lock);
438
439 return rc;
440}
441
442static void vol_part_cat_change_cb(void *arg)
443{
444 vol_parts_t *parts = (vol_parts_t *) arg;
445
446 (void) vol_part_check_new(parts);
447}
448
449errno_t vol_parts_create(vol_volumes_t *volumes, vol_parts_t **rparts)
450{
451 vol_parts_t *parts;
452
453 parts = calloc(1, sizeof(vol_parts_t));
454 if (parts == NULL)
455 return ENOMEM;
456
457 fibril_mutex_initialize(&parts->lock);
458 list_initialize(&parts->parts);
459 parts->volumes = volumes;
460
461 *rparts = parts;
462 return EOK;
463}
464
465void vol_parts_destroy(vol_parts_t *parts)
466{
467 if (parts == NULL)
468 return;
469
470 assert(list_empty(&parts->parts));
471 free(parts);
472}
473
474errno_t vol_part_discovery_start(vol_parts_t *parts)
475{
476 errno_t rc;
477
478 rc = loc_register_cat_change_cb(vol_part_cat_change_cb, parts);
479 if (rc != EOK) {
480 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering callback "
481 "for partition discovery: %s.", str_error(rc));
482 return rc;
483 }
484
485 return vol_part_check_new(parts);
486}
487
488/** Get list of partitions as array of service IDs. */
489errno_t vol_part_get_ids(vol_parts_t *parts, service_id_t *id_buf,
490 size_t buf_size, size_t *act_size)
491{
492 size_t act_cnt;
493 size_t buf_cnt;
494
495 fibril_mutex_lock(&parts->lock);
496
497 buf_cnt = buf_size / sizeof(service_id_t);
498
499 act_cnt = list_count(&parts->parts);
500 *act_size = act_cnt * sizeof(service_id_t);
501
502 if (buf_size % sizeof(service_id_t) != 0) {
503 fibril_mutex_unlock(&parts->lock);
504 return EINVAL;
505 }
506
507 size_t pos = 0;
508 list_foreach(parts->parts, lparts, vol_part_t, part) {
509 if (pos < buf_cnt)
510 id_buf[pos] = part->svc_id;
511 pos++;
512 }
513
514 fibril_mutex_unlock(&parts->lock);
515 return EOK;
516}
517
518static errno_t vol_part_find_by_id_ref_locked(vol_parts_t *parts,
519 service_id_t sid, vol_part_t **rpart)
520{
521 assert(fibril_mutex_is_locked(&parts->lock));
522
523 list_foreach(parts->parts, lparts, vol_part_t, part) {
524 if (part->svc_id == sid) {
525 /* Add reference */
526 atomic_inc(&part->refcnt);
527 *rpart = part;
528 return EOK;
529 }
530 }
531
532 return ENOENT;
533}
534
535errno_t vol_part_find_by_id_ref(vol_parts_t *parts, service_id_t sid,
536 vol_part_t **rpart)
537{
538 errno_t rc;
539
540 fibril_mutex_lock(&parts->lock);
541 rc = vol_part_find_by_id_ref_locked(parts, sid, rpart);
542 fibril_mutex_unlock(&parts->lock);
543
544 return rc;
545}
546
547void vol_part_del_ref(vol_part_t *part)
548{
549 if (atomic_predec(&part->refcnt) == 0)
550 vol_part_delete(part);
551}
552
553errno_t vol_part_eject_part(vol_part_t *part)
554{
555 int rc;
556
557 log_msg(LOG_DEFAULT, LVL_DEBUG, "vol_part_eject_part()");
558
559 if (part->cur_mp == NULL) {
560 log_msg(LOG_DEFAULT, LVL_DEBUG, "Attempt to mount unmounted "
561 "partition.");
562 return EINVAL;
563 }
564
565 rc = vfs_unmount_path(part->cur_mp);
566 if (rc != EOK) {
567 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unmounting partition "
568 "from %s", part->cur_mp);
569 return rc;
570 }
571
572 if (part->cur_mp_auto) {
573 rc = vfs_unlink_path(part->cur_mp);
574 if (rc != EOK) {
575 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed deleting "
576 "mount directory %s.", part->cur_mp);
577 }
578 }
579
580 free(part->cur_mp);
581 part->cur_mp = NULL;
582 part->cur_mp_auto = false;
583
584 return EOK;
585}
586
587errno_t vol_part_empty_part(vol_part_t *part)
588{
589 errno_t rc;
590
591 log_msg(LOG_DEFAULT, LVL_DEBUG, "vol_part_empty_part()");
592
593 rc = volsrv_part_empty(part->svc_id);
594 if (rc != EOK) {
595 log_msg(LOG_DEFAULT, LVL_DEBUG, "vol_part_empty_part() - failed %s",
596 str_error(rc));
597 return rc;
598 }
599
600 part->pcnt = vpc_empty;
601 return EOK;
602}
603
604/** Set mount point.
605 *
606 * Verify and set a mount point. If the value of the mount point is
607 * the same as the default value, we will actually unset the mount point
608 * value (therefore effectively changing it to use the default).
609 *
610 * @return EOK on success, error code otherwise
611 */
612static errno_t vol_part_mountp_set(vol_part_t *part, const char *mountp)
613{
614 errno_t rc;
615 const char *def_mp;
616 const char *mp;
617
618 rc = vol_mountp_validate(mountp);
619 if (rc != EOK)
620 return rc;
621
622 def_mp = vol_part_def_mountp(part);
623
624 /* If the value is the same as default, set to empty string. */
625 if (str_cmp(def_mp, mountp) == 0)
626 mp = "";
627 else
628 mp = mountp;
629
630 rc = vol_volume_set_mountp(part->volume, mp);
631 if (rc != EOK)
632 return rc;
633
634 return EOK;
635}
636
637errno_t vol_part_mkfs_part(vol_part_t *part, vol_fstype_t fstype,
638 const char *label, const char *mountp)
639{
640 errno_t rc;
641
642 log_msg(LOG_DEFAULT, LVL_DEBUG, "vol_part_mkfs_part()");
643
644 fibril_mutex_lock(&part->parts->lock);
645
646 rc = volsrv_part_mkfs(part->svc_id, fstype, label);
647 if (rc != EOK) {
648 log_msg(LOG_DEFAULT, LVL_DEBUG, "vol_part_mkfs_part() - failed %s",
649 str_error(rc));
650 fibril_mutex_unlock(&part->parts->lock);
651 return rc;
652 }
653
654 /*
655 * Re-probe the partition to update information. This is needed since
656 * the FS can make conversions of the volume label (e.g. make it
657 * uppercase).
658 */
659 rc = vol_part_probe(part);
660 if (rc != EOK) {
661 fibril_mutex_unlock(&part->parts->lock);
662 return rc;
663 }
664
665 rc = vol_part_mountp_set(part, mountp);
666 if (rc != EOK) {
667 fibril_mutex_unlock(&part->parts->lock);
668 return rc;
669 }
670
671 rc = vol_part_mount(part);
672 if (rc != EOK) {
673 fibril_mutex_unlock(&part->parts->lock);
674 return rc;
675 }
676
677 fibril_mutex_unlock(&part->parts->lock);
678 return EOK;
679}
680
681/** Set partition mount point.
682 *
683 * Set the partition mount point, (un-, re-)mounting the partition as necessary.
684 *
685 * @param part Partition
686 * @param mountp
687 *
688 * @return EOK on success or error code
689 */
690errno_t vol_part_set_mountp_part(vol_part_t *part, const char *mountp)
691{
692 errno_t rc;
693
694 if (part->cur_mp != NULL) {
695 rc = vol_part_eject_part(part);
696 if (rc != EOK)
697 return rc;
698 }
699
700 rc = vol_part_mountp_set(part, mountp);
701 if (rc != EOK)
702 return rc;
703
704 rc = vol_part_mount(part);
705 if (rc != EOK)
706 return rc;
707
708 return EOK;
709}
710
711errno_t vol_part_get_info(vol_part_t *part, vol_part_info_t *pinfo)
712{
713 memset(pinfo, 0, sizeof(*pinfo));
714
715 pinfo->pcnt = part->pcnt;
716 pinfo->fstype = part->fstype;
717 str_cpy(pinfo->label, sizeof(pinfo->label), part->label);
718 if (part->cur_mp != NULL)
719 str_cpy(pinfo->cur_mp, sizeof(pinfo->cur_mp), part->cur_mp);
720 pinfo->cur_mp_auto = part->cur_mp_auto;
721 return EOK;
722}
723
724/** @}
725 */
Note: See TracBrowser for help on using the repository browser.