xref: /dragonfly/contrib/lvm2/dist/lib/activate/fs.c (revision 3851e4b8)
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 }
468