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

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

Persistence of Tetris highscore table. Detect live mode and create directory structure in init task. Reading volume configuration, vol -c.

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