nrelease - fix/improve livecd
[dragonfly.git] / contrib / lvm2 / dist / lib / activate / fs.c
1 /*      $NetBSD: fs.c,v 1.4 2009/12/02 00:58:03 haad Exp $      */
2
3 /*
4  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
5  * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
6  *
7  * This file is part of LVM2.
8  *
9  * This copyrighted material is made available to anyone wishing to use,
10  * modify, copy, or redistribute it subject to the terms and conditions
11  * of the GNU Lesser General Public License v.2.1.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, write to the Free Software Foundation,
15  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17
18 #include "lib.h"
19 #include "fs.h"
20 #include "toolcontext.h"
21 #include "lvm-string.h"
22 #include "lvm-file.h"
23 #include "memlock.h"
24
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <limits.h>
29 #include <dirent.h>
30
31 static int _mk_dir(const char *dev_dir, const char *vg_name)
32 {
33         char vg_path[PATH_MAX];
34         mode_t old_umask;
35
36         if (dm_snprintf(vg_path, sizeof(vg_path), "%s%s",
37                          dev_dir, vg_name) == -1) {
38                 log_error("Couldn't construct name of volume "
39                           "group directory.");
40                 return 0;
41         }
42
43         if (dir_exists(vg_path))
44                 return 1;
45
46         log_very_verbose("Creating directory %s", vg_path);
47
48         old_umask = umask(DM_DEV_DIR_UMASK);
49         if (mkdir(vg_path, 0777)) {
50                 log_sys_error("mkdir", vg_path);
51                 umask(old_umask);
52                 return 0;
53         }
54         umask(old_umask);
55
56         return 1;
57 }
58
59 static int _rm_dir(const char *dev_dir, const char *vg_name)
60 {
61         char vg_path[PATH_MAX];
62
63         if (dm_snprintf(vg_path, sizeof(vg_path), "%s%s",
64                          dev_dir, vg_name) == -1) {
65                 log_error("Couldn't construct name of volume "
66                           "group directory.");
67                 return 0;
68         }
69
70         if (dir_exists(vg_path) && is_empty_dir(vg_path)) {
71                 log_very_verbose("Removing directory %s", vg_path);
72                 rmdir(vg_path);
73         }
74
75         return 1;
76 }
77
78 static void _rm_blks(const char *dir)
79 {
80         const char *name;
81         char path[PATH_MAX];
82         struct dirent *dirent;
83         struct stat buf;
84         DIR *d;
85
86         if (!(d = opendir(dir))) {
87                 log_sys_error("opendir", dir);
88                 return;
89         }
90
91         while ((dirent = readdir(d))) {
92                 name = dirent->d_name;
93
94                 if (!strcmp(name, ".") || !strcmp(name, ".."))
95                         continue;
96
97                 if (dm_snprintf(path, sizeof(path), "%s/%s", dir, name) == -1) {
98                         log_error("Couldn't create path for %s", name);
99                         continue;
100                 }
101
102                 if (!lstat(path, &buf)) {
103                         if (!S_ISBLK(buf.st_mode))
104                                 continue;
105                         log_very_verbose("Removing %s", path);
106                         if (unlink(path) < 0)
107                                 log_sys_error("unlink", path);
108                 }
109 #ifdef __NetBSD__
110                 if (dm_snprintf(path, sizeof(path), "%s/r%s", dir, name) == -1) {
111                         log_error("Couldn't create path for r%s", name);
112                         continue;
113                 }
114
115                 if (!lstat(path, &buf)) {
116                         if (!S_ISCHR(buf.st_mode))
117                                 continue;
118                         log_very_verbose("Removing %s", path);
119                         if (unlink(path) < 0)
120                                 log_sys_error("unlink", path);
121                 }
122 #endif          
123         }
124 }
125
126 static int _mk_link(const char *dev_dir, const char *vg_name,
127                     const char *lv_name, const char *dev)
128 {
129         char lv_path[PATH_MAX], link_path[PATH_MAX], lvm1_group_path[PATH_MAX];
130         char vg_path[PATH_MAX];
131         struct stat buf, buf_lp;
132
133 #ifdef __NetBSD__
134         /* Add support for creating links to BSD raw devices */
135         char raw_lv_path[PATH_MAX], raw_link_path[PATH_MAX];
136 #endif  
137
138         if (dm_snprintf(vg_path, sizeof(vg_path), "%s%s",
139                          dev_dir, vg_name) == -1) {
140                 log_error("Couldn't create path for volume group dir %s",
141                           vg_name);
142                 return 0;
143         }
144
145 #ifdef __NetBSD__
146         if (dm_snprintf(raw_lv_path, sizeof(raw_lv_path), "%s/r%s", vg_path,
147                 lv_name) == -1) {
148                 log_error("Couldn't create source pathname for "
149                     "logical volume link r%s", lv_name);
150                 return 0;
151         }
152
153         if (dm_snprintf(raw_link_path, sizeof(raw_link_path), "%s/r%s",
154                 dm_dir(), dev) == -1) {
155                 log_error("Couldn't create destination pathname for "
156                     "logical volume link for %s", lv_name);
157                 return 0;
158         }
159
160         if (!lstat(raw_lv_path, &buf)) {
161                 if (!S_ISLNK(buf.st_mode) && !S_ISCHR(buf.st_mode)) {
162                         log_error("Symbolic link %s not created: file exists",
163                                   raw_link_path);
164                         return 0;
165                 }
166
167                 log_very_verbose("Removing %s", raw_lv_path);
168                 if (unlink(raw_lv_path) < 0) {
169                         log_sys_error("unlink", raw_lv_path);
170                         return 0;
171                 }
172         }
173
174         log_very_verbose("Linking %s -> %s", raw_lv_path, raw_link_path);
175         if (symlink(raw_link_path, raw_lv_path) < 0) {
176                 log_sys_error("symlink", raw_lv_path);
177                 return 0;
178         }
179         
180 #endif  
181         if (dm_snprintf(lv_path, sizeof(lv_path), "%s/%s", vg_path,
182                          lv_name) == -1) {
183                 log_error("Couldn't create source pathname for "
184                           "logical volume link %s", lv_name);
185                 return 0;
186         }
187
188         if (dm_snprintf(link_path, sizeof(link_path), "%s/%s",
189                          dm_dir(), dev) == -1) {
190                 log_error("Couldn't create destination pathname for "
191                           "logical volume link for %s", lv_name);
192                 return 0;
193         }
194
195         if (dm_snprintf(lvm1_group_path, sizeof(lvm1_group_path), "%s/group",
196                          vg_path) == -1) {
197                 log_error("Couldn't create pathname for LVM1 group file for %s",
198                           vg_name);
199                 return 0;
200         }
201
202         /* To reach this point, the VG must have been locked.
203          * As locking fails if the VG is active under LVM1, it's
204          * now safe to remove any LVM1 devices we find here
205          * (as well as any existing LVM2 symlink). */
206         if (!lstat(lvm1_group_path, &buf)) {
207                 if (!S_ISCHR(buf.st_mode)) {
208                         log_error("Non-LVM1 character device found at %s",
209                                   lvm1_group_path);
210                 } else {
211                         _rm_blks(vg_path);
212
213                         log_very_verbose("Removing %s", lvm1_group_path);
214                         if (unlink(lvm1_group_path) < 0)
215                                 log_sys_error("unlink", lvm1_group_path);
216                 }
217         }
218
219         if (!lstat(lv_path, &buf)) {
220                 if (!S_ISLNK(buf.st_mode) && !S_ISBLK(buf.st_mode)) {
221                         log_error("Symbolic link %s not created: file exists",
222                                   link_path);
223                         return 0;
224                 }
225
226                 if (dm_udev_get_sync_support()) {
227                         /* Check udev created the correct link. */
228                         if (!stat(link_path, &buf_lp) &&
229                             !stat(lv_path, &buf)) {
230                                 if (buf_lp.st_rdev == buf.st_rdev)
231                                         return 1;
232                                 else
233                                         log_warn("Symlink %s that should have been "
234                                                  "created by udev does not have "
235                                                  "correct target. Falling back to "
236                                                  "direct link creation", lv_path);
237                         } else
238                                 log_warn("Symlink %s that should have been "
239                                          "created by udev could not be checked "
240                                          "for its correctness. Falling back to "
241                                          "direct link creation.", lv_path);
242
243                 }
244
245                 log_very_verbose("Removing %s", lv_path);
246                 if (unlink(lv_path) < 0) {
247                         log_sys_error("unlink", lv_path);
248                         return 0;
249                 }
250         } else if (dm_udev_get_sync_support())
251                 log_warn("The link %s should had been created by udev "
252                           "but it was not found. Falling back to "
253                           "direct link creation.", lv_path);
254
255         log_very_verbose("Linking %s -> %s", lv_path, link_path);
256         if (symlink(link_path, lv_path) < 0) {
257                 log_sys_error("symlink", lv_path);
258                 return 0;
259         }
260
261         if (!dm_set_selinux_context(lv_path, S_IFLNK))
262                 return_0;
263
264         return 1;
265 }
266
267 static int _rm_link(const char *dev_dir, const char *vg_name,
268                     const char *lv_name)
269 {
270         struct stat buf;
271         char lv_path[PATH_MAX];
272
273 #ifdef __NetBSD__
274         /* Add support for removing links to BSD raw devices */
275         char raw_lv_path[PATH_MAX];
276
277         if (dm_snprintf(raw_lv_path, sizeof(raw_lv_path), "%s%s/r%s",
278                          dev_dir, vg_name, lv_name) == -1) {
279                 log_error("Couldn't determine link pathname.");
280                 return 0;
281         }
282
283         if (lstat(raw_lv_path, &buf) || !S_ISLNK(buf.st_mode)) {
284                 if (errno == ENOENT)
285                         return 1;
286                 log_error("%s not symbolic link - not removing", raw_lv_path);
287                 return 0;
288         }
289
290         log_very_verbose("Removing link %s", raw_lv_path);
291         if (unlink(raw_lv_path) < 0) {
292                 log_sys_error("unlink", raw_lv_path);
293                 return 0;
294         }
295 #endif
296         if (dm_snprintf(lv_path, sizeof(lv_path), "%s%s/%s",
297                          dev_dir, vg_name, lv_name) == -1) {
298                 log_error("Couldn't determine link pathname.");
299                 return 0;
300         }
301
302         if (lstat(lv_path, &buf) && errno == ENOENT)
303                 return 1;
304         else if (dm_udev_get_sync_support())
305                 log_warn("The link %s should have been removed by udev "
306                          "but it is still present. Falling back to "
307                          "direct link removal.", lv_path);
308
309         if (!S_ISLNK(buf.st_mode)) {
310                 log_error("%s not symbolic link - not removing", lv_path);
311                 return 0;
312         }
313
314         log_very_verbose("Removing link %s", lv_path);
315         if (unlink(lv_path) < 0) {
316                 log_sys_error("unlink", lv_path);
317                 return 0;
318         }
319
320         return 1;
321 }
322
323 typedef enum {
324         FS_ADD,
325         FS_DEL,
326         FS_RENAME
327 } fs_op_t;
328
329 static int _do_fs_op(fs_op_t type, const char *dev_dir, const char *vg_name,
330                      const char *lv_name, const char *dev,
331                      const char *old_lv_name)
332 {
333         switch (type) {
334         case FS_ADD:
335                 if (!_mk_dir(dev_dir, vg_name) ||
336                     !_mk_link(dev_dir, vg_name, lv_name, dev))
337                         return_0;
338                 break;
339         case FS_DEL:
340                 if (!_rm_link(dev_dir, vg_name, lv_name) ||
341                     !_rm_dir(dev_dir, vg_name))
342                         return_0;
343                 break;
344                 /* FIXME Use rename() */
345         case FS_RENAME:
346                 if (old_lv_name && !_rm_link(dev_dir, vg_name, old_lv_name))
347                         stack;
348
349                 if (!_mk_link(dev_dir, vg_name, lv_name, dev))
350                         stack;
351         }
352
353         return 1;
354 }
355
356 static DM_LIST_INIT(_fs_ops);
357
358 struct fs_op_parms {
359         struct dm_list list;
360         fs_op_t type;
361         char *dev_dir;
362         char *vg_name;
363         char *lv_name;
364         char *dev;
365         char *old_lv_name;
366         char names[0];
367 };
368
369 static void _store_str(char **pos, char **ptr, const char *str)
370 {
371         strcpy(*pos, str);
372         *ptr = *pos;
373         *pos += strlen(*ptr) + 1;
374 }
375
376 static int _stack_fs_op(fs_op_t type, const char *dev_dir, const char *vg_name,
377                         const char *lv_name, const char *dev,
378                         const char *old_lv_name)
379 {
380         struct fs_op_parms *fsp;
381         size_t len = strlen(dev_dir) + strlen(vg_name) + strlen(lv_name) +
382             strlen(dev) + strlen(old_lv_name) + 5;
383         char *pos;
384
385         if (!(fsp = dm_malloc(sizeof(*fsp) + len))) {
386                 log_error("No space to stack fs operation");
387                 return 0;
388         }
389
390         pos = fsp->names;
391         fsp->type = type;
392
393         _store_str(&pos, &fsp->dev_dir, dev_dir);
394         _store_str(&pos, &fsp->vg_name, vg_name);
395         _store_str(&pos, &fsp->lv_name, lv_name);
396         _store_str(&pos, &fsp->dev, dev);
397         _store_str(&pos, &fsp->old_lv_name, old_lv_name);
398
399         dm_list_add(&_fs_ops, &fsp->list);
400
401         return 1;
402 }
403
404 static void _pop_fs_ops(void)
405 {
406         struct dm_list *fsph, *fspht;
407         struct fs_op_parms *fsp;
408
409         dm_list_iterate_safe(fsph, fspht, &_fs_ops) {
410                 fsp = dm_list_item(fsph, struct fs_op_parms);
411                 _do_fs_op(fsp->type, fsp->dev_dir, fsp->vg_name, fsp->lv_name,
412                           fsp->dev, fsp->old_lv_name);
413                 dm_list_del(&fsp->list);
414                 dm_free(fsp);
415         }
416 }
417
418 static int _fs_op(fs_op_t type, const char *dev_dir, const char *vg_name,
419                   const char *lv_name, const char *dev, const char *old_lv_name)
420 {
421         if (memlock()) {
422                 if (!_stack_fs_op(type, dev_dir, vg_name, lv_name, dev,
423                                   old_lv_name))
424                         return_0;
425                 return 1;
426         }
427
428         return _do_fs_op(type, dev_dir, vg_name, lv_name, dev, old_lv_name);
429 }
430
431 int fs_add_lv(const struct logical_volume *lv, const char *dev)
432 {
433         return _fs_op(FS_ADD, lv->vg->cmd->dev_dir, lv->vg->name, lv->name,
434                       dev, "");
435 }
436
437 int fs_del_lv(const struct logical_volume *lv)
438 {
439         return _fs_op(FS_DEL, lv->vg->cmd->dev_dir, lv->vg->name, lv->name,
440                       "", "");
441 }
442
443 int fs_del_lv_byname(const char *dev_dir, const char *vg_name, const char *lv_name)
444 {
445         return _fs_op(FS_DEL, dev_dir, vg_name, lv_name, "", "");
446 }
447
448 int fs_rename_lv(struct logical_volume *lv, const char *dev, 
449                 const char *old_vgname, const char *old_lvname)
450 {
451         if (strcmp(old_vgname, lv->vg->name)) {
452                 return
453                         (_fs_op(FS_DEL, lv->vg->cmd->dev_dir, old_vgname, old_lvname, "", "") &&
454                          _fs_op(FS_ADD, lv->vg->cmd->dev_dir, lv->vg->name, lv->name, dev, ""));
455         }
456         else 
457                 return _fs_op(FS_RENAME, lv->vg->cmd->dev_dir, lv->vg->name, lv->name,
458                               dev, old_lvname);
459 }
460
461 void fs_unlock(void)
462 {
463         if (!memlock()) {
464                 dm_lib_release();
465                 _pop_fs_ops();
466         }
467 }