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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d51cca8 was 498ced1, checked in by Jiří Zárevúcky <jiri.zarevucky@…>, 7 years ago

Unify reference counting and remove some unnecessary instances of <atomic.h>

  • Property mode set to 100644
File size: 11.1 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
128 /* Try opening existing repository */
129 rc = sif_open(cfg_path, &repo);
130 if (rc != EOK) {
131 /* Failed to open existing, create new repository */
132 rc = sif_create(cfg_path, &repo);
133 if (rc != EOK)
134 goto error;
135
136 rc = sif_trans_begin(repo, &trans);
137 if (rc != EOK)
138 goto error;
139
140 /* Create 'volumes' node. */
141 rc = sif_node_append_child(trans, sif_get_root(repo),
142 "volumes", &volumes->nvolumes);
143 if (rc != EOK)
144 goto error;
145
146 rc = sif_trans_end(trans);
147 if (rc != EOK)
148 goto error;
149
150 trans = NULL;
151 } else {
152 /*
153 * Opened existing repo. Find 'volumes' node, should be
154 * the first child of the root node.
155 */
156 node = sif_node_first_child(sif_get_root(repo));
157
158 /* Verify it's the correct node type */
159 ntype = sif_node_get_type(node);
160 if (str_cmp(ntype, "volumes") != 0) {
161 rc = EIO;
162 goto error;
163 }
164
165 rc = vol_volumes_load(node, volumes);
166 if (rc != EOK)
167 goto error;
168 }
169
170 volumes->repo = repo;
171 *rvolumes = volumes;
172
173 return EOK;
174error:
175 if (trans != NULL)
176 sif_trans_abort(trans);
177 if (repo != NULL)
178 (void) sif_close(repo);
179 if (volumes != NULL)
180 free(volumes);
181
182 return rc;
183}
184
185/** Destroy list of volumes.
186 *
187 * @param volumes List of volumes or @c NULL
188 */
189void vol_volumes_destroy(vol_volumes_t *volumes)
190{
191 link_t *link;
192 vol_volume_t *volume;
193
194 if (volumes == NULL)
195 return;
196
197 link = list_first(&volumes->volumes);
198 while (link != NULL) {
199 volume = list_get_instance(link, vol_volume_t, lvolumes);
200
201 list_remove(&volume->lvolumes);
202 vol_volume_delete(volume);
203
204 link = list_first(&volumes->volumes);
205 }
206
207 free(volumes);
208}
209
210/** Add new volume structure to list of volumes.
211 *
212 * @param volumes List of volumes
213 * @param volume Volume structure
214 */
215static void vol_volume_add_locked(vol_volumes_t *volumes,
216 vol_volume_t *volume)
217{
218 assert(fibril_mutex_is_locked(&volumes->lock));
219 log_msg(LOG_DEFAULT, LVL_NOTE, "vol_volume_add_locked(%p)", volume);
220
221 volume->volumes = volumes;
222 list_append(&volume->lvolumes, &volumes->volumes);
223}
224
225/** Look up volume structure with locked volumes lock.
226 *
227 * If a matching existing volume is found, it is returned. Otherwise
228 * a new volume structure is created.
229 *
230 * @param volumes List of volumes
231 * @param label Volume label
232 * @param rvolume Place to store pointer to volume structure (existing or new)
233 *
234 * @return EOK on success, ENOMEM if out of memory
235 */
236static errno_t vol_volume_lookup_ref_locked(vol_volumes_t *volumes,
237 const char *label, vol_volume_t **rvolume)
238{
239 vol_volume_t *volume;
240
241 assert(fibril_mutex_is_locked(&volumes->lock));
242
243 list_foreach(volumes->volumes, lvolumes, vol_volume_t, volume) {
244 if (str_cmp(volume->label, label) == 0 &&
245 str_size(label) > 0) {
246 /* Add reference */
247 refcount_up(&volume->refcnt);
248 *rvolume = volume;
249 return EOK;
250 }
251 }
252
253 /* No existing volume found. Create a new one. */
254 volume = vol_volume_new();
255 if (volume == NULL)
256 return ENOMEM;
257
258 free(volume->label);
259 volume->label = str_dup(label);
260
261 if (volume->label == NULL) {
262 vol_volume_delete(volume);
263 return ENOMEM;
264 }
265
266 vol_volume_add_locked(volumes, volume);
267
268 *rvolume = volume;
269 return EOK;
270}
271
272/** Look up volume structure.
273 *
274 * If a matching existing volume is found, it is returned. Otherwise
275 * a new volume structure is created.
276 *
277 * @param volumes List of volumes
278 * @param label Volume label
279 * @param rvolume Place to store pointer to volume structure (existing or new)
280 *
281 * @return EOK on success, ENOMEM if out of memory
282 */
283errno_t vol_volume_lookup_ref(vol_volumes_t *volumes, const char *label,
284 vol_volume_t **rvolume)
285{
286 errno_t rc;
287
288 fibril_mutex_lock(&volumes->lock);
289 rc = vol_volume_lookup_ref_locked(volumes, label, rvolume);
290 fibril_mutex_unlock(&volumes->lock);
291
292 return rc;
293}
294
295/** Determine if volume has non-default settings that need to persist.
296 *
297 * @param volume Volume
298 * @return @c true iff volume has settings that need to persist
299 */
300static bool vol_volume_is_persist(vol_volume_t *volume)
301{
302 return str_size(volume->mountp) > 0;
303}
304
305/** Delete reference to volume.
306 *
307 * @param volume Volume
308 */
309void vol_volume_del_ref(vol_volume_t *volume)
310{
311 if (refcount_down(&volume->refcnt)) {
312 /* No more references. Check if volume is persistent. */
313 if (!vol_volume_is_persist(volume)) {
314 list_remove(&volume->lvolumes);
315 vol_volume_delete(volume);
316 }
317 }
318}
319
320/** Set volume mount point.
321 *
322 * @param volume Volume
323 * @param mountp Mount point
324 *
325 * @return EOK on success or error code
326 */
327errno_t vol_volume_set_mountp(vol_volume_t *volume, const char *mountp)
328{
329 char *mp;
330 char *old_mp;
331 errno_t rc;
332 sif_trans_t *trans = NULL;
333 sif_node_t *nvolume;
334
335 mp = str_dup(mountp);
336 if (mp == NULL)
337 return ENOMEM;
338
339 old_mp = volume->mountp;
340 volume->mountp = mp;
341
342 if (vol_volume_is_persist(volume)) {
343 /* Volume is now persistent */
344 if (volume->nvolume == NULL) {
345 /* Create volume node */
346 rc = sif_trans_begin(volume->volumes->repo, &trans);
347 if (rc != EOK)
348 goto error;
349
350 rc = sif_node_append_child(trans,
351 volume->volumes->nvolumes, "volume", &nvolume);
352 if (rc != EOK)
353 goto error;
354
355 rc = sif_node_set_attr(trans, nvolume, "label",
356 volume->label);
357 if (rc != EOK)
358 goto error;
359
360 rc = sif_node_set_attr(trans, nvolume, "mountp",
361 volume->mountp);
362 if (rc != EOK)
363 goto error;
364
365 rc = sif_trans_end(trans);
366 if (rc != EOK)
367 goto error;
368
369 trans = NULL;
370 volume->nvolume = nvolume;
371 } else {
372 /* Update volume node */
373 rc = sif_trans_begin(volume->volumes->repo, &trans);
374 if (rc != EOK)
375 goto error;
376
377 rc = sif_node_set_attr(trans, volume->nvolume,
378 "mountp", volume->mountp);
379 if (rc != EOK)
380 goto error;
381
382 rc = sif_trans_end(trans);
383 if (rc != EOK)
384 goto error;
385
386 trans = NULL;
387 }
388 } else {
389 /* Volume is now non-persistent */
390 if (volume->nvolume != NULL) {
391 /* Delete volume node */
392 rc = sif_trans_begin(volume->volumes->repo, &trans);
393 if (rc != EOK)
394 goto error;
395
396 sif_node_destroy(trans, volume->nvolume);
397
398 rc = sif_trans_end(trans);
399 if (rc != EOK)
400 goto error;
401
402 volume->nvolume = NULL;
403 }
404 }
405
406 free(old_mp);
407 return EOK;
408error:
409 free(mp);
410 volume->mountp = old_mp;
411
412 if (trans != NULL)
413 sif_trans_abort(trans);
414 return rc;
415}
416
417/** Load volumes from SIF repository.
418 *
419 * @param nvolumes Volumes node
420 * @param volumes Volumes object
421 *
422 * @return EOK on success or error code
423 */
424static errno_t vol_volumes_load(sif_node_t *nvolumes, vol_volumes_t *volumes)
425{
426 sif_node_t *nvolume;
427 vol_volume_t *volume = NULL;
428 const char *label;
429 const char *mountp;
430 errno_t rc;
431
432 volumes->nvolumes = nvolumes;
433
434 nvolume = sif_node_first_child(nvolumes);
435 while (nvolume != NULL) {
436 if (str_cmp(sif_node_get_type(nvolume), "volume") != 0) {
437 rc = EIO;
438 goto error;
439 }
440
441 volume = vol_volume_new();
442 if (volume == NULL) {
443 rc = ENOMEM;
444 goto error;
445 }
446
447 label = sif_node_get_attr(nvolume, "label");
448 mountp = sif_node_get_attr(nvolume, "mountp");
449
450 if (label == NULL || mountp == NULL) {
451 rc = EIO;
452 goto error;
453 }
454
455 free(volume->label);
456 free(volume->mountp);
457
458 volume->label = str_dup(label);
459 volume->mountp = str_dup(mountp);
460
461 volume->nvolume = nvolume;
462 fibril_mutex_lock(&volumes->lock);
463 vol_volume_add_locked(volumes, volume);
464 fibril_mutex_unlock(&volumes->lock);
465 nvolume = sif_node_next_child(nvolume);
466 }
467
468 return EOK;
469error:
470 if (volume != NULL)
471 vol_volume_delete(volume);
472 return rc;
473}
474
475/** @}
476 */
Note: See TracBrowser for help on using the repository browser.