hbench: add options to set duration and run count
[helenos.git] / uspace / srv / volsrv / part.c
blob5eb62f6eaeebfd8bb194dacdf85925a2c11026cc
1 /*
2 * Copyright (c) 2015 Jiri Svoboda
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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
30 * @{
32 /**
33 * @file Partition handling
34 * @brief
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>
49 #include "empty.h"
50 #include "mkfs.h"
51 #include "part.h"
52 #include "types/part.h"
53 #include "volume.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,
58 vol_part_t **);
60 struct fsname_type {
61 const char *name;
62 vol_fstype_t fstype;
65 static 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 }
74 static const char *fstype_str(vol_fstype_t fstype)
76 struct fsname_type *fst;
78 fst = &fstab[0];
79 while (fst->name != NULL) {
80 if (fst->fstype == fstype)
81 return fst->name;
82 ++fst;
85 assert(false);
86 return NULL;
89 /** Check for new and removed partitions */
90 static errno_t vol_part_check_new(vol_parts_t *parts)
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;
101 fibril_mutex_lock(&parts->lock);
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;
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;
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;
126 break;
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.");
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]) {
151 still_exists = true;
152 break;
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);
162 cur = next;
165 free(svcs);
167 fibril_mutex_unlock(&parts->lock);
168 return EOK;
171 static vol_part_t *vol_part_new(void)
173 vol_part_t *part = calloc(1, sizeof(vol_part_t));
175 if (part == NULL) {
176 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed allocating partition "
177 "structure. Out of memory.");
178 return NULL;
181 refcount_init(&part->refcnt);
182 link_initialize(&part->lparts);
183 part->parts = NULL;
184 part->pcnt = vpc_empty;
186 return part;
189 static void vol_part_delete(vol_part_t *part)
191 log_msg(LOG_DEFAULT, LVL_ERROR, "Freeing partition %p", part);
192 if (part == NULL)
193 return;
195 if (part->volume != NULL)
196 vol_volume_del_ref(part->volume);
198 free(part->cur_mp);
199 free(part->svc_name);
200 free(part);
203 static errno_t vol_part_probe(vol_part_t *part)
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;
212 log_msg(LOG_DEFAULT, LVL_NOTE, "Probe partition %s", part->svc_name);
214 assert(fibril_mutex_is_locked(&part->parts->lock));
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;
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;
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.");
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;
248 label = str_dup("");
249 if (label == NULL) {
250 rc = ENOMEM;
251 goto error;
254 part->pcnt = empty ? vpc_empty : vpc_unknown;
255 part->label = label;
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;
263 part->volume = volume;
264 return EOK;
266 error:
267 return rc;
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)
279 return true;
281 /* For other file systems disallow mounting from ATA hard drive */
282 if (str_str(part->svc_name, "\\ata-c") != NULL)
283 return false;
285 /* Allow otherwise (e.g. USB mass storage) */
286 return true;
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,
309 bool *rauto)
311 const char *cfg_mp;
312 char *mp;
313 bool mp_auto;
314 int err;
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'",
320 cfg_mp);
321 } else {
322 cfg_mp = vol_part_def_mountp(part);
323 log_msg(LOG_DEFAULT, LVL_NOTE, "Default mount point '%s'",
324 cfg_mp);
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 */
331 *rpath = NULL;
332 *rauto = false;
333 return EOK;
336 log_msg(LOG_DEFAULT, LVL_NOTE, "Determine MP label='%s'", part->label);
337 err = asprintf(&mp, "/vol/%s", part->label);
338 if (err < 0) {
339 log_msg(LOG_DEFAULT, LVL_ERROR, "Out of memory");
340 return ENOMEM;
343 mp_auto = true;
344 } else if (str_cmp(cfg_mp, "None") == 0 || str_cmp(cfg_mp, "none") == 0) {
345 mp = NULL;
346 mp_auto = false;
347 } else {
348 mp = str_dup(cfg_mp);
349 mp_auto = false;
352 *rpath = mp;
353 *rauto = mp_auto;
354 return EOK;
357 /** Mount partition.
359 * @param part Partition
361 static errno_t vol_part_mount(vol_part_t *part)
363 char *mp;
364 bool mp_auto;
365 errno_t rc;
367 rc = vol_part_determine_mount_path(part, &mp, &mp_auto);
368 if (rc != EOK) {
369 log_msg(LOG_DEFAULT, LVL_ERROR, "Error determining mount point.\n");
370 return rc;
373 if (mp == NULL) {
374 log_msg(LOG_DEFAULT, LVL_NOTE, "Not mounting volume.");
375 return EOK;
378 if (mp_auto) {
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);
382 if (rc != EOK) {
383 log_msg(LOG_DEFAULT, LVL_ERROR, "Error creating mount point '%s'",
384 mp);
385 free(mp);
386 return EIO;
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);
394 if (rc != EOK) {
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);
399 part->cur_mp = mp;
400 part->cur_mp_auto = mp_auto;
402 return rc;
405 static errno_t vol_part_add_locked(vol_parts_t *parts, service_id_t sid)
407 vol_part_t *part;
408 errno_t rc;
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);
415 if (rc == EOK) {
416 vol_part_del_ref(part);
417 return EEXIST;
420 log_msg(LOG_DEFAULT, LVL_NOTE, "partition %zu is new", sid);
422 part = vol_part_new();
423 if (part == NULL)
424 return ENOMEM;
426 part->svc_id = sid;
427 part->parts = parts;
429 rc = loc_service_get_name(sid, &part->svc_name);
430 if (rc != EOK) {
431 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed getting service name.");
432 goto error;
435 rc = vol_part_probe(part);
436 if (rc != EOK)
437 goto error;
439 rc = vol_part_mount(part);
440 if (rc != EOK)
441 goto error;
443 list_append(&part->lparts, &parts->parts);
445 log_msg(LOG_DEFAULT, LVL_NOTE, "Added partition %zu", part->svc_id);
447 return EOK;
449 error:
450 vol_part_delete(part);
451 return rc;
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)",
458 part->svc_id);
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)
468 errno_t rc;
470 fibril_mutex_lock(&parts->lock);
471 rc = vol_part_add_locked(parts, sid);
472 fibril_mutex_unlock(&parts->lock);
474 return rc;
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)
486 vol_parts_t *parts;
488 parts = calloc(1, sizeof(vol_parts_t));
489 if (parts == NULL)
490 return ENOMEM;
492 fibril_mutex_initialize(&parts->lock);
493 list_initialize(&parts->parts);
494 parts->volumes = volumes;
496 *rparts = parts;
497 return EOK;
500 void vol_parts_destroy(vol_parts_t *parts)
502 if (parts == NULL)
503 return;
505 assert(list_empty(&parts->parts));
506 free(parts);
509 errno_t vol_part_discovery_start(vol_parts_t *parts)
511 errno_t rc;
513 rc = loc_register_cat_change_cb(vol_part_cat_change_cb, parts);
514 if (rc != EOK) {
515 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering callback "
516 "for partition discovery: %s.", str_error(rc));
517 return 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)
527 size_t act_cnt;
528 size_t buf_cnt;
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);
539 return EINVAL;
542 size_t pos = 0;
543 list_foreach(parts->parts, lparts, vol_part_t, part) {
544 if (pos < buf_cnt)
545 id_buf[pos] = part->svc_id;
546 pos++;
549 fibril_mutex_unlock(&parts->lock);
550 return EOK;
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) {
560 /* Add reference */
561 refcount_up(&part->refcnt);
562 *rpart = part;
563 return EOK;
567 return ENOENT;
570 errno_t vol_part_find_by_id_ref(vol_parts_t *parts, service_id_t sid,
571 vol_part_t **rpart)
573 errno_t rc;
575 fibril_mutex_lock(&parts->lock);
576 rc = vol_part_find_by_id_ref_locked(parts, sid, rpart);
577 fibril_mutex_unlock(&parts->lock);
579 return rc;
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,
590 vol_part_t **rpart)
592 errno_t rc;
593 char *mpath;
594 bool mauto;
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);
600 if (rc != EOK) {
601 fibril_mutex_unlock(&parts->lock);
602 return ENOMEM;
605 if (mpath != NULL && str_cmp(mpath, path) == 0) {
606 /* Add reference */
607 refcount_up(&part->refcnt);
608 fibril_mutex_unlock(&parts->lock);
609 free(mpath);
610 *rpart = part;
611 return EOK;
614 if (mpath != NULL)
615 free(mpath);
618 fibril_mutex_unlock(&parts->lock);
619 return ENOENT;
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)
630 int rc;
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 "
636 "partition.");
637 return EINVAL;
640 rc = vfs_unmount_path(part->cur_mp);
641 if (rc != EOK) {
642 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unmounting partition "
643 "from %s", part->cur_mp);
644 return rc;
647 if (part->cur_mp_auto) {
648 rc = vfs_unlink_path(part->cur_mp);
649 if (rc != EOK) {
650 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed deleting "
651 "mount directory %s.", part->cur_mp);
655 free(part->cur_mp);
656 part->cur_mp = NULL;
657 part->cur_mp_auto = false;
659 return EOK;
662 errno_t vol_part_empty_part(vol_part_t *part)
664 errno_t rc;
666 log_msg(LOG_DEFAULT, LVL_DEBUG, "vol_part_empty_part()");
668 rc = volsrv_part_empty(part->svc_id);
669 if (rc != EOK) {
670 log_msg(LOG_DEFAULT, LVL_DEBUG, "vol_part_empty_part() - failed %s",
671 str_error(rc));
672 return rc;
675 part->pcnt = vpc_empty;
676 return EOK;
679 /** Insert volume.
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)
687 int rc;
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);
695 return EOK;
698 rc = vol_part_probe(part);
699 if (rc != EOK)
700 goto error;
702 rc = vol_part_mount(part);
703 if (rc != EOK)
704 goto error;
706 fibril_mutex_unlock(&part->parts->lock);
708 return EOK;
709 error:
710 return rc;
713 /** Set mount point.
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)
723 errno_t rc;
724 const char *def_mp;
725 const char *mp;
727 rc = vol_mountp_validate(mountp);
728 if (rc != EOK)
729 return rc;
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)
735 mp = "";
736 else
737 mp = mountp;
739 rc = vol_volume_set_mountp(part->volume, mp);
740 if (rc != EOK)
741 return rc;
743 return EOK;
746 errno_t vol_part_mkfs_part(vol_part_t *part, vol_fstype_t fstype,
747 const char *label, const char *mountp)
749 errno_t rc;
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);
756 if (rc != EOK) {
757 log_msg(LOG_DEFAULT, LVL_DEBUG, "vol_part_mkfs_part() - failed %s",
758 str_error(rc));
759 fibril_mutex_unlock(&part->parts->lock);
760 return rc;
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
766 * uppercase).
768 rc = vol_part_probe(part);
769 if (rc != EOK) {
770 fibril_mutex_unlock(&part->parts->lock);
771 return rc;
774 rc = vol_part_mountp_set(part, mountp);
775 if (rc != EOK) {
776 fibril_mutex_unlock(&part->parts->lock);
777 return rc;
780 rc = vol_part_mount(part);
781 if (rc != EOK) {
782 fibril_mutex_unlock(&part->parts->lock);
783 return rc;
786 fibril_mutex_unlock(&part->parts->lock);
787 return EOK;
790 /** Set partition mount point.
792 * Set the partition mount point, (un-, re-)mounting the partition as necessary.
794 * @param part Partition
795 * @param mountp
797 * @return EOK on success or error code
799 errno_t vol_part_set_mountp_part(vol_part_t *part, const char *mountp)
801 errno_t rc;
803 if (part->cur_mp != NULL) {
804 rc = vol_part_eject_part(part);
805 if (rc != EOK)
806 return rc;
809 rc = vol_part_mountp_set(part, mountp);
810 if (rc != EOK)
811 return rc;
813 rc = vol_part_mount(part);
814 if (rc != EOK)
815 return rc;
817 return EOK;
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;
830 return EOK;
833 /** @}