source: mainline/uspace/srv/volsrv/volume.c

Last change on this file was ca127f37, checked in by Jiri Svoboda <jiri@…>, 10 months ago

Persist volume configuration

  • Property mode set to 100644
File size: 15.7 KB
Line 
1/*
2 * Copyright (c) 2024 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 Volume handling
34 * @brief
35 *
36 * Volumes are the file systems (or similar) contained in partitions.
37 * Each vol_volume_t can be considered the configuration entry
38 * for a volume. Each partition has an associated vol_volume_t.
39 *
40 * If there is any non-default configuration to be remembered for a
41 * volume, the vol_volume_t structure is kept around even after the partition
42 * is disassociated from it. Otherwise it is deleted once no longer
43 * referenced.
44 */
45
46#include <adt/list.h>
47#include <errno.h>
48#include <fibril_synch.h>
49#include <io/log.h>
50#include <sif.h>
51#include <stdbool.h>
52#include <stdlib.h>
53#include <str.h>
54
55#include "volume.h"
56#include "types/volume.h"
57
58static void vol_volume_delete(vol_volume_t *);
59static void vol_volume_add_locked(vol_volumes_t *, vol_volume_t *);
60static errno_t vol_volume_lookup_ref_locked(vol_volumes_t *, const char *,
61 vol_volume_t **);
62static errno_t vol_volumes_load(sif_node_t *, vol_volumes_t *);
63static errno_t vol_volumes_save(vol_volumes_t *, sif_node_t *);
64
65/** Allocate new volume structure.
66 *
67 * @return Pointer to new volume structure
68 */
69static vol_volume_t *vol_volume_new(void)
70{
71 vol_volume_t *volume = calloc(1, sizeof(vol_volume_t));
72
73 if (volume == NULL) {
74 log_msg(LOG_DEFAULT, LVL_ERROR, "Failed allocating volume "
75 "structure. Out of memory.");
76 return NULL;
77 }
78
79 volume->label = str_dup("");
80 volume->mountp = str_dup("");
81
82 if (volume->label == NULL || volume->mountp == NULL) {
83 vol_volume_delete(volume);
84 return NULL;
85 }
86
87 refcount_init(&volume->refcnt);
88 link_initialize(&volume->lvolumes);
89 volume->volumes = NULL;
90
91 return volume;
92}
93
94/** Delete volume structure.
95 *
96 * @param volume Volume structure
97 */
98static void vol_volume_delete(vol_volume_t *volume)
99{
100 log_msg(LOG_DEFAULT, LVL_DEBUG, "Freeing volume %p", volume);
101
102 free(volume->label);
103 free(volume->mountp);
104 free(volume);
105}
106
107/** Create list of volumes.
108 *
109 * @param cfg_path Path to file containing configuration repository in SIF
110 * @param rvolumes Place to store pointer to list of volumes.
111 * @return EOK on success, ENOMEM if out of memory
112 */
113errno_t vol_volumes_create(const char *cfg_path,
114 vol_volumes_t **rvolumes)
115{
116 vol_volumes_t *volumes;
117 sif_doc_t *doc = NULL;
118 sif_node_t *node;
119 sif_node_t *nvolumes;
120 const char *ntype;
121 errno_t rc;
122
123 volumes = calloc(1, sizeof(vol_volumes_t));
124 if (volumes == NULL)
125 return ENOMEM;
126
127 volumes->cfg_path = str_dup(cfg_path);
128 if (volumes->cfg_path == NULL) {
129 rc = ENOMEM;
130 goto error;
131 }
132
133 fibril_mutex_initialize(&volumes->lock);
134 list_initialize(&volumes->volumes);
135 volumes->next_id = 1;
136
137 /* Try opening existing repository */
138 rc = sif_load(cfg_path, &doc);
139 if (rc != EOK) {
140 /* Failed to open existing, create new repository */
141 rc = sif_new(&doc);
142 if (rc != EOK)
143 goto error;
144
145 /* Create 'volumes' node. */
146 rc = sif_node_append_child(sif_get_root(doc), "volumes",
147 &nvolumes);
148 if (rc != EOK)
149 goto error;
150
151 rc = sif_save(doc, cfg_path);
152 if (rc != EOK)
153 goto error;
154
155 sif_delete(doc);
156 } else {
157 /*
158 * Loaded existing configuration. Find 'volumes' node, should
159 * be the first child of the root node.
160 */
161 node = sif_node_first_child(sif_get_root(doc));
162
163 /* Verify it's the correct node type */
164 ntype = sif_node_get_type(node);
165 if (str_cmp(ntype, "volumes") != 0) {
166 rc = EIO;
167 goto error;
168 }
169
170 rc = vol_volumes_load(node, volumes);
171 if (rc != EOK)
172 goto error;
173
174 sif_delete(doc);
175 }
176
177 *rvolumes = volumes;
178 return EOK;
179error:
180 if (doc != NULL)
181 (void) sif_delete(doc);
182 if (volumes != NULL && volumes->cfg_path != NULL)
183 free(volumes->cfg_path);
184 if (volumes != NULL)
185 free(volumes);
186
187 return rc;
188}
189
190/** Merge list of volumes into new file.
191 *
192 * @param volumes List of volumes
193 * @param cfg_path Path to file containing configuration repository in SIF
194 * @return EOK on success, ENOMEM if out of memory
195 */
196errno_t vol_volumes_merge_to(vol_volumes_t *volumes, const char *cfg_path)
197{
198 sif_doc_t *doc = NULL;
199 sif_node_t *node;
200 const char *ntype;
201 char *dcfg_path;
202 errno_t rc;
203
204 dcfg_path = str_dup(cfg_path);
205 if (dcfg_path == NULL) {
206 rc = ENOMEM;
207 goto error;
208 }
209
210 free(volumes->cfg_path);
211 volumes->cfg_path = dcfg_path;
212
213 /* Try opening existing repository */
214 rc = sif_load(cfg_path, &doc);
215 if (rc != EOK) {
216 /* Failed to open existing, create new repository */
217 rc = vol_volumes_sync(volumes);
218 if (rc != EOK)
219 goto error;
220 } else {
221 /*
222 * Loaded existing configuration. Find 'volumes' node, should
223 * be the first child of the root node.
224 */
225 node = sif_node_first_child(sif_get_root(doc));
226
227 /* Verify it's the correct node type */
228 ntype = sif_node_get_type(node);
229 if (str_cmp(ntype, "volumes") != 0) {
230 rc = EIO;
231 goto error;
232 }
233
234 rc = vol_volumes_load(node, volumes);
235 if (rc != EOK)
236 goto error;
237
238 sif_delete(doc);
239 }
240
241 return EOK;
242error:
243 if (doc != NULL)
244 (void) sif_delete(doc);
245 return rc;
246}
247
248/** Sync volume configuration to config file.
249 *
250 * @param volumes List of volumes
251 * @return EOK on success, ENOMEM if out of memory
252 */
253errno_t vol_volumes_sync(vol_volumes_t *volumes)
254{
255 sif_doc_t *doc = NULL;
256 errno_t rc;
257
258 rc = sif_new(&doc);
259 if (rc != EOK)
260 goto error;
261
262 rc = vol_volumes_save(volumes, sif_get_root(doc));
263 if (rc != EOK)
264 goto error;
265
266 rc = sif_save(doc, volumes->cfg_path);
267 if (rc != EOK)
268 goto error;
269
270 sif_delete(doc);
271 return EOK;
272error:
273 if (doc != NULL)
274 (void) sif_delete(doc);
275 return rc;
276}
277
278/** Destroy list of volumes.
279 *
280 * @param volumes List of volumes or @c NULL
281 */
282void vol_volumes_destroy(vol_volumes_t *volumes)
283{
284 link_t *link;
285 vol_volume_t *volume;
286
287 if (volumes == NULL)
288 return;
289
290 link = list_first(&volumes->volumes);
291 while (link != NULL) {
292 volume = list_get_instance(link, vol_volume_t, lvolumes);
293
294 list_remove(&volume->lvolumes);
295 vol_volume_delete(volume);
296
297 link = list_first(&volumes->volumes);
298 }
299
300 free(volumes->cfg_path);
301 free(volumes);
302}
303
304/** Add new volume structure to list of volumes.
305 *
306 * @param volumes List of volumes
307 * @param volume Volume structure
308 */
309static void vol_volume_add_locked(vol_volumes_t *volumes,
310 vol_volume_t *volume)
311{
312 assert(fibril_mutex_is_locked(&volumes->lock));
313 log_msg(LOG_DEFAULT, LVL_DEBUG, "vol_volume_add_locked(%p)", volume);
314
315 volume->volumes = volumes;
316 list_append(&volume->lvolumes, &volumes->volumes);
317 volume->id.id = volumes->next_id;
318 ++volumes->next_id;
319}
320
321/** Look up volume structure with locked volumes lock.
322 *
323 * If a matching existing volume is found, it is returned. Otherwise
324 * a new volume structure is created.
325 *
326 * @param volumes List of volumes
327 * @param label Volume label
328 * @param rvolume Place to store pointer to volume structure (existing or new)
329 *
330 * @return EOK on success, ENOMEM if out of memory
331 */
332static errno_t vol_volume_lookup_ref_locked(vol_volumes_t *volumes,
333 const char *label, vol_volume_t **rvolume)
334{
335 vol_volume_t *volume;
336
337 assert(fibril_mutex_is_locked(&volumes->lock));
338
339 list_foreach(volumes->volumes, lvolumes, vol_volume_t, volume) {
340 if (str_cmp(volume->label, label) == 0 &&
341 str_size(label) > 0) {
342 /* Add reference */
343 refcount_up(&volume->refcnt);
344 *rvolume = volume;
345 return EOK;
346 }
347 }
348
349 /* No existing volume found. Create a new one. */
350 volume = vol_volume_new();
351 if (volume == NULL)
352 return ENOMEM;
353
354 free(volume->label);
355 volume->label = str_dup(label);
356
357 if (volume->label == NULL) {
358 vol_volume_delete(volume);
359 return ENOMEM;
360 }
361
362 vol_volume_add_locked(volumes, volume);
363
364 *rvolume = volume;
365 return EOK;
366}
367
368/** Look up volume structure.
369 *
370 * If a matching existing volume is found, it is returned. Otherwise
371 * a new volume structure is created.
372 *
373 * @param volumes List of volumes
374 * @param label Volume label
375 * @param rvolume Place to store pointer to volume structure (existing or new)
376 *
377 * @return EOK on success, ENOMEM if out of memory
378 */
379errno_t vol_volume_lookup_ref(vol_volumes_t *volumes, const char *label,
380 vol_volume_t **rvolume)
381{
382 errno_t rc;
383
384 fibril_mutex_lock(&volumes->lock);
385 rc = vol_volume_lookup_ref_locked(volumes, label, rvolume);
386 fibril_mutex_unlock(&volumes->lock);
387
388 return rc;
389}
390
391/** Find volume structure by ID with locked volumes lock.
392 * *
393 * @param volumes List of volumes
394 * @param vid Volume ID
395 * @param rvolume Place to store pointer to volume structure (existing or new)
396 *
397 * @return EOK on success, ENOENT if not found
398 */
399static errno_t vol_volume_find_by_id_ref_locked(vol_volumes_t *volumes,
400 volume_id_t vid, vol_volume_t **rvolume)
401{
402 assert(fibril_mutex_is_locked(&volumes->lock));
403
404 list_foreach(volumes->volumes, lvolumes, vol_volume_t, volume) {
405 log_msg(LOG_DEFAULT, LVL_DEBUG2,
406 "vol_volume_find_by_id_ref_locked(%zu==%zu)?",
407 volume->id.id, vid.id);
408 if (volume->id.id == vid.id) {
409 log_msg(LOG_DEFAULT, LVL_DEBUG2,
410 "vol_volume_find_by_id_ref_locked: found");
411 /* Add reference */
412 refcount_up(&volume->refcnt);
413 *rvolume = volume;
414 return EOK;
415 }
416 }
417
418 log_msg(LOG_DEFAULT, LVL_DEBUG2,
419 "vol_volume_find_by_id_ref_locked: not found");
420 return ENOENT;
421}
422
423/** Find volume by ID.
424 *
425 * @param volumes Volumes
426 * @param vid Volume ID
427 * @param rvolume Place to store pointer to volume, with reference count
428 * increased.
429 * @return EOK on success or an error code
430 */
431errno_t vol_volume_find_by_id_ref(vol_volumes_t *volumes, volume_id_t vid,
432 vol_volume_t **rvolume)
433{
434 errno_t rc;
435
436 fibril_mutex_lock(&volumes->lock);
437 rc = vol_volume_find_by_id_ref_locked(volumes, vid, rvolume);
438 fibril_mutex_unlock(&volumes->lock);
439
440 return rc;
441}
442
443/** Determine if volume has non-default settings that need to persist.
444 *
445 * @param volume Volume
446 * @return @c true iff volume has settings that need to persist
447 */
448static bool vol_volume_is_persist(vol_volume_t *volume)
449{
450 return str_size(volume->mountp) > 0;
451}
452
453/** Delete reference to volume.
454 *
455 * @param volume Volume
456 */
457void vol_volume_del_ref(vol_volume_t *volume)
458{
459 if (refcount_down(&volume->refcnt)) {
460 /* No more references. Check if volume is persistent. */
461 list_remove(&volume->lvolumes);
462 vol_volume_delete(volume);
463 }
464}
465
466/** Set volume mount point.
467 *
468 * @param volume Volume
469 * @param mountp Mount point
470 *
471 * @return EOK on success or error code
472 */
473errno_t vol_volume_set_mountp(vol_volume_t *volume, const char *mountp)
474{
475 char *mp;
476 char *old_mp;
477 bool was_persist;
478
479 mp = str_dup(mountp);
480 if (mp == NULL)
481 return ENOMEM;
482
483 was_persist = vol_volume_is_persist(volume);
484
485 old_mp = volume->mountp;
486 volume->mountp = mp;
487
488 if (vol_volume_is_persist(volume)) {
489 if (!was_persist) {
490 /*
491 * Volume is now persistent. Prevent it from being
492 * freed.
493 */
494 refcount_up(&volume->refcnt);
495 }
496 } else {
497 if (was_persist) {
498 /*
499 * Volume is now non-persistent
500 * Allow volume to be freed.
501 */
502 vol_volume_del_ref(volume);
503 }
504 }
505
506 vol_volumes_sync(volume->volumes);
507 free(old_mp);
508 return EOK;
509}
510
511/** Get list of volume IDs.
512 *
513 * Get the list of IDs of all persistent volumes (volume configuration
514 * entries).
515 *
516 * @param volumes Volumes
517 * @param id_buf Buffer to hold the IDs
518 * @param buf_size Buffer size in bytes
519 * @param act_size Place to store actual number bytes needed
520 * @return EOK on success or an error code
521 */
522errno_t vol_get_ids(vol_volumes_t *volumes, volume_id_t *id_buf,
523 size_t buf_size, size_t *act_size)
524{
525 size_t act_cnt;
526 size_t buf_cnt;
527
528 fibril_mutex_lock(&volumes->lock);
529
530 buf_cnt = buf_size / sizeof(volume_id_t);
531
532 act_cnt = 0;
533 list_foreach(volumes->volumes, lvolumes, vol_volume_t, volume) {
534 if (vol_volume_is_persist(volume))
535 ++act_cnt;
536 }
537 *act_size = act_cnt * sizeof(volume_id_t);
538
539 if (buf_size % sizeof(volume_id_t) != 0) {
540 fibril_mutex_unlock(&volumes->lock);
541 return EINVAL;
542 }
543
544 size_t pos = 0;
545 list_foreach(volumes->volumes, lvolumes, vol_volume_t, volume) {
546 if (vol_volume_is_persist(volume)) {
547 if (pos < buf_cnt)
548 id_buf[pos].id = volume->id.id;
549 pos++;
550 }
551 }
552
553 fibril_mutex_unlock(&volumes->lock);
554 return EOK;
555}
556
557/** Load volumes from SIF document.
558 *
559 * @param nvolumes Volumes node
560 * @param volumes Volumes object
561 *
562 * @return EOK on success or error code
563 */
564static errno_t vol_volumes_load(sif_node_t *nvolumes, vol_volumes_t *volumes)
565{
566 sif_node_t *nvolume;
567 vol_volume_t *volume = NULL;
568 const char *label;
569 const char *mountp;
570 errno_t rc;
571
572 nvolume = sif_node_first_child(nvolumes);
573 while (nvolume != NULL) {
574 if (str_cmp(sif_node_get_type(nvolume), "volume") != 0) {
575 rc = EIO;
576 goto error;
577 }
578
579 volume = vol_volume_new();
580 if (volume == NULL) {
581 rc = ENOMEM;
582 goto error;
583 }
584
585 label = sif_node_get_attr(nvolume, "label");
586 mountp = sif_node_get_attr(nvolume, "mountp");
587
588 if (label == NULL || mountp == NULL) {
589 rc = EIO;
590 goto error;
591 }
592
593 free(volume->label);
594 free(volume->mountp);
595
596 volume->label = str_dup(label);
597 volume->mountp = str_dup(mountp);
598
599 fibril_mutex_lock(&volumes->lock);
600 vol_volume_add_locked(volumes, volume);
601 fibril_mutex_unlock(&volumes->lock);
602 nvolume = sif_node_next_child(nvolume);
603 }
604
605 return EOK;
606error:
607 if (volume != NULL)
608 vol_volume_delete(volume);
609 return rc;
610}
611
612/** Save volumes to SIF document.
613 *
614 * @param volumes List of volumes
615 * @param rnode Configuration root node
616 * @return EOK on success, ENOMEM if out of memory
617 */
618errno_t vol_volumes_save(vol_volumes_t *volumes, sif_node_t *rnode)
619{
620 sif_node_t *nvolumes;
621 sif_node_t *node;
622 link_t *link;
623 vol_volume_t *volume;
624 errno_t rc;
625
626 /* Create 'volumes' node. */
627 rc = sif_node_append_child(rnode, "volumes", &nvolumes);
628 if (rc != EOK)
629 goto error;
630
631 link = list_first(&volumes->volumes);
632 while (link != NULL) {
633 volume = list_get_instance(link, vol_volume_t, lvolumes);
634
635 if (vol_volume_is_persist(volume)) {
636 /* Create 'volume' node. */
637 rc = sif_node_append_child(nvolumes, "volume", &node);
638 if (rc != EOK)
639 goto error;
640
641 rc = sif_node_set_attr(node, "label", volume->label);
642 if (rc != EOK)
643 goto error;
644
645 rc = sif_node_set_attr(node, "mountp", volume->mountp);
646 if (rc != EOK)
647 goto error;
648 }
649
650 link = list_next(&volume->lvolumes, &volumes->volumes);
651 }
652
653 return EOK;
654error:
655 return rc;
656}
657
658/** Get volume information.
659 *
660 * @param volume Volume
661 * @param vinfo Volume information structure to safe info to
662 * @return EOK on success or an error code
663 */
664errno_t vol_volume_get_info(vol_volume_t *volume, vol_info_t *vinfo)
665{
666 vinfo->id = volume->id;
667 str_cpy(vinfo->label, sizeof(vinfo->label), volume->label);
668 str_cpy(vinfo->path, sizeof(vinfo->path), volume->mountp);
669 return EOK;
670}
671
672/** @}
673 */
Note: See TracBrowser for help on using the repository browser.