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