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

Last change on this file since cde067e was bff8619, checked in by Jiri Svoboda <jiri@…>, 12 months ago

Simplify SIF interface, remove contacts

Remove transactions, move to a load/save model. Remove contacts
application as it was never finished and not useful at all.

  • Property mode set to 100644
File size: 14.5 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/** Sync volume configuration to config file.
191 *
192 * @param volumes List of volumes
193 * @return EOK on success, ENOMEM if out of memory
194 */
195errno_t vol_volumes_sync(vol_volumes_t *volumes)
196{
197 sif_doc_t *doc = NULL;
198 errno_t rc;
199
200 rc = sif_new(&doc);
201 if (rc != EOK)
202 goto error;
203
204 rc = vol_volumes_save(volumes, sif_get_root(doc));
205 if (rc != EOK)
206 goto error;
207
208 rc = sif_save(doc, volumes->cfg_path);
209 if (rc != EOK)
210 goto error;
211
212 sif_delete(doc);
213 return EOK;
214error:
215 if (doc != NULL)
216 (void) sif_delete(doc);
217 return rc;
218}
219
220/** Destroy list of volumes.
221 *
222 * @param volumes List of volumes or @c NULL
223 */
224void vol_volumes_destroy(vol_volumes_t *volumes)
225{
226 link_t *link;
227 vol_volume_t *volume;
228
229 if (volumes == NULL)
230 return;
231
232 link = list_first(&volumes->volumes);
233 while (link != NULL) {
234 volume = list_get_instance(link, vol_volume_t, lvolumes);
235
236 list_remove(&volume->lvolumes);
237 vol_volume_delete(volume);
238
239 link = list_first(&volumes->volumes);
240 }
241
242 free(volumes->cfg_path);
243 free(volumes);
244}
245
246/** Add new volume structure to list of volumes.
247 *
248 * @param volumes List of volumes
249 * @param volume Volume structure
250 */
251static void vol_volume_add_locked(vol_volumes_t *volumes,
252 vol_volume_t *volume)
253{
254 assert(fibril_mutex_is_locked(&volumes->lock));
255 log_msg(LOG_DEFAULT, LVL_DEBUG, "vol_volume_add_locked(%p)", volume);
256
257 volume->volumes = volumes;
258 list_append(&volume->lvolumes, &volumes->volumes);
259 volume->id.id = volumes->next_id;
260 ++volumes->next_id;
261}
262
263/** Look up volume structure with locked volumes lock.
264 *
265 * If a matching existing volume is found, it is returned. Otherwise
266 * a new volume structure is created.
267 *
268 * @param volumes List of volumes
269 * @param label Volume label
270 * @param rvolume Place to store pointer to volume structure (existing or new)
271 *
272 * @return EOK on success, ENOMEM if out of memory
273 */
274static errno_t vol_volume_lookup_ref_locked(vol_volumes_t *volumes,
275 const char *label, vol_volume_t **rvolume)
276{
277 vol_volume_t *volume;
278
279 assert(fibril_mutex_is_locked(&volumes->lock));
280
281 list_foreach(volumes->volumes, lvolumes, vol_volume_t, volume) {
282 if (str_cmp(volume->label, label) == 0 &&
283 str_size(label) > 0) {
284 /* Add reference */
285 refcount_up(&volume->refcnt);
286 *rvolume = volume;
287 return EOK;
288 }
289 }
290
291 /* No existing volume found. Create a new one. */
292 volume = vol_volume_new();
293 if (volume == NULL)
294 return ENOMEM;
295
296 free(volume->label);
297 volume->label = str_dup(label);
298
299 if (volume->label == NULL) {
300 vol_volume_delete(volume);
301 return ENOMEM;
302 }
303
304 vol_volume_add_locked(volumes, volume);
305
306 *rvolume = volume;
307 return EOK;
308}
309
310/** Look up volume structure.
311 *
312 * If a matching existing volume is found, it is returned. Otherwise
313 * a new volume structure is created.
314 *
315 * @param volumes List of volumes
316 * @param label Volume label
317 * @param rvolume Place to store pointer to volume structure (existing or new)
318 *
319 * @return EOK on success, ENOMEM if out of memory
320 */
321errno_t vol_volume_lookup_ref(vol_volumes_t *volumes, const char *label,
322 vol_volume_t **rvolume)
323{
324 errno_t rc;
325
326 fibril_mutex_lock(&volumes->lock);
327 rc = vol_volume_lookup_ref_locked(volumes, label, rvolume);
328 fibril_mutex_unlock(&volumes->lock);
329
330 return rc;
331}
332
333/** Find volume structure by ID with locked volumes lock.
334 * *
335 * @param volumes List of volumes
336 * @param vid Volume ID
337 * @param rvolume Place to store pointer to volume structure (existing or new)
338 *
339 * @return EOK on success, ENOENT if not found
340 */
341static errno_t vol_volume_find_by_id_ref_locked(vol_volumes_t *volumes,
342 volume_id_t vid, vol_volume_t **rvolume)
343{
344 assert(fibril_mutex_is_locked(&volumes->lock));
345
346 list_foreach(volumes->volumes, lvolumes, vol_volume_t, volume) {
347 log_msg(LOG_DEFAULT, LVL_DEBUG2,
348 "vol_volume_find_by_id_ref_locked(%zu==%zu)?",
349 volume->id.id, vid.id);
350 if (volume->id.id == vid.id) {
351 log_msg(LOG_DEFAULT, LVL_DEBUG2,
352 "vol_volume_find_by_id_ref_locked: found");
353 /* Add reference */
354 refcount_up(&volume->refcnt);
355 *rvolume = volume;
356 return EOK;
357 }
358 }
359
360 log_msg(LOG_DEFAULT, LVL_DEBUG2,
361 "vol_volume_find_by_id_ref_locked: not found");
362 return ENOENT;
363}
364
365/** Find volume by ID.
366 *
367 * @param volumes Volumes
368 * @param vid Volume ID
369 * @param rvolume Place to store pointer to volume, with reference count
370 * increased.
371 * @return EOK on success or an error code
372 */
373errno_t vol_volume_find_by_id_ref(vol_volumes_t *volumes, volume_id_t vid,
374 vol_volume_t **rvolume)
375{
376 errno_t rc;
377
378 fibril_mutex_lock(&volumes->lock);
379 rc = vol_volume_find_by_id_ref_locked(volumes, vid, rvolume);
380 fibril_mutex_unlock(&volumes->lock);
381
382 return rc;
383}
384
385/** Determine if volume has non-default settings that need to persist.
386 *
387 * @param volume Volume
388 * @return @c true iff volume has settings that need to persist
389 */
390static bool vol_volume_is_persist(vol_volume_t *volume)
391{
392 return str_size(volume->mountp) > 0;
393}
394
395/** Delete reference to volume.
396 *
397 * @param volume Volume
398 */
399void vol_volume_del_ref(vol_volume_t *volume)
400{
401 if (refcount_down(&volume->refcnt)) {
402 /* No more references. Check if volume is persistent. */
403 list_remove(&volume->lvolumes);
404 vol_volume_delete(volume);
405 }
406}
407
408/** Set volume mount point.
409 *
410 * @param volume Volume
411 * @param mountp Mount point
412 *
413 * @return EOK on success or error code
414 */
415errno_t vol_volume_set_mountp(vol_volume_t *volume, const char *mountp)
416{
417 char *mp;
418 char *old_mp;
419 bool was_persist;
420
421 mp = str_dup(mountp);
422 if (mp == NULL)
423 return ENOMEM;
424
425 was_persist = vol_volume_is_persist(volume);
426
427 old_mp = volume->mountp;
428 volume->mountp = mp;
429
430 if (vol_volume_is_persist(volume)) {
431 if (!was_persist) {
432 /*
433 * Volume is now persistent. Prevent it from being
434 * freed.
435 */
436 refcount_up(&volume->refcnt);
437 }
438 } else {
439 if (was_persist) {
440 /*
441 * Volume is now non-persistent
442 * Allow volume to be freed.
443 */
444 vol_volume_del_ref(volume);
445 }
446 }
447
448 vol_volumes_sync(volume->volumes);
449 free(old_mp);
450 return EOK;
451}
452
453/** Get list of volume IDs.
454 *
455 * Get the list of IDs of all persistent volumes (volume configuration
456 * entries).
457 *
458 * @param volumes Volumes
459 * @param id_buf Buffer to hold the IDs
460 * @param buf_size Buffer size in bytes
461 * @param act_size Place to store actual number bytes needed
462 * @return EOK on success or an error code
463 */
464errno_t vol_get_ids(vol_volumes_t *volumes, volume_id_t *id_buf,
465 size_t buf_size, size_t *act_size)
466{
467 size_t act_cnt;
468 size_t buf_cnt;
469
470 fibril_mutex_lock(&volumes->lock);
471
472 buf_cnt = buf_size / sizeof(volume_id_t);
473
474 act_cnt = 0;
475 list_foreach(volumes->volumes, lvolumes, vol_volume_t, volume) {
476 if (vol_volume_is_persist(volume))
477 ++act_cnt;
478 }
479 *act_size = act_cnt * sizeof(volume_id_t);
480
481 if (buf_size % sizeof(volume_id_t) != 0) {
482 fibril_mutex_unlock(&volumes->lock);
483 return EINVAL;
484 }
485
486 size_t pos = 0;
487 list_foreach(volumes->volumes, lvolumes, vol_volume_t, volume) {
488 if (vol_volume_is_persist(volume)) {
489 if (pos < buf_cnt)
490 id_buf[pos].id = volume->id.id;
491 pos++;
492 }
493 }
494
495 fibril_mutex_unlock(&volumes->lock);
496 return EOK;
497}
498
499/** Load volumes from SIF document.
500 *
501 * @param nvolumes Volumes node
502 * @param volumes Volumes object
503 *
504 * @return EOK on success or error code
505 */
506static errno_t vol_volumes_load(sif_node_t *nvolumes, vol_volumes_t *volumes)
507{
508 sif_node_t *nvolume;
509 vol_volume_t *volume = NULL;
510 const char *label;
511 const char *mountp;
512 errno_t rc;
513
514 nvolume = sif_node_first_child(nvolumes);
515 while (nvolume != NULL) {
516 if (str_cmp(sif_node_get_type(nvolume), "volume") != 0) {
517 rc = EIO;
518 goto error;
519 }
520
521 volume = vol_volume_new();
522 if (volume == NULL) {
523 rc = ENOMEM;
524 goto error;
525 }
526
527 label = sif_node_get_attr(nvolume, "label");
528 mountp = sif_node_get_attr(nvolume, "mountp");
529
530 if (label == NULL || mountp == NULL) {
531 rc = EIO;
532 goto error;
533 }
534
535 free(volume->label);
536 free(volume->mountp);
537
538 volume->label = str_dup(label);
539 volume->mountp = str_dup(mountp);
540
541 fibril_mutex_lock(&volumes->lock);
542 vol_volume_add_locked(volumes, volume);
543 fibril_mutex_unlock(&volumes->lock);
544 nvolume = sif_node_next_child(nvolume);
545 }
546
547 return EOK;
548error:
549 if (volume != NULL)
550 vol_volume_delete(volume);
551 return rc;
552}
553
554/** Save volumes to SIF document.
555 *
556 * @param volumes List of volumes
557 * @param rnode Configuration root node
558 * @return EOK on success, ENOMEM if out of memory
559 */
560errno_t vol_volumes_save(vol_volumes_t *volumes, sif_node_t *rnode)
561{
562 sif_node_t *nvolumes;
563 sif_node_t *node;
564 link_t *link;
565 vol_volume_t *volume;
566 errno_t rc;
567
568 /* Create 'volumes' node. */
569 rc = sif_node_append_child(rnode, "volumes", &nvolumes);
570 if (rc != EOK)
571 goto error;
572
573 link = list_first(&volumes->volumes);
574 while (link != NULL) {
575 volume = list_get_instance(link, vol_volume_t, lvolumes);
576
577 if (vol_volume_is_persist(volume)) {
578 /* Create 'volume' node. */
579 rc = sif_node_append_child(rnode, "volume", &node);
580 if (rc != EOK)
581 goto error;
582
583 rc = sif_node_set_attr(node, "label", volume->label);
584 if (rc != EOK)
585 goto error;
586
587 rc = sif_node_set_attr(node, "mountp", volume->mountp);
588 if (rc != EOK)
589 goto error;
590 }
591
592 link = list_next(&volume->lvolumes, &volumes->volumes);
593 }
594
595 return EOK;
596error:
597 return rc;
598}
599
600/** Get volume information.
601 *
602 * @param volume Volume
603 * @param vinfo Volume information structure to safe info to
604 * @return EOK on success or an error code
605 */
606errno_t vol_volume_get_info(vol_volume_t *volume, vol_info_t *vinfo)
607{
608 vinfo->id = volume->id;
609 str_cpy(vinfo->label, sizeof(vinfo->label), volume->label);
610 str_cpy(vinfo->path, sizeof(vinfo->path), volume->mountp);
611 return EOK;
612}
613
614/** @}
615 */
Note: See TracBrowser for help on using the repository browser.