2 * Copyright (c) 2015 Jiri Svoboda
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
29 /** @addtogroup volsrv
33 * @file Partition handling
39 #include <fibril_synch.h>
45 #include <str_error.h>
52 #include "types/part.h"
55 static errno_t
vol_part_add_locked(vol_parts_t
*, service_id_t
);
56 static void vol_part_remove_locked(vol_part_t
*);
57 static errno_t
vol_part_find_by_id_ref_locked(vol_parts_t
*, service_id_t
,
65 static struct fsname_type fstab
[] = {
66 { "ext4fs", fs_ext4
},
68 { "exfat", fs_exfat
},
74 static const char *fstype_str(vol_fstype_t fstype
)
76 struct fsname_type
*fst
;
79 while (fst
->name
!= NULL
) {
80 if (fst
->fstype
== fstype
)
89 /** Check for new and removed partitions */
90 static errno_t
vol_part_check_new(vol_parts_t
*parts
)
94 category_id_t part_cat
;
101 fibril_mutex_lock(&parts
->lock
);
103 rc
= loc_category_get_id("partition", &part_cat
, IPC_FLAG_BLOCKING
);
105 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed resolving category 'partition'.");
106 fibril_mutex_unlock(&parts
->lock
);
110 rc
= loc_category_get_svcs(part_cat
, &svcs
, &count
);
112 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed getting list of partition "
114 fibril_mutex_unlock(&parts
->lock
);
118 /* Check for new partitions */
119 for (i
= 0; i
< count
; i
++) {
120 already_known
= false;
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;
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
]);
135 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Could not add "
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
);
147 still_exists
= false;
148 // XXX Make this faster
149 for (i
= 0; i
< count
; i
++) {
150 if (part
->svc_id
== svcs
[i
]) {
157 log_msg(LOG_DEFAULT
, LVL_NOTE
, "Partition '%zu' is gone",
159 vol_part_remove_locked(part
);
167 fibril_mutex_unlock(&parts
->lock
);
171 static vol_part_t
*vol_part_new(void)
173 vol_part_t
*part
= calloc(1, sizeof(vol_part_t
));
176 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed allocating partition "
177 "structure. Out of memory.");
181 refcount_init(&part
->refcnt
);
182 link_initialize(&part
->lparts
);
184 part
->pcnt
= vpc_empty
;
189 static void vol_part_delete(vol_part_t
*part
)
191 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Freeing partition %p", part
);
195 if (part
->volume
!= NULL
)
196 vol_volume_del_ref(part
->volume
);
199 free(part
->svc_name
);
203 static errno_t
vol_part_probe(vol_part_t
*part
)
206 vfs_fs_probe_info_t info
;
207 struct fsname_type
*fst
;
209 vol_volume_t
*volume
;
212 log_msg(LOG_DEFAULT
, LVL_NOTE
, "Probe partition %s", part
->svc_name
);
214 assert(fibril_mutex_is_locked(&part
->parts
->lock
));
217 while (fst
->name
!= NULL
) {
218 rc
= vfs_fsprobe(fst
->name
, part
->svc_id
, &info
);
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
);
234 part
->fstype
= fst
->fstype
;
237 log_msg(LOG_DEFAULT
, LVL_NOTE
, "Partition does not contain "
238 "a recognized file system.");
240 rc
= volsrv_part_is_empty(part
->svc_id
, &empty
);
242 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed determining if "
243 "partition is empty.");
254 part
->pcnt
= empty
? vpc_empty
: vpc_unknown
;
258 /* Look up new or existing volume. */
259 rc
= vol_volume_lookup_ref(part
->parts
->volumes
, part
->label
, &volume
);
263 part
->volume
= volume
;
270 /** Determine if partition is allowed to be mounted by default.
272 * @param part Partition
273 * @return @c true iff partition is allowed to be mounted by default
275 static bool vol_part_allow_mount_by_def(vol_part_t
*part
)
277 /* CDFS is safe to mount (after all, it is read-only) */
278 if (part
->pcnt
== vpc_fs
&& part
->fstype
== fs_cdfs
)
281 /* For other file systems disallow mounting from ATA hard drive */
282 if (str_str(part
->svc_name
, "\\ata-c") != NULL
)
285 /* Allow otherwise (e.g. USB mass storage) */
289 /** Determine the default mount point for a partition.
291 * @param part Partition
292 * @return Pointer to the constant string "Auto" or "None"
294 static const char *vol_part_def_mountp(vol_part_t
*part
)
296 return vol_part_allow_mount_by_def(part
) ? "Auto" : "None";
299 /** Determine actual mount path to be used for a partition.
301 * @param part Partition
302 * @param rpath Place to store pointer to newly allocated string or @c NULL
303 * if volume should not be mounted
304 * @param rauto Place to store boolean flag whether mount point is automatic
306 * @return EOK on success, ENOMEM if out of memory
308 static errno_t
vol_part_determine_mount_path(vol_part_t
*part
, char **rpath
,
316 /* Get configured mount point */
317 if (str_size(part
->volume
->mountp
) > 0) {
318 cfg_mp
= part
->volume
->mountp
;
319 log_msg(LOG_DEFAULT
, LVL_NOTE
, "Configured mount point '%s'",
322 cfg_mp
= vol_part_def_mountp(part
);
323 log_msg(LOG_DEFAULT
, LVL_NOTE
, "Default mount point '%s'",
327 if (str_cmp(cfg_mp
, "Auto") == 0 || str_cmp(cfg_mp
, "auto") == 0) {
329 if (str_size(part
->label
) < 1) {
330 /* Don't mount nameless volumes */
336 log_msg(LOG_DEFAULT
, LVL_NOTE
, "Determine MP label='%s'", part
->label
);
337 err
= asprintf(&mp
, "/vol/%s", part
->label
);
339 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Out of memory");
344 } else if (str_cmp(cfg_mp
, "None") == 0 || str_cmp(cfg_mp
, "none") == 0) {
348 mp
= str_dup(cfg_mp
);
359 * @param part Partition
361 static errno_t
vol_part_mount(vol_part_t
*part
)
367 rc
= vol_part_determine_mount_path(part
, &mp
, &mp_auto
);
369 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Error determining mount point.\n");
374 log_msg(LOG_DEFAULT
, LVL_NOTE
, "Not mounting volume.");
379 /* Create directory for automatic mount point */
380 log_msg(LOG_DEFAULT
, LVL_NOTE
, "Create mount point '%s'", mp
);
381 rc
= vfs_link_path(mp
, KIND_DIRECTORY
, NULL
);
383 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Error creating mount point '%s'",
390 log_msg(LOG_DEFAULT
, LVL_NOTE
, "Call vfs_mount_path mp='%s' fstype='%s' svc_name='%s'",
391 mp
, fstype_str(part
->fstype
), part
->svc_name
);
392 rc
= vfs_mount_path(mp
, fstype_str(part
->fstype
),
393 part
->svc_name
, "", 0, 0);
395 log_msg(LOG_DEFAULT
, LVL_NOTE
, "Failed mounting to %s", mp
);
397 log_msg(LOG_DEFAULT
, LVL_NOTE
, "Mount to %s -> %d\n", mp
, rc
);
400 part
->cur_mp_auto
= mp_auto
;
405 static errno_t
vol_part_add_locked(vol_parts_t
*parts
, service_id_t sid
)
410 assert(fibril_mutex_is_locked(&parts
->lock
));
411 log_msg(LOG_DEFAULT
, LVL_NOTE
, "vol_part_add_locked(%zu)", sid
);
413 /* Check for duplicates */
414 rc
= vol_part_find_by_id_ref_locked(parts
, sid
, &part
);
416 vol_part_del_ref(part
);
420 log_msg(LOG_DEFAULT
, LVL_NOTE
, "partition %zu is new", sid
);
422 part
= vol_part_new();
429 rc
= loc_service_get_name(sid
, &part
->svc_name
);
431 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed getting service name.");
435 rc
= vol_part_probe(part
);
439 rc
= vol_part_mount(part
);
443 list_append(&part
->lparts
, &parts
->parts
);
445 log_msg(LOG_DEFAULT
, LVL_NOTE
, "Added partition %zu", part
->svc_id
);
450 vol_part_delete(part
);
454 static void vol_part_remove_locked(vol_part_t
*part
)
456 assert(fibril_mutex_is_locked(&part
->parts
->lock
));
457 log_msg(LOG_DEFAULT
, LVL_NOTE
, "vol_part_remove_locked(%zu)",
460 list_remove(&part
->lparts
);
462 log_msg(LOG_DEFAULT
, LVL_NOTE
, "Removed partition.");
463 vol_part_del_ref(part
);
466 errno_t
vol_part_add_part(vol_parts_t
*parts
, service_id_t sid
)
470 fibril_mutex_lock(&parts
->lock
);
471 rc
= vol_part_add_locked(parts
, sid
);
472 fibril_mutex_unlock(&parts
->lock
);
477 static void vol_part_cat_change_cb(void *arg
)
479 vol_parts_t
*parts
= (vol_parts_t
*) arg
;
481 (void) vol_part_check_new(parts
);
484 errno_t
vol_parts_create(vol_volumes_t
*volumes
, vol_parts_t
**rparts
)
488 parts
= calloc(1, sizeof(vol_parts_t
));
492 fibril_mutex_initialize(&parts
->lock
);
493 list_initialize(&parts
->parts
);
494 parts
->volumes
= volumes
;
500 void vol_parts_destroy(vol_parts_t
*parts
)
505 assert(list_empty(&parts
->parts
));
509 errno_t
vol_part_discovery_start(vol_parts_t
*parts
)
513 rc
= loc_register_cat_change_cb(vol_part_cat_change_cb
, parts
);
515 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed registering callback "
516 "for partition discovery: %s.", str_error(rc
));
520 return vol_part_check_new(parts
);
523 /** Get list of partitions as array of service IDs. */
524 errno_t
vol_part_get_ids(vol_parts_t
*parts
, service_id_t
*id_buf
,
525 size_t buf_size
, size_t *act_size
)
530 fibril_mutex_lock(&parts
->lock
);
532 buf_cnt
= buf_size
/ sizeof(service_id_t
);
534 act_cnt
= list_count(&parts
->parts
);
535 *act_size
= act_cnt
* sizeof(service_id_t
);
537 if (buf_size
% sizeof(service_id_t
) != 0) {
538 fibril_mutex_unlock(&parts
->lock
);
543 list_foreach(parts
->parts
, lparts
, vol_part_t
, part
) {
545 id_buf
[pos
] = part
->svc_id
;
549 fibril_mutex_unlock(&parts
->lock
);
553 static errno_t
vol_part_find_by_id_ref_locked(vol_parts_t
*parts
,
554 service_id_t sid
, vol_part_t
**rpart
)
556 assert(fibril_mutex_is_locked(&parts
->lock
));
558 list_foreach(parts
->parts
, lparts
, vol_part_t
, part
) {
559 if (part
->svc_id
== sid
) {
561 refcount_up(&part
->refcnt
);
570 errno_t
vol_part_find_by_id_ref(vol_parts_t
*parts
, service_id_t sid
,
575 fibril_mutex_lock(&parts
->lock
);
576 rc
= vol_part_find_by_id_ref_locked(parts
, sid
, rpart
);
577 fibril_mutex_unlock(&parts
->lock
);
582 /** Find partition by filesystem path.
584 * @param parts Partitions
585 * @param path Filesystem path
586 * @param rpart Place to store pointer to partition
587 * @return EOK on success, ENOENT if not found, ENOMEM if out of memory
589 errno_t
vol_part_find_by_path_ref(vol_parts_t
*parts
, const char *path
,
596 fibril_mutex_lock(&parts
->lock
);
598 list_foreach(parts
->parts
, lparts
, vol_part_t
, part
) {
599 rc
= vol_part_determine_mount_path(part
, &mpath
, &mauto
);
601 fibril_mutex_unlock(&parts
->lock
);
605 if (mpath
!= NULL
&& str_cmp(mpath
, path
) == 0) {
607 refcount_up(&part
->refcnt
);
608 fibril_mutex_unlock(&parts
->lock
);
618 fibril_mutex_unlock(&parts
->lock
);
622 void vol_part_del_ref(vol_part_t
*part
)
624 if (refcount_down(&part
->refcnt
))
625 vol_part_delete(part
);
628 errno_t
vol_part_eject_part(vol_part_t
*part
)
632 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "vol_part_eject_part()");
634 if (part
->cur_mp
== NULL
) {
635 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "Attempt to mount unmounted "
640 rc
= vfs_unmount_path(part
->cur_mp
);
642 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed unmounting partition "
643 "from %s", part
->cur_mp
);
647 if (part
->cur_mp_auto
) {
648 rc
= vfs_unlink_path(part
->cur_mp
);
650 log_msg(LOG_DEFAULT
, LVL_ERROR
, "Failed deleting "
651 "mount directory %s.", part
->cur_mp
);
657 part
->cur_mp_auto
= false;
662 errno_t
vol_part_empty_part(vol_part_t
*part
)
666 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "vol_part_empty_part()");
668 rc
= volsrv_part_empty(part
->svc_id
);
670 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "vol_part_empty_part() - failed %s",
675 part
->pcnt
= vpc_empty
;
681 * Re-mount the volume in the partition, if applicable.
683 * @param part Partition
685 errno_t
vol_part_insert_part(vol_part_t
*part
)
689 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "vol_part_insert_part()");
691 fibril_mutex_lock(&part
->parts
->lock
);
693 if (part
->cur_mp
!= NULL
) {
694 fibril_mutex_unlock(&part
->parts
->lock
);
698 rc
= vol_part_probe(part
);
702 rc
= vol_part_mount(part
);
706 fibril_mutex_unlock(&part
->parts
->lock
);
715 * Verify and set a mount point. If the value of the mount point is
716 * the same as the default value, we will actually unset the mount point
717 * value (therefore effectively changing it to use the default).
719 * @return EOK on success, error code otherwise
721 static errno_t
vol_part_mountp_set(vol_part_t
*part
, const char *mountp
)
727 rc
= vol_mountp_validate(mountp
);
731 def_mp
= vol_part_def_mountp(part
);
733 /* If the value is the same as default, set to empty string. */
734 if (str_cmp(def_mp
, mountp
) == 0)
739 rc
= vol_volume_set_mountp(part
->volume
, mp
);
746 errno_t
vol_part_mkfs_part(vol_part_t
*part
, vol_fstype_t fstype
,
747 const char *label
, const char *mountp
)
751 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "vol_part_mkfs_part()");
753 fibril_mutex_lock(&part
->parts
->lock
);
755 rc
= volsrv_part_mkfs(part
->svc_id
, fstype
, label
);
757 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "vol_part_mkfs_part() - failed %s",
759 fibril_mutex_unlock(&part
->parts
->lock
);
764 * Re-probe the partition to update information. This is needed since
765 * the FS can make conversions of the volume label (e.g. make it
768 rc
= vol_part_probe(part
);
770 fibril_mutex_unlock(&part
->parts
->lock
);
774 rc
= vol_part_mountp_set(part
, mountp
);
776 fibril_mutex_unlock(&part
->parts
->lock
);
780 rc
= vol_part_mount(part
);
782 fibril_mutex_unlock(&part
->parts
->lock
);
786 fibril_mutex_unlock(&part
->parts
->lock
);
790 /** Set partition mount point.
792 * Set the partition mount point, (un-, re-)mounting the partition as necessary.
794 * @param part Partition
797 * @return EOK on success or error code
799 errno_t
vol_part_set_mountp_part(vol_part_t
*part
, const char *mountp
)
803 if (part
->cur_mp
!= NULL
) {
804 rc
= vol_part_eject_part(part
);
809 rc
= vol_part_mountp_set(part
, mountp
);
813 rc
= vol_part_mount(part
);
820 errno_t
vol_part_get_info(vol_part_t
*part
, vol_part_info_t
*pinfo
)
822 memset(pinfo
, 0, sizeof(*pinfo
));
824 pinfo
->pcnt
= part
->pcnt
;
825 pinfo
->fstype
= part
->fstype
;
826 str_cpy(pinfo
->label
, sizeof(pinfo
->label
), part
->label
);
827 if (part
->cur_mp
!= NULL
)
828 str_cpy(pinfo
->cur_mp
, sizeof(pinfo
->cur_mp
), part
->cur_mp
);
829 pinfo
->cur_mp_auto
= part
->cur_mp_auto
;