chiark / gitweb /
[PATCH] update volume_id
[elogind.git] / extras / multipath-tools / libdevmapper / libdm-common.c
1 /*
2  * Copyright (C) 2001 Sistina Software (UK) Limited.
3  *
4  * This file is released under the LGPL.
5  */
6
7 #include "libdm-targets.h"
8 #include "libdm-common.h"
9 #include "list.h"
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <stdarg.h>
14 #include <string.h>
15 #include <sys/param.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
18 #include <errno.h>
19 #include <linux/dm-ioctl.h>
20 #include <linux/kdev_t.h>
21
22 #define DEV_DIR "/dev/"
23
24 static char _dm_dir[PATH_MAX] = DEV_DIR DM_DIR;
25
26 static int _verbose = 0;
27
28 /*
29  * Library users can provide their own logging
30  * function.
31  */
32 static void _default_log(int level, const char *file, int line,
33                          const char *f, ...)
34 {
35         va_list ap;
36
37         if (level > _LOG_WARN && !_verbose)
38                 return;
39
40         va_start(ap, f);
41
42         if (level < _LOG_WARN)
43                 vfprintf(stderr, f, ap);
44         else
45                 vprintf(f, ap);
46
47         va_end(ap);
48
49         if (level < _LOG_WARN)
50                 fprintf(stderr, "\n");
51         else
52                 fprintf(stdout, "\n");
53 }
54
55 dm_log_fn _log = _default_log;
56
57 void dm_log_init(dm_log_fn fn)
58 {
59         _log = fn;
60 }
61
62 void dm_log_init_verbose(int level)
63 {
64         _verbose = level;
65 }
66
67 static void _build_dev_path(char *buffer, size_t len, const char *dev_name)
68 {
69         /* If there's a /, assume caller knows what they're doing */
70         if (strchr(dev_name, '/'))
71                 snprintf(buffer, len, "%s", dev_name);
72         else
73                 snprintf(buffer, len, "%s/%s", _dm_dir, dev_name);
74 }
75
76 int dm_get_library_version(char *version, size_t size)
77 {
78         strncpy(version, DM_LIB_VERSION, size);
79         return 1;
80 }
81
82 struct dm_task *dm_task_create(int type)
83 {
84         struct dm_task *dmt = malloc(sizeof(*dmt));
85
86         if (!dm_check_version())
87                 return NULL;
88
89         if (!dmt) {
90                 log_error("dm_task_create: malloc(%d) failed", sizeof(*dmt));
91                 return NULL;
92         }
93
94         memset(dmt, 0, sizeof(*dmt));
95
96         dmt->type = type;
97         dmt->minor = -1;
98         dmt->major = -1;
99
100         return dmt;
101 }
102
103 int dm_task_set_name(struct dm_task *dmt, const char *name)
104 {
105         char *pos;
106         char path[PATH_MAX];
107         struct stat st1, st2;
108
109         if (dmt->dev_name) {
110                 free(dmt->dev_name);
111                 dmt->dev_name = NULL;
112         }
113
114         /* If path was supplied, remove it if it points to the same device
115          * as its last component.
116          */
117         if ((pos = strrchr(name, '/'))) {
118                 snprintf(path, sizeof(path), "%s/%s", _dm_dir, pos + 1);
119
120                 if (stat(name, &st1) || stat(path, &st2) ||
121                     !(st1.st_dev == st2.st_dev)) {
122                         log_error("dm_task_set_name: Device %s not found",
123                                   name);
124                         return 0;
125                 }
126
127                 name = pos + 1;
128         }
129
130         if (!(dmt->dev_name = strdup(name))) {
131                 log_error("dm_task_set_name: strdup(%s) failed", name);
132                 return 0;
133         }
134
135         return 1;
136 }
137
138 int dm_task_set_uuid(struct dm_task *dmt, const char *uuid)
139 {
140         if (dmt->uuid) {
141                 free(dmt->uuid);
142                 dmt->uuid = NULL;
143         }
144
145         if (!(dmt->uuid = strdup(uuid))) {
146                 log_error("dm_task_set_uuid: strdup(%s) failed", uuid);
147                 return 0;
148         }
149
150         return 1;
151 }
152
153 int dm_task_set_major(struct dm_task *dmt, int major)
154 {
155         dmt->major = major;
156         log_debug("Setting major: %d", dmt->major);
157
158         return 1;
159 }
160
161 int dm_task_set_minor(struct dm_task *dmt, int minor)
162 {
163         dmt->minor = minor;
164         log_debug("Setting minor: %d", dmt->minor);
165
166         return 1;
167 }
168
169 int dm_task_add_target(struct dm_task *dmt, uint64_t start, uint64_t size,
170                        const char *ttype, const char *params)
171 {
172         struct target *t = create_target(start, size, ttype, params);
173
174         if (!t)
175                 return 0;
176
177         if (!dmt->head)
178                 dmt->head = dmt->tail = t;
179         else {
180                 dmt->tail->next = t;
181                 dmt->tail = t;
182         }
183
184         return 1;
185 }
186
187 static int _add_dev_node(const char *dev_name, uint32_t major, uint32_t minor)
188 {
189         char path[PATH_MAX];
190         struct stat info;
191         dev_t dev = MKDEV(major, minor);
192
193         _build_dev_path(path, sizeof(path), dev_name);
194
195         if (stat(path, &info) >= 0) {
196                 if (!S_ISBLK(info.st_mode)) {
197                         log_error("A non-block device file at '%s' "
198                                   "is already present", path);
199                         return 0;
200                 }
201
202                 if (info.st_rdev == dev)
203                         return 1;
204
205                 if (unlink(path) < 0) {
206                         log_error("Unable to unlink device node for '%s'",
207                                   dev_name);
208                         return 0;
209                 }
210         }
211
212         if (mknod(path, S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, dev) < 0) {
213                 log_error("Unable to make device node for '%s'", dev_name);
214                 return 0;
215         }
216
217         return 1;
218 }
219
220 static int _rename_dev_node(const char *old_name, const char *new_name)
221 {
222         char oldpath[PATH_MAX];
223         char newpath[PATH_MAX];
224         struct stat info;
225
226         _build_dev_path(oldpath, sizeof(oldpath), old_name);
227         _build_dev_path(newpath, sizeof(newpath), new_name);
228
229         if (stat(newpath, &info) == 0) {
230                 if (!S_ISBLK(info.st_mode)) {
231                         log_error("A non-block device file at '%s' "
232                                   "is already present", newpath);
233                         return 0;
234                 }
235
236                 if (unlink(newpath) < 0) {
237                         if (errno == EPERM) {
238                                 /* devfs, entry has already been renamed */
239                                 return 1;
240                         }
241                         log_error("Unable to unlink device node for '%s'",
242                                   new_name);
243                         return 0;
244                 }
245         }
246
247         if (rename(oldpath, newpath) < 0) {
248                 log_error("Unable to rename device node from '%s' to '%s'",
249                           old_name, new_name);
250                 return 0;
251         }
252
253         return 1;
254 }
255
256 static int _rm_dev_node(const char *dev_name)
257 {
258         char path[PATH_MAX];
259         struct stat info;
260
261         _build_dev_path(path, sizeof(path), dev_name);
262
263         if (stat(path, &info) < 0)
264                 return 1;
265
266         if (unlink(path) < 0) {
267                 log_error("Unable to unlink device node for '%s'", dev_name);
268                 return 0;
269         }
270
271         return 1;
272 }
273
274 typedef enum {
275         NODE_ADD,
276         NODE_DEL,
277         NODE_RENAME
278 } node_op_t;
279
280 static int _do_node_op(node_op_t type, const char *dev_name, uint32_t major,
281                        uint32_t minor, const char *old_name)
282 {
283         switch (type) {
284         case NODE_ADD:
285                 return _add_dev_node(dev_name, major, minor);
286         case NODE_DEL:
287                 return _rm_dev_node(dev_name);
288         case NODE_RENAME:
289                 return _rename_dev_node(old_name, dev_name);
290         }
291
292         return 1;
293 }
294
295 static LIST_INIT(_node_ops);
296
297 struct node_op_parms {
298         struct list list;
299         node_op_t type;
300         char *dev_name;
301         uint32_t major;
302         uint32_t minor;
303         char *old_name;
304         char names[0];
305 };
306
307 static void _store_str(char **pos, char **ptr, const char *str)
308 {
309         strcpy(*pos, str);
310         *ptr = *pos;
311         *pos += strlen(*ptr) + 1;
312 }
313
314 static int _stack_node_op(node_op_t type, const char *dev_name, uint32_t major,
315                           uint32_t minor, const char *old_name)
316 {
317         struct node_op_parms *nop;
318         size_t len = strlen(dev_name) + strlen(old_name) + 2;
319         char *pos;
320
321         if (!(nop = malloc(sizeof(*nop) + len))) {
322                 log_error("Insufficient memory to stack mknod operation");
323                 return 0;
324         }
325
326         pos = nop->names;
327         nop->type = type;
328         nop->major = major;
329         nop->minor = minor;
330
331         _store_str(&pos, &nop->dev_name, dev_name);
332         _store_str(&pos, &nop->old_name, old_name);
333
334         list_add(&_node_ops, &nop->list);
335
336         return 1;
337 }
338
339 static void _pop_node_ops(void)
340 {
341         struct list *noph, *nopht;
342         struct node_op_parms *nop;
343
344         list_iterate_safe(noph, nopht, &_node_ops) {
345                 nop = list_item(noph, struct node_op_parms);
346                 _do_node_op(nop->type, nop->dev_name, nop->major, nop->minor,
347                             nop->old_name);
348                 list_del(&nop->list);
349                 free(nop);
350         }
351 }
352
353 int add_dev_node(const char *dev_name, uint32_t major, uint32_t minor)
354 {
355         return _stack_node_op(NODE_ADD, dev_name, major, minor, "");
356 }
357
358 int rename_dev_node(const char *old_name, const char *new_name)
359 {
360         return _stack_node_op(NODE_RENAME, new_name, 0, 0, old_name);
361 }
362
363 int rm_dev_node(const char *dev_name)
364 {
365         return _stack_node_op(NODE_DEL, dev_name, 0, 0, "");
366 }
367
368 void update_devs(void)
369 {
370         _pop_node_ops();
371 }
372
373 int dm_set_dev_dir(const char *dir)
374 {
375         snprintf(_dm_dir, sizeof(_dm_dir), "%s%s", dir, DM_DIR);
376         return 1;
377 }
378
379 const char *dm_dir(void)
380 {
381         return _dm_dir;
382 }