Whamcloud - gitweb
afba909b5728abdfab0eefd7a926bf7949750668
[tools/e2fsprogs.git] / lib / blkid / dev.c
1 /*
2  * dev.c - allocation/initialization/free routines for dev
3  *
4  * Copyright (C) 2001 Andreas Dilger
5  *
6  * %Begin-Header%
7  * This file may be redistributed under the terms of the
8  * GNU Lesser General Public License.
9  * %End-Header%
10  */
11
12 #include <stdlib.h>
13 #include <string.h>
14
15 #include "blkidP.h"
16
17 #ifdef DEBUG_DEV
18 #include <stdio.h>
19 #define DBG(x)  x
20 #else
21 #define DBG(x)
22 #endif
23
24 blkid_dev blkid_new_dev(void)
25 {
26         blkid_dev dev;
27
28         if (!(dev = (blkid_dev) calloc(1, sizeof(struct blkid_struct_dev))))
29                 return NULL;
30
31         INIT_LIST_HEAD(&dev->bid_devs);
32         INIT_LIST_HEAD(&dev->bid_tags);
33
34         return dev;
35 }
36
37 void blkid_free_dev(blkid_dev dev)
38 {
39         if (!dev)
40                 return;
41
42         DBG(printf("  freeing dev %s (%s)\n", dev->bid_name, dev->bid_type));
43         DEB_DUMP_DEV(dev);
44
45         list_del(&dev->bid_devs);
46         while (!list_empty(&dev->bid_tags)) {
47                 blkid_tag tag = list_entry(dev->bid_tags.next,
48                                            struct blkid_struct_tag,
49                                            bit_tags);
50                 blkid_free_tag(tag);
51         }
52         if (dev->bid_name)
53                 string_free(dev->bid_name);
54         free(dev);
55 }
56
57 /*
58  * This is kind of ugly, but I want to be able to compare two strings in
59  * several different ways.  For example, in some cases, if both strings
60  * are NULL, those would be considered different, but in other cases
61  * they would be considered the same.  Hence the ugliness.
62  *
63  * Use as:      "ret == SC_SAME" if both strings exist and are equal
64  *                              this is equivalent to "!(ret & SC_DIFF)"
65  *              "ret & SC_SAME" if both strings being NULL is also equal
66  *                              this is equivalent to "!(ret == SC_DIFF)"
67  *              "ret == SC_DIFF" if both strings exist and are different
68  *                              this is equivalent to "!(ret & SC_SAME)"
69  *              "ret & SC_DIFF" if both strings being NULL is also different
70  *                              this is equivalent to "!(ret == SC_SAME)"
71  *              "ret == SC_NONE" to see if both strings do not exist
72  */
73 #define SC_DIFF 0x0001
74 #define SC_NONE 0x0003
75 #define SC_SAME 0x0002
76
77 static int string_compare(char *s1, char *s2)
78 {
79         if (!s1 && !s2)
80                 return SC_NONE;
81
82         if (!s1 || !s2)
83                 return SC_DIFF;
84
85         if (strcmp(s1, s2))
86                 return SC_DIFF;
87
88         return SC_SAME;
89 }
90
91 /*
92  * Add a tag to the global cache tag list.
93  */
94 static int add_tag_to_cache(blkid_cache cache, blkid_tag tag)
95 {
96         blkid_tag head = NULL;
97
98         if (!cache || !tag)
99                 return 0;
100
101         DBG(printf("    adding tag %s=%s to cache\n", tag->bit_name, tag->bit_val));
102
103         if (!(head = blkid_find_head_cache(cache, tag))) {
104                 head = blkid_new_tag();
105                 if (!head)
106                         return -BLKID_ERR_MEM;
107
108                 DBG(printf("    creating new cache tag head %s\n",tag->bit_name));
109                 head->bit_name = string_copy(tag->bit_name);
110                 if (!head->bit_name) {
111                         blkid_free_tag(head);
112                         return -BLKID_ERR_MEM;
113                 }
114
115                 list_add_tail(&head->bit_tags, &cache->bic_tags);
116         }
117
118         /* Add this tag to global list */
119         list_add_tail(&tag->bit_names, &head->bit_names);
120
121         return 0;
122 }
123
124 /*
125  * Given a blkid device, return its name
126  */
127 extern const char *blkid_devname_name(blkid_dev dev)
128 {
129         return dev->bid_name;
130 }
131
132 /*
133  * dev iteration routines for the public libblkid interface.
134  *
135  * These routines do not expose the list.h implementation, which are a
136  * contamination of the namespace, and which force us to reveal far, far
137  * too much of our internal implemenation.  I'm not convinced I want
138  * to keep list.h in the long term, anyway.  It's fine for kernel
139  * programming, but performance is not the #1 priority for this
140  * library, and I really don't like the tradeoff of type-safety for
141  * performance for this application.  [tytso:20030125.2007EST]
142  */
143
144 /*
145  * This series of functions iterate over all devices in a blkid cache
146  */
147 #define DEV_ITERATE_MAGIC       0x01a5284c
148         
149 struct blkid_struct_dev_iterate {
150         int                     magic;
151         blkid_cache             cache;
152         struct list_head        *p;
153 };
154
155 extern blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache)
156 {
157         blkid_dev_iterate       iter;
158
159         iter = malloc(sizeof(struct blkid_struct_dev_iterate));
160         if (iter) {
161                 iter->magic = DEV_ITERATE_MAGIC;
162                 iter->cache = cache;
163                 iter->p = cache->bic_devs.next;
164         }
165         return (iter);
166 }
167
168 /*
169  * Return 0 on success, -1 on error
170  */
171 extern int blkid_dev_next(blkid_dev_iterate iter,
172                           blkid_dev *dev)
173 {
174         *dev = 0;
175         if (!iter || iter->magic != DEV_ITERATE_MAGIC ||
176             iter->p == &iter->cache->bic_devs)
177                 return -1;
178         *dev = list_entry(iter->p, struct blkid_struct_dev, bid_devs);
179         iter->p = iter->p->next;
180         return 0;
181 }
182
183 extern void blkid_dev_iterate_end(blkid_dev_iterate iter)
184 {
185         if (!iter || iter->magic != DEV_ITERATE_MAGIC)
186                 return;
187         iter->magic = 0;
188         free(iter);
189 }
190
191 /*
192  * Add a device to the global cache list, along with all its tags.
193  */
194 blkid_dev blkid_add_dev_to_cache(blkid_cache cache, blkid_dev dev)
195 {
196         struct list_head *p;
197
198         if (!cache || !dev)
199                 return dev;
200
201         if (!dev->bid_id)
202                 dev->bid_id = ++(cache->bic_idmax);
203
204         list_for_each(p, &cache->bic_devs) {
205                 blkid_dev odev = list_entry(p, struct blkid_struct_dev, bid_devs);
206                 int dup_uuid, dup_label, dup_name, dup_type;
207
208                 dup_name = string_compare(odev->bid_name, dev->bid_name);
209                 dup_label = string_compare(odev->bid_label, dev->bid_label);
210                 dup_uuid = string_compare(odev->bid_uuid, dev->bid_uuid);
211
212                 if (odev->bid_id == dev->bid_id)
213                         dev->bid_id = ++(cache->bic_idmax);
214
215                 /* Fields different, do nothing (check more fields?) */
216                 if ((dup_name & SC_DIFF) && (dup_uuid & SC_DIFF) &&
217                     (dup_label & SC_DIFF))
218                         continue;
219
220                 /* We can't simply allow duplicate fields if the bid_type is
221                  * different, given that a filesystem may change from ext2
222                  * to ext3 but it will have the same UUID and LABEL fields.
223                  * We need to discard the old cache entry in this case.
224                  */
225
226                 /* If the UUIDs are the same but one is unverified discard it */
227                 if (dup_uuid == SC_SAME) {
228                         DBG(printf("  duplicate uuid %s\n", dev->bid_uuid));
229                         if (!(odev->bid_flags & BLKID_BID_FL_VERIFIED)) {
230                                 dev->bid_id = odev->bid_id; /* keep old id */
231                                 blkid_free_dev(odev);
232                                 goto exit_new;
233                         } else if (!(dev->bid_flags & BLKID_BID_FL_VERIFIED)) {
234                                 blkid_free_dev(dev);
235                                 dev = odev;
236                                 goto exit_old;
237                         }
238
239                         /* This shouldn't happen */
240                         fprintf(stderr, "blkid: same UUID for %s and %s\n",
241                                 dev->bid_name, odev->bid_name);
242                 }
243
244                 /* If the device name is the same, discard one of them
245                  * (prefer one that has been validated, or the first one).
246                  */
247                 if (dup_name == SC_SAME) {
248                         DBG(printf("  duplicate devname %s\n", dev->bid_name));
249                         if (odev->bid_flags & BLKID_BID_FL_VERIFIED ||
250                             !(dev->bid_flags & BLKID_BID_FL_VERIFIED)) {
251                                 if ((dup_uuid & SC_SAME) &&
252                                     (dup_label & SC_SAME))      /* use old id */
253                                         dev->bid_id = odev->bid_id;
254                                 blkid_free_dev(dev);
255                                 dev = odev;
256                                 goto exit_old;
257                         } else {
258                                 blkid_free_dev(odev);
259                                 goto exit_new;
260                         }
261                 }
262
263                 dup_type = string_compare(odev->bid_type, dev->bid_type);
264
265                 if (dup_label == SC_SAME && dup_type == SC_SAME) {
266                         DBG(printf("  duplicate label %s\n", dev->bid_label));
267                         if (!(odev->bid_flags & BLKID_BID_FL_VERIFIED)) {
268                                 blkid_free_dev(odev);
269                                 goto exit_new;
270                         } else if (!(dev->bid_flags & BLKID_BID_FL_VERIFIED)) {
271                                 blkid_free_dev(dev);
272                                 dev = odev;
273                                 goto exit_old;
274                         }
275                         fprintf(stderr, "blkid: same LABEL for %s and %s\n",
276                                 dev->bid_name, odev->bid_name);
277                 }
278         }
279
280 exit_new:
281         DBG(printf("  adding new devname %s to cache\n", dev->bid_name));
282
283         cache->bic_flags |= BLKID_BIC_FL_CHANGED;
284
285         list_add_tail(&dev->bid_devs, &cache->bic_devs);
286         list_for_each(p, &dev->bid_tags) {
287                 blkid_tag tag = list_entry(p, struct blkid_struct_tag, 
288                                            bit_tags);
289                 add_tag_to_cache(cache, tag);
290         }
291         return dev;
292
293 exit_old:
294         DBG(printf("  using old devname %s from cache\n", dev->bid_name));
295         return dev;
296 }
297
298 #ifdef TEST_PROGRAM
299 int main(int argc, char** argv)
300 {
301         blkid_cache cache;
302         blkid_dev dev, newdev;
303
304         if ((argc != 3)) {
305                 fprintf(stderr, "Usage:\t%s dev1 dev2\n"
306                         "Test that adding the same device to the cache fails\n",
307                         argv[0]);
308                 exit(1);
309         }
310
311         cache = blkid_new_cache();
312         if (!cache) {
313                 perror(argv[0]);
314                 return 1;
315         }
316         dev = blkid_devname_to_dev(argv[1], 0);
317         newdev = blkid_add_dev_to_cache(cache, dev);
318         if (newdev != dev)
319                 printf("devices changed for %s (unexpected)\n", argv[1]);
320         dev = blkid_devname_to_dev(argv[2], 0);
321         newdev = blkid_add_dev_to_cache(cache, dev);
322         if (newdev != dev)
323                 printf("devices changed for %s (unexpected)\n", argv[2]);
324         dev = blkid_devname_to_dev(argv[2], 0);
325         newdev = blkid_add_dev_to_cache(cache, dev);
326         if (newdev != dev)
327                 printf("devices changed for %s (expected)\n", argv[2]);
328
329         blkid_free_cache(cache);
330
331         return 0;
332 }
333 #endif