Whamcloud - gitweb
Integrate new blkid library.
[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 "blkid/blkid.h"
16
17 #ifdef DEBUG_DEV
18 #include <stdio.h>
19 #define DEB_DEV(fmt, arg...) printf("dev: " fmt, ## arg)
20 #else
21 #define DEB_DEV(fmt, arg...) do {} while (0)
22 #endif
23
24 blkid_dev *blkid_new_dev(void)
25 {
26         blkid_dev *dev;
27
28         if (!(dev = (blkid_dev *)calloc(1, sizeof(blkid_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         DEB_DEV("  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, blkid_tag,
48                                             bit_tags);
49                 blkid_free_tag(tag);
50         }
51         if (dev->bid_name)
52                 string_free(dev->bid_name);
53         free(dev);
54 }
55
56 /*
57  * This is kind of ugly, but I want to be able to compare two strings in
58  * several different ways.  For example, in some cases, if both strings
59  * are NULL, those would be considered different, but in other cases
60  * they would be considered the same.  Hence the ugliness.
61  *
62  * Use as:      "ret == SC_SAME" if both strings exist and are equal
63  *                              this is equivalent to "!(ret & SC_DIFF)"
64  *              "ret & SC_SAME" if both strings being NULL is also equal
65  *                              this is equivalent to "!(ret == SC_DIFF)"
66  *              "ret == SC_DIFF" if both strings exist and are different
67  *                              this is equivalent to "!(ret & SC_SAME)"
68  *              "ret & SC_DIFF" if both strings being NULL is also different
69  *                              this is equivalent to "!(ret == SC_SAME)"
70  *              "ret == SC_NONE" to see if both strings do not exist
71  */
72 #define SC_DIFF 0x0001
73 #define SC_NONE 0x0003
74 #define SC_SAME 0x0002
75
76 int string_compare(char *s1, char *s2)
77 {
78         if (!s1 && !s2)
79                 return SC_NONE;
80
81         if (!s1 || !s2)
82                 return SC_DIFF;
83
84         if (strcmp(s1, s2))
85                 return SC_DIFF;
86
87         return SC_SAME;
88 }
89
90 /*
91  * Add a tag to the global cache tag list.
92  */
93 static int add_tag_to_cache(blkid_cache *cache, blkid_tag *tag)
94 {
95         blkid_tag *head = NULL;
96
97         if (!cache || !tag)
98                 return 0;
99
100         DEB_DEV("    adding tag %s=%s to cache\n", tag->bit_name, tag->bit_val);
101
102         if (!(head = blkid_find_head_cache(cache, tag))) {
103                 head = blkid_new_tag();
104                 if (!head)
105                         return -BLKID_ERR_MEM;
106
107                 DEB_DEV("    creating new cache tag head %s\n",tag->bit_name);
108                 head->bit_name = string_copy(tag->bit_name);
109                 if (!head->bit_name) {
110                         blkid_free_tag(head);
111                         return -BLKID_ERR_MEM;
112                 }
113
114                 list_add_tail(&head->bit_tags, &cache->bic_tags);
115         }
116
117         /* Add this tag to global list */
118         list_add_tail(&tag->bit_names, &head->bit_names);
119
120         return 0;
121 }
122
123 /*
124  * Add a device to the global cache list, along with all its tags.
125  */
126 blkid_dev *blkid_add_dev_to_cache(blkid_cache *cache, blkid_dev *dev)
127 {
128         struct list_head *p;
129
130         if (!cache || !dev)
131                 return dev;
132
133         if (!dev->bid_id)
134                 dev->bid_id = ++(cache->bic_idmax);
135
136         list_for_each(p, &cache->bic_devs) {
137                 blkid_dev *odev = list_entry(p, blkid_dev, bid_devs);
138                 int dup_uuid, dup_label, dup_name, dup_type;
139
140                 dup_name = string_compare(odev->bid_name, dev->bid_name);
141                 dup_label = string_compare(odev->bid_label, dev->bid_label);
142                 dup_uuid = string_compare(odev->bid_uuid, dev->bid_uuid);
143
144                 if (odev->bid_id == dev->bid_id)
145                         dev->bid_id = ++(cache->bic_idmax);
146
147                 /* Fields different, do nothing (check more fields?) */
148                 if ((dup_name & SC_DIFF) && (dup_uuid & SC_DIFF) &&
149                     (dup_label & SC_DIFF))
150                         continue;
151
152                 /* We can't simply allow duplicate fields if the bid_type is
153                  * different, given that a filesystem may change from ext2
154                  * to ext3 but it will have the same UUID and LABEL fields.
155                  * We need to discard the old cache entry in this case.
156                  */
157
158                 /* If the UUIDs are the same but one is unverified discard it */
159                 if (dup_uuid == SC_SAME) {
160                         DEB_DEV("  duplicate uuid %s\n", dev->bid_uuid);
161                         if (!(odev->bid_flags & BLKID_BID_FL_VERIFIED)) {
162                                 dev->bid_id = odev->bid_id; /* keep old id */
163                                 blkid_free_dev(odev);
164                                 goto exit_new;
165                         } else if (!(dev->bid_flags & BLKID_BID_FL_VERIFIED)) {
166                                 blkid_free_dev(dev);
167                                 dev = odev;
168                                 goto exit_old;
169                         }
170
171                         /* This shouldn't happen */
172                         fprintf(stderr, "blkid: same UUID for %s and %s\n",
173                                 dev->bid_name, odev->bid_name);
174                 }
175
176                 /* If the device name is the same, discard one of them
177                  * (prefer one that has been validated, or the first one).
178                  */
179                 if (dup_name == SC_SAME) {
180                         DEB_DEV("  duplicate devname %s\n", dev->bid_name);
181                         if (odev->bid_flags & BLKID_BID_FL_VERIFIED ||
182                             !(dev->bid_flags & BLKID_BID_FL_VERIFIED)) {
183                                 if ((dup_uuid & SC_SAME) &&
184                                     (dup_label & SC_SAME))      /* use old id */
185                                         dev->bid_id = odev->bid_id;
186                                 blkid_free_dev(dev);
187                                 dev = odev;
188                                 goto exit_old;
189                         } else {
190                                 blkid_free_dev(odev);
191                                 goto exit_new;
192                         }
193                 }
194
195                 dup_type = string_compare(odev->bid_type, dev->bid_type);
196
197                 if (dup_label == SC_SAME && dup_type == SC_SAME) {
198                         DEB_DEV("  duplicate label %s\n", dev->bid_label);
199                         if (!(odev->bid_flags & BLKID_BID_FL_VERIFIED)) {
200                                 blkid_free_dev(odev);
201                                 goto exit_new;
202                         } else if (!(dev->bid_flags & BLKID_BID_FL_VERIFIED)) {
203                                 blkid_free_dev(dev);
204                                 dev = odev;
205                                 goto exit_old;
206                         }
207                         fprintf(stderr, "blkid: same LABEL for %s and %s\n",
208                                 dev->bid_name, odev->bid_name);
209                 }
210         }
211
212 exit_new:
213         DEB_DEV("  adding new devname %s to cache\n", dev->bid_name);
214
215         cache->bic_flags |= BLKID_BIC_FL_CHANGED;
216
217         list_add_tail(&dev->bid_devs, &cache->bic_devs);
218         list_for_each(p, &dev->bid_tags) {
219                 blkid_tag *tag = list_entry(p, blkid_tag, bit_tags);
220                 add_tag_to_cache(cache, tag);
221         }
222         return dev;
223
224 exit_old:
225         DEB_DEV("  using old devname %s from cache\n", dev->bid_name);
226         return dev;
227 }
228
229 #ifdef TEST_PROGRAM
230 int main(int argc, char** argv)
231 {
232         blkid_cache *cache;
233         blkid_dev *dev, *newdev;
234
235         if ((argc != 3)) {
236                 fprintf(stderr, "Usage:\t%s dev1 dev2\n"
237                         "Test that adding the same device to the cache fails\n",
238                         argv[0]);
239                 exit(1);
240         }
241
242         cache = blkid_new_cache();
243         if (!cache) {
244                 perror(argv[0]);
245                 return 1;
246         }
247         dev = blkid_devname_to_dev(argv[1], 0);
248         newdev = blkid_add_dev_to_cache(cache, dev);
249         if (newdev != dev)
250                 printf("devices changed for %s (unexpected)\n", argv[1]);
251         dev = blkid_devname_to_dev(argv[2], 0);
252         newdev = blkid_add_dev_to_cache(cache, dev);
253         if (newdev != dev)
254                 printf("devices changed for %s (unexpected)\n", argv[2]);
255         dev = blkid_devname_to_dev(argv[2], 0);
256         newdev = blkid_add_dev_to_cache(cache, dev);
257         if (newdev != dev)
258                 printf("devices changed for %s (expected)\n", argv[2]);
259
260         blkid_free_cache(cache);
261
262         return 0;
263 }
264 #endif