multipathd: fix reservation_key check
[multipath-tools/.git] / libmultipath / alias.c
1 /*
2  * Copyright (c) 2005 Christophe Varoqui
3  * Copyright (c) 2005 Benjamin Marzinski, Redhat
4  */
5 #include <stdlib.h>
6 #include <errno.h>
7 #include <unistd.h>
8 #include <string.h>
9 #include <limits.h>
10 #include <stdio.h>
11
12 #include "debug.h"
13 #include "uxsock.h"
14 #include "alias.h"
15 #include "file.h"
16 #include "vector.h"
17 #include "checkers.h"
18 #include "structs.h"
19
20
21 /*
22  * significant parts of this file were taken from iscsi-bindings.c of the
23  * linux-iscsi project.
24  * Copyright (C) 2002 Cisco Systems, Inc.
25  *
26  * This program is free software; you can redistribute it and/or modify
27  * it under the terms of the GNU General Public License as published
28  * by the Free Software Foundation; either version 2 of the License, or
29  * (at your option) any later version.
30  *
31  * This program is distributed in the hope that it will be useful, but
32  * WITHOUT ANY WARRANTY; without even the implied warranty of
33  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
34  * General Public License for more details.
35  *
36  * See the file COPYING included with this distribution for more details.
37  */
38
39 int
40 valid_alias(char *alias)
41 {
42         if (strchr(alias, '/') != NULL)
43                 return 0;
44         return 1;
45 }
46
47
48 static int
49 format_devname(char *name, int id, int len, char *prefix)
50 {
51         int pos;
52         int prefix_len = strlen(prefix);
53
54         memset(name,0, len);
55         strcpy(name, prefix);
56         for (pos = len - 1; pos >= prefix_len; pos--) {
57                 id--;
58                 name[pos] = 'a' + id % 26;
59                 if (id < 26)
60                         break;
61                 id /= 26;
62         }
63         memmove(name + prefix_len, name + pos, len - pos);
64         name[prefix_len + len - pos] = '\0';
65         return (prefix_len + len - pos);
66 }
67
68 static int
69 scan_devname(char *alias, char *prefix)
70 {
71         char *c;
72         int i, n = 0;
73
74         if (!prefix || strncmp(alias, prefix, strlen(prefix)))
75                 return -1;
76
77         if (strlen(alias) == strlen(prefix))
78                 return -1;
79
80         if (strlen(alias) > strlen(prefix) + 7)
81                 /* id of 'aaaaaaaa' overflows int */
82                 return -1;
83
84         c = alias + strlen(prefix);
85         while (*c != '\0' && *c != ' ' && *c != '\t') {
86                 if (*c < 'a' || *c > 'z')
87                         return -1;
88                 i = *c - 'a';
89                 n = ( n * 26 ) + i;
90                 if (n < 0)
91                         return -1;
92                 c++;
93                 n++;
94         }
95
96         return n;
97 }
98
99 static int
100 lookup_binding(FILE *f, char *map_wwid, char **map_alias, char *prefix)
101 {
102         char buf[LINE_MAX];
103         unsigned int line_nr = 0;
104         int id = 1;
105         int biggest_id = 1;
106         int smallest_bigger_id = INT_MAX;
107
108         *map_alias = NULL;
109
110         rewind(f);
111         while (fgets(buf, LINE_MAX, f)) {
112                 char *c, *alias, *wwid;
113                 int curr_id;
114
115                 line_nr++;
116                 c = strpbrk(buf, "#\n\r");
117                 if (c)
118                         *c = '\0';
119                 alias = strtok(buf, " \t");
120                 if (!alias) /* blank line */
121                         continue;
122                 curr_id = scan_devname(alias, prefix);
123                 if (curr_id == id)
124                         id++;
125                 if (curr_id > biggest_id)
126                         biggest_id = curr_id;
127                 if (curr_id > id && curr_id < smallest_bigger_id)
128                         smallest_bigger_id = curr_id;
129                 wwid = strtok(NULL, " \t");
130                 if (!wwid){
131                         condlog(3,
132                                 "Ignoring malformed line %u in bindings file",
133                                 line_nr);
134                         continue;
135                 }
136                 if (strcmp(wwid, map_wwid) == 0){
137                         condlog(3, "Found matching wwid [%s] in bindings file."
138                                 " Setting alias to %s", wwid, alias);
139                         *map_alias = strdup(alias);
140                         if (*map_alias == NULL)
141                                 condlog(0, "Cannot copy alias from bindings "
142                                         "file : %s", strerror(errno));
143                         return 0;
144                 }
145         }
146         condlog(3, "No matching wwid [%s] in bindings file.", map_wwid);
147         if (id < 0) {
148                 condlog(0, "no more available user_friendly_names");
149                 return 0;
150         }
151         if (id < smallest_bigger_id)
152                 return id;
153         return biggest_id + 1;
154 }
155
156 static int
157 rlookup_binding(FILE *f, char *buff, char *map_alias, char *prefix)
158 {
159         char line[LINE_MAX];
160         unsigned int line_nr = 0;
161
162         buff[0] = '\0';
163
164         while (fgets(line, LINE_MAX, f)) {
165                 char *c, *alias, *wwid;
166
167                 line_nr++;
168                 c = strpbrk(line, "#\n\r");
169                 if (c)
170                         *c = '\0';
171                 alias = strtok(line, " \t");
172                 if (!alias) /* blank line */
173                         continue;
174                 wwid = strtok(NULL, " \t");
175                 if (!wwid){
176                         condlog(3,
177                                 "Ignoring malformed line %u in bindings file",
178                                 line_nr);
179                         continue;
180                 }
181                 if (strlen(wwid) > WWID_SIZE - 1) {
182                         condlog(3,
183                                 "Ignoring too large wwid at %u in bindings file", line_nr);
184                         continue;
185                 }
186                 if (strcmp(alias, map_alias) == 0){
187                         condlog(3, "Found matching alias [%s] in bindings file."
188                                 "\nSetting wwid to %s", alias, wwid);
189                         strncpy(buff, wwid, WWID_SIZE);
190                         buff[WWID_SIZE - 1] = '\0';
191                         return 0;
192                 }
193         }
194         condlog(3, "No matching alias [%s] in bindings file.", map_alias);
195
196         return -1;
197 }
198
199 static char *
200 allocate_binding(int fd, char *wwid, int id, char *prefix)
201 {
202         char buf[LINE_MAX];
203         off_t offset;
204         char *alias, *c;
205         int i;
206
207         if (id < 0) {
208                 condlog(0, "Bindings file full. Cannot allocate new binding");
209                 return NULL;
210         }
211
212         i = format_devname(buf, id, LINE_MAX, prefix);
213         c = buf + i;
214         snprintf(c,LINE_MAX - i, " %s\n", wwid);
215         buf[LINE_MAX - 1] = '\0';
216
217         offset = lseek(fd, 0, SEEK_END);
218         if (offset < 0){
219                 condlog(0, "Cannot seek to end of bindings file : %s",
220                         strerror(errno));
221                 return NULL;
222         }
223         if (write(fd, buf, strlen(buf)) != strlen(buf)){
224                 condlog(0, "Cannot write binding to bindings file : %s",
225                         strerror(errno));
226                 /* clear partial write */
227                 if (ftruncate(fd, offset))
228                         condlog(0, "Cannot truncate the header : %s",
229                                 strerror(errno));
230                 return NULL;
231         }
232         c = strchr(buf, ' ');
233         if (c)
234                 *c = '\0';
235         alias = strdup(buf);
236         if (alias == NULL)
237                 condlog(0, "cannot copy new alias from bindings file : %s",
238                         strerror(errno));
239         else
240                 condlog(3, "Created new binding [%s] for WWID [%s]", alias,
241                         wwid);
242         return alias;
243 }
244
245 char *
246 use_existing_alias (char *wwid, char *file, char *alias_old,
247                 char *prefix, int bindings_read_only)
248 {
249         char *alias = NULL;
250         int id = 0;
251         int fd, can_write;
252         char buff[WWID_SIZE];
253         FILE *f;
254
255         fd = open_file(file, &can_write, BINDINGS_FILE_HEADER);
256         if (fd < 0)
257                 return NULL;
258
259         f = fdopen(fd, "r");
260         if (!f) {
261                 condlog(0, "cannot fdopen on bindings file descriptor");
262                 close(fd);
263                 return NULL;
264         }
265         /* lookup the binding. if it exsists, the wwid will be in buff
266          * either way, id contains the id for the alias
267          */
268         rlookup_binding(f, buff, alias_old, prefix);
269
270         if (strlen(buff) > 0) {
271                 /* if buff is our wwid, it's already
272                  * allocated correctly
273                  */
274                 if (strcmp(buff, wwid) == 0)
275                         alias = STRDUP(alias_old);
276                 else {
277                         alias = NULL;
278                         condlog(0, "alias %s already bound to wwid %s, cannot reuse",
279                                 alias_old, buff);
280                 }
281                 goto out;
282         }
283
284         id = lookup_binding(f, wwid, &alias, NULL);
285         if (alias) {
286                 condlog(3, "Use existing binding [%s] for WWID [%s]",
287                         alias, wwid);
288                 goto out;
289         }
290
291         /* allocate the existing alias in the bindings file */
292         id = scan_devname(alias_old, prefix);
293         if (id <= 0)
294                 goto out;
295
296         if (fflush(f) != 0) {
297                 condlog(0, "cannot fflush bindings file stream : %s",
298                         strerror(errno));
299                 goto out;
300         }
301
302         if (can_write && !bindings_read_only) {
303                 alias = allocate_binding(fd, wwid, id, prefix);
304                 condlog(0, "Allocated existing binding [%s] for WWID [%s]",
305                         alias, wwid);
306         }
307
308 out:
309         fclose(f);
310         return alias;
311 }
312
313 char *
314 get_user_friendly_alias(char *wwid, char *file, char *prefix,
315                         int bindings_read_only)
316 {
317         char *alias;
318         int fd, id;
319         FILE *f;
320         int can_write;
321
322         if (!wwid || *wwid == '\0') {
323                 condlog(3, "Cannot find binding for empty WWID");
324                 return NULL;
325         }
326
327         fd = open_file(file, &can_write, BINDINGS_FILE_HEADER);
328         if (fd < 0)
329                 return NULL;
330
331         f = fdopen(fd, "r");
332         if (!f) {
333                 condlog(0, "cannot fdopen on bindings file descriptor : %s",
334                         strerror(errno));
335                 close(fd);
336                 return NULL;
337         }
338
339         id = lookup_binding(f, wwid, &alias, prefix);
340         if (id < 0) {
341                 fclose(f);
342                 return NULL;
343         }
344
345         if (fflush(f) != 0) {
346                 condlog(0, "cannot fflush bindings file stream : %s",
347                         strerror(errno));
348                 free(alias);
349                 fclose(f);
350                 return NULL;
351         }
352
353         if (!alias && can_write && !bindings_read_only && id)
354                 alias = allocate_binding(fd, wwid, id, prefix);
355
356         fclose(f);
357         return alias;
358 }
359
360 int
361 get_user_friendly_wwid(char *alias, char *buff, char *file)
362 {
363         int fd, unused;
364         FILE *f;
365
366         if (!alias || *alias == '\0') {
367                 condlog(3, "Cannot find binding for empty alias");
368                 return -1;
369         }
370
371         fd = open_file(file, &unused, BINDINGS_FILE_HEADER);
372         if (fd < 0)
373                 return -1;
374
375         f = fdopen(fd, "r");
376         if (!f) {
377                 condlog(0, "cannot fdopen on bindings file descriptor : %s",
378                         strerror(errno));
379                 close(fd);
380                 return -1;
381         }
382
383         rlookup_binding(f, buff, alias, NULL);
384         if (!strlen(buff)) {
385                 fclose(f);
386                 return -1;
387         }
388
389         fclose(f);
390         return 0;
391 }