multipathd: fix reservation_key check
[multipath-tools/.git] / libmultipath / file.c
1 /*
2  * Copyright (c) 2005 Christophe Varoqui
3  * Copyright (c) 2005 Benjamin Marzinski, Redhat
4  */
5 #include <stdlib.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <fcntl.h>
9 #include <errno.h>
10 #include <unistd.h>
11 #include <string.h>
12 #include <limits.h>
13 #include <stdio.h>
14 #include <signal.h>
15
16 #include "file.h"
17 #include "debug.h"
18 #include "uxsock.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 ensure_directories_exist(const char *str, mode_t dir_mode)
41 {
42         char *pathname;
43         char *end;
44         int err;
45
46         pathname = strdup(str);
47         if (!pathname){
48                 condlog(0, "Cannot copy file pathname %s : %s",
49                         str, strerror(errno));
50                 return -1;
51         }
52         end = pathname;
53         /* skip leading slashes */
54         while (end && *end && (*end == '/'))
55                 end++;
56
57         while ((end = strchr(end, '/'))) {
58                 /* if there is another slash, make the dir. */
59                 *end = '\0';
60                 err = mkdir(pathname, dir_mode);
61                 if (err && errno != EEXIST) {
62                         condlog(0, "Cannot make directory [%s] : %s",
63                                 pathname, strerror(errno));
64                         free(pathname);
65                         return -1;
66                 }
67                 if (!err)
68                         condlog(3, "Created dir [%s]", pathname);
69                 *end = '/';
70                 end++;
71         }
72         free(pathname);
73         return 0;
74 }
75
76 static void
77 sigalrm(int sig)
78 {
79         /* do nothing */
80 }
81
82 static int
83 lock_file(int fd, const char *file_name)
84 {
85         struct sigaction act, oldact;
86         sigset_t set, oldset;
87         struct flock lock;
88         int err;
89
90         memset(&lock, 0, sizeof(lock));
91         lock.l_type = F_WRLCK;
92         lock.l_whence = SEEK_SET;
93
94         act.sa_handler = sigalrm;
95         sigemptyset(&act.sa_mask);
96         act.sa_flags = 0;
97         sigemptyset(&set);
98         sigaddset(&set, SIGALRM);
99
100         sigaction(SIGALRM, &act, &oldact);
101         pthread_sigmask(SIG_UNBLOCK, &set, &oldset);
102
103         alarm(FILE_TIMEOUT);
104         err = fcntl(fd, F_SETLKW, &lock);
105         alarm(0);
106
107         if (err) {
108                 if (errno != EINTR)
109                         condlog(0, "Cannot lock %s : %s", file_name,
110                                 strerror(errno));
111                 else
112                         condlog(0, "%s is locked. Giving up.", file_name);
113         }
114
115         pthread_sigmask(SIG_SETMASK, &oldset, NULL);
116         sigaction(SIGALRM, &oldact, NULL);
117         return err;
118 }
119
120 int
121 open_file(const char *file, int *can_write, const char *header)
122 {
123         int fd;
124         struct stat s;
125
126         if (ensure_directories_exist(file, 0700))
127                 return -1;
128         *can_write = 1;
129         fd = open(file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
130         if (fd < 0) {
131                 if (errno == EROFS) {
132                         *can_write = 0;
133                         condlog(3, "Cannot open file [%s] read/write. "
134                                 " trying readonly", file);
135                         fd = open(file, O_RDONLY);
136                         if (fd < 0) {
137                                 condlog(0, "Cannot open file [%s] "
138                                         "readonly : %s", file, strerror(errno));
139                                 return -1;
140                         }
141                 }
142                 else {
143                         condlog(0, "Cannot open file [%s] : %s", file,
144                                 strerror(errno));
145                         return -1;
146                 }
147         }
148         if (*can_write && lock_file(fd, file) < 0)
149                 goto fail;
150
151         memset(&s, 0, sizeof(s));
152         if (fstat(fd, &s) < 0){
153                 condlog(0, "Cannot stat file %s : %s", file, strerror(errno));
154                 goto fail;
155         }
156         if (s.st_size == 0) {
157                 if (*can_write == 0)
158                         goto fail;
159                 /* If file is empty, write the header */
160                 size_t len = strlen(header);
161                 if (write(fd, header, len) != len) {
162                         condlog(0,
163                                 "Cannot write header to file %s : %s", file,
164                                 strerror(errno));
165                         /* cleanup partially written header */
166                         if (ftruncate(fd, 0))
167                                 condlog(0, "Cannot truncate header : %s",
168                                         strerror(errno));
169                         goto fail;
170                 }
171                 fsync(fd);
172                 condlog(3, "Initialized new file [%s]", file);
173         }
174
175         return fd;
176
177 fail:
178         close(fd);
179         return -1;
180 }