source: mainline/uspace/srv/volsrv/part.c@ 40340461

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

Volume server needs to react to partitions being removed.

  • Property mode set to 100644
File size: 12.3 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 <stdbool.h>
38#include <errno.h>
39#include <str_error.h>
40#include <fibril_synch.h>
41#include <io/log.h>
42#include <loc.h>
43#include <stdlib.h>
44#include <str.h>
45#include <vfs/vfs.h>
46
47#include "empty.h"
48#include "mkfs.h"
49#include "part.h"
50#include "types/part.h"
51
52static errno_t vol_part_add_locked(service_id_t);
53static void vol_part_remove_locked(vol_part_t *);
54static errno_t vol_part_find_by_id_ref_locked(service_id_t, vol_part_t **);
55
56static LIST_INITIALIZE(vol_parts); /* of vol_part_t */
57static FIBRIL_MUTEX_INITIALIZE(vol_parts_lock);
58
59struct fsname_type {
60 const char *name;
61 vol_fstype_t fstype;
62};
63
64static struct fsname_type fstab[] = {
65 { "ext4fs", fs_ext4 },
66 { "cdfs", fs_cdfs },
67 { "exfat", fs_exfat },
68 { "fat", fs_fat },
69 { "mfs", fs_minix },
70 { NULL, 0 }
71};
72
73static const char *fstype_str(vol_fstype_t fstype)
74{
75 struct fsname_type *fst;
76
77 fst = &fstab[0];
78 while (fst->name != NULL) {
79 if (fst->fstype == fstype)
80 return fst->name;
81 ++fst;
82 }
83
84 assert(false);
85 return NULL;
86}
87
88/** Check for new and removed partitions */
89static errno_t vol_part_check_new(void)
90{
91 bool already_known;
92 bool still_exists;
93 category_id_t part_cat;
94 service_id_t *svcs;
95 size_t count, i;
96 link_t *cur, *next;
97 vol_part_t *part;
98 errno_t rc;
99
100 fibril_mutex_lock(&vol_parts_lock);
101
102 rc = loc_category_get_id("partition", &part_cat, IPC_FLAG_BLOCKING);
103 if (rc != EOK) {
104 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed resolving category 'partition'.");
105 fibril_mutex_unlock(&vol_parts_lock);
106 return ENOENT;
107 }
108
109 rc = loc_category_get_svcs(part_cat, &svcs, &count);
110 if (rc != EOK) {
111 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed getting list of partition "
112 "devices.");
113 fibril_mutex_unlock(&vol_parts_lock);
114 return EIO;
115 }
116
117 /* Check for new partitions */
118 for (i = 0; i < count; i++) {
119 already_known = false;
120
121 // XXX Make this faster
122 list_foreach(vol_parts, lparts, vol_part_t, part) {
123 if (part->svc_id == svcs[i]) {
124 already_known = true;
125 break;
126 }
127 }
128
129 if (!already_known) {
130 log_msg(LOG_DEFAULT, LVL_NOTE, "Found partition '%lu'",
131 (unsigned long) svcs[i]);
132 rc = vol_part_add_locked(svcs[i]);
133 if (rc != EOK) {
134 log_msg(LOG_DEFAULT, LVL_ERROR, "Could not add "
135 "partition.");
136 }
137 }
138 }
139
140 /* Check for removed partitions */
141 cur = list_first(&vol_parts);
142 while (cur != NULL) {
143 next = list_next(cur, &vol_parts);
144 part = list_get_instance(cur, vol_part_t, lparts);
145
146 still_exists = false;
147 // XXX Make this faster
148 for (i = 0; i < count; i++) {
149 if (part->svc_id == svcs[i]) {
150 still_exists = true;
151 break;
152 }
153 }
154
155 if (!still_exists) {
156 log_msg(LOG_DEFAULT, LVL_NOTE, "Partition '%zu' is gone",
157 part->svc_id);
158 vol_part_remove_locked(part);
159 }
160
161 cur = next;
162 }
163
164 free(svcs);
165
166 fibril_mutex_unlock(&vol_parts_lock);
167 return EOK;
168}
169
170static vol_part_t *vol_part_new(void)
171{
172 vol_part_t *part = calloc(1, sizeof(vol_part_t));
173
174 if (part == NULL) {
175 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed allocating partition "
176 "structure. Out of memory.");
177 return NULL;
178 }
179
180 atomic_set(&part->refcnt, 1);
181 link_initialize(&part->lparts);
182 part->pcnt = vpc_empty;
183
184 return part;
185}
186
187static void vol_part_delete(vol_part_t *part)
188{
189 log_msg(LOG_DEFAULT, LVL_ERROR, "Freeing partition %p", part);
190 if (part == NULL)
191 return;
192
193 free(part->cur_mp);
194 free(part->svc_name);
195 free(part);
196}
197
198static errno_t vol_part_probe(vol_part_t *part)
199{
200 bool empty;
201 vfs_fs_probe_info_t info;
202 struct fsname_type *fst;
203 char *label;
204 errno_t rc;
205
206 log_msg(LOG_DEFAULT, LVL_NOTE, "Probe partition %s", part->svc_name);
207
208 assert(fibril_mutex_is_locked(&vol_parts_lock));
209
210 fst = &fstab[0];
211 while (fst->name != NULL) {
212 rc = vfs_fsprobe(fst->name, part->svc_id, &info);
213 if (rc == EOK)
214 break;
215 ++fst;
216 }
217
218 if (fst->name != NULL) {
219 log_msg(LOG_DEFAULT, LVL_NOTE, "Found %s, label '%s'",
220 fst->name, info.label);
221 label = str_dup(info.label);
222 if (label == NULL) {
223 rc = ENOMEM;
224 goto error;
225 }
226
227 part->pcnt = vpc_fs;
228 part->fstype = fst->fstype;
229 part->label = label;
230 } else {
231 log_msg(LOG_DEFAULT, LVL_NOTE, "Partition does not contain "
232 "a recognized file system.");
233
234 rc = volsrv_part_is_empty(part->svc_id, &empty);
235 if (rc != EOK) {
236 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed determining if "
237 "partition is empty.");
238 rc = EIO;
239 goto error;
240 }
241
242 label = str_dup("");
243 if (label == NULL) {
244 rc = ENOMEM;
245 goto error;
246 }
247
248 part->pcnt = empty ? vpc_empty : vpc_unknown;
249 part->label = label;
250 }
251
252 return EOK;
253
254error:
255 return rc;
256}
257
258static errno_t vol_part_mount(vol_part_t *part)
259{
260 char *mp;
261 int err;
262 errno_t rc;
263
264 if (str_size(part->label) < 1) {
265 /* Don't mount nameless volumes */
266 log_msg(LOG_DEFAULT, LVL_NOTE, "Not mounting nameless partition.");
267 return EOK;
268 }
269
270 log_msg(LOG_DEFAULT, LVL_NOTE, "Determine MP label='%s'", part->label);
271 err = asprintf(&mp, "/vol/%s", part->label);
272 if (err < 0) {
273 log_msg(LOG_DEFAULT, LVL_ERROR, "Out of memory");
274 return ENOMEM;
275 }
276
277 log_msg(LOG_DEFAULT, LVL_NOTE, "Create mount point '%s'", mp);
278 rc = vfs_link_path(mp, KIND_DIRECTORY, NULL);
279 if (rc != EOK) {
280 log_msg(LOG_DEFAULT, LVL_ERROR, "Error creating mount point '%s'",
281 mp);
282 free(mp);
283 return EIO;
284 }
285
286 log_msg(LOG_DEFAULT, LVL_NOTE, "Call vfs_mount_path mp='%s' fstype='%s' svc_name='%s'",
287 mp, fstype_str(part->fstype), part->svc_name);
288 rc = vfs_mount_path(mp, fstype_str(part->fstype),
289 part->svc_name, "", 0, 0);
290 if (rc != EOK) {
291 log_msg(LOG_DEFAULT, LVL_NOTE, "Failed mounting to %s", mp);
292 }
293 log_msg(LOG_DEFAULT, LVL_NOTE, "Mount to %s -> %d\n", mp, rc);
294
295 part->cur_mp = mp;
296 part->cur_mp_auto = true;
297
298 return rc;
299}
300
301static errno_t vol_part_add_locked(service_id_t sid)
302{
303 vol_part_t *part;
304 errno_t rc;
305
306 assert(fibril_mutex_is_locked(&vol_parts_lock));
307 log_msg(LOG_DEFAULT, LVL_NOTE, "vol_part_add_locked(%zu)", sid);
308
309 /* Check for duplicates */
310 rc = vol_part_find_by_id_ref_locked(sid, &part);
311 if (rc == EOK) {
312 vol_part_del_ref(part);
313 return EEXIST;
314 }
315
316 log_msg(LOG_DEFAULT, LVL_NOTE, "partition %zu is new", sid);
317
318 part = vol_part_new();
319 if (part == NULL)
320 return ENOMEM;
321
322 part->svc_id = sid;
323
324 rc = loc_service_get_name(sid, &part->svc_name);
325 if (rc != EOK) {
326 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed getting service name.");
327 goto error;
328 }
329
330 rc = vol_part_probe(part);
331 if (rc != EOK)
332 goto error;
333
334 rc = vol_part_mount(part);
335 if (rc != EOK)
336 goto error;
337
338 list_append(&part->lparts, &vol_parts);
339
340 log_msg(LOG_DEFAULT, LVL_NOTE, "Added partition %zu", part->svc_id);
341
342 return EOK;
343
344error:
345 vol_part_delete(part);
346 return rc;
347}
348
349static void vol_part_remove_locked(vol_part_t *part)
350{
351 assert(fibril_mutex_is_locked(&vol_parts_lock));
352 log_msg(LOG_DEFAULT, LVL_NOTE, "vol_part_remove_locked(%zu)", part->svc_id);
353
354 list_remove(&part->lparts);
355
356 log_msg(LOG_DEFAULT, LVL_NOTE, "Removed partition.");
357 vol_part_del_ref(part);
358}
359
360errno_t vol_part_add(service_id_t sid)
361{
362 errno_t rc;
363
364 fibril_mutex_lock(&vol_parts_lock);
365 rc = vol_part_add_locked(sid);
366 fibril_mutex_unlock(&vol_parts_lock);
367
368 return rc;
369}
370
371static void vol_part_cat_change_cb(void)
372{
373 (void) vol_part_check_new();
374}
375
376errno_t vol_part_init(void)
377{
378 return EOK;
379}
380
381errno_t vol_part_discovery_start(void)
382{
383 errno_t rc;
384
385 rc = loc_register_cat_change_cb(vol_part_cat_change_cb);
386 if (rc != EOK) {
387 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering callback "
388 "for partition discovery: %s.", str_error(rc));
389 return rc;
390 }
391
392 return vol_part_check_new();
393}
394
395/** Get list of partitions as array of service IDs. */
396errno_t vol_part_get_ids(service_id_t *id_buf, size_t buf_size, size_t *act_size)
397{
398 size_t act_cnt;
399 size_t buf_cnt;
400
401 fibril_mutex_lock(&vol_parts_lock);
402
403 buf_cnt = buf_size / sizeof(service_id_t);
404
405 act_cnt = list_count(&vol_parts);
406 *act_size = act_cnt * sizeof(service_id_t);
407
408 if (buf_size % sizeof(service_id_t) != 0) {
409 fibril_mutex_unlock(&vol_parts_lock);
410 return EINVAL;
411 }
412
413 size_t pos = 0;
414 list_foreach(vol_parts, lparts, vol_part_t, part) {
415 if (pos < buf_cnt)
416 id_buf[pos] = part->svc_id;
417 pos++;
418 }
419
420 fibril_mutex_unlock(&vol_parts_lock);
421 return EOK;
422}
423
424static errno_t vol_part_find_by_id_ref_locked(service_id_t sid,
425 vol_part_t **rpart)
426{
427 assert(fibril_mutex_is_locked(&vol_parts_lock));
428
429 list_foreach(vol_parts, lparts, vol_part_t, part) {
430 if (part->svc_id == sid) {
431 /* Add reference */
432 atomic_inc(&part->refcnt);
433 *rpart = part;
434 return EOK;
435 }
436 }
437
438 return ENOENT;
439}
440
441errno_t vol_part_find_by_id_ref(service_id_t sid, vol_part_t **rpart)
442{
443 errno_t rc;
444
445 fibril_mutex_lock(&vol_parts_lock);
446 rc = vol_part_find_by_id_ref_locked(sid, rpart);
447 fibril_mutex_unlock(&vol_parts_lock);
448
449 return rc;
450}
451
452void vol_part_del_ref(vol_part_t *part)
453{
454 if (atomic_predec(&part->refcnt) == 0)
455 vol_part_delete(part);
456}
457
458errno_t vol_part_eject_part(vol_part_t *part)
459{
460 int rc;
461
462 log_msg(LOG_DEFAULT, LVL_DEBUG, "vol_part_eject_part()");
463
464 if (part->cur_mp == NULL) {
465 log_msg(LOG_DEFAULT, LVL_DEBUG, "Attempt to mount unmounted "
466 "partition.");
467 return EINVAL;
468 }
469
470 rc = vfs_unmount_path(part->cur_mp);
471 if (rc != EOK) {
472 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed unmounting partition "
473 "from %s", part->cur_mp);
474 return rc;
475 }
476
477 if (part->cur_mp_auto) {
478 rc = vfs_unlink_path(part->cur_mp);
479 if (rc != EOK) {
480 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed deleting "
481 "mount directory %s.", part->cur_mp);
482 }
483 }
484
485 free(part->cur_mp);
486 part->cur_mp = NULL;
487 part->cur_mp_auto = false;
488
489 return EOK;
490}
491
492errno_t vol_part_empty_part(vol_part_t *part)
493{
494 errno_t rc;
495
496 log_msg(LOG_DEFAULT, LVL_DEBUG, "vol_part_empty_part()");
497
498 rc = volsrv_part_empty(part->svc_id);
499 if (rc != EOK) {
500 log_msg(LOG_DEFAULT, LVL_DEBUG, "vol_part_empty_part() - failed %s",
501 str_error(rc));
502 return rc;
503 }
504
505 part->pcnt = vpc_empty;
506 return EOK;
507}
508
509errno_t vol_part_mkfs_part(vol_part_t *part, vol_fstype_t fstype,
510 const char *label)
511{
512 errno_t rc;
513
514 log_msg(LOG_DEFAULT, LVL_DEBUG, "vol_part_mkfs_part()");
515
516 fibril_mutex_lock(&vol_parts_lock);
517
518 rc = volsrv_part_mkfs(part->svc_id, fstype, label);
519 if (rc != EOK) {
520 log_msg(LOG_DEFAULT, LVL_DEBUG, "vol_part_mkfs_part() - failed %s",
521 str_error(rc));
522 fibril_mutex_unlock(&vol_parts_lock);
523 return rc;
524 }
525
526 /*
527 * Re-probe the partition to update information. This is needed since
528 * the FS can make conversions of the volume label (e.g. make it
529 * uppercase).
530 */
531 rc = vol_part_probe(part);
532 if (rc != EOK) {
533 fibril_mutex_unlock(&vol_parts_lock);
534 return rc;
535 }
536
537 rc = vol_part_mount(part);
538 if (rc != EOK) {
539 fibril_mutex_unlock(&vol_parts_lock);
540 return rc;
541 }
542
543 fibril_mutex_unlock(&vol_parts_lock);
544 return EOK;
545}
546
547errno_t vol_part_get_info(vol_part_t *part, vol_part_info_t *pinfo)
548{
549 memset(pinfo, 0, sizeof(*pinfo));
550
551 pinfo->pcnt = part->pcnt;
552 pinfo->fstype = part->fstype;
553 str_cpy(pinfo->label, sizeof(pinfo->label), part->label);
554 if (part->cur_mp != NULL)
555 str_cpy(pinfo->cur_mp, sizeof(pinfo->cur_mp), part->cur_mp);
556 pinfo->cur_mp_auto = part->cur_mp_auto;
557 return EOK;
558}
559
560/** @}
561 */
Note: See TracBrowser for help on using the repository browser.