xref: /netbsd-src/external/gpl2/lvm2/dist/lib/misc/lvm-file.c (revision 404fbe5fb94ca1e054339640cabb2801ce52dd30)
1 /*	$NetBSD: lvm-file.c,v 1.1.1.1 2008/12/22 00:18:13 haad Exp $	*/
2 
3 /*
4  * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
5  * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
6  *
7  * This file is part of LVM2.
8  *
9  * This copyrighted material is made available to anyone wishing to use,
10  * modify, copy, or redistribute it subject to the terms and conditions
11  * of the GNU Lesser General Public License v.2.1.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, write to the Free Software Foundation,
15  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17 
18 #include "lib.h"
19 #include "lvm-file.h"
20 #include "lvm-string.h"
21 
22 #include <unistd.h>
23 #include <sys/stat.h>
24 #include <sys/file.h>
25 #include <fcntl.h>
26 #include <dirent.h>
27 
28 /*
29  * Creates a temporary filename, and opens a descriptor to the
30  * file.  Both the filename and descriptor are needed so we can
31  * rename the file after successfully writing it.  Grab
32  * NFS-supported exclusive fcntl discretionary lock.
33  */
34 int create_temp_name(const char *dir, char *buffer, size_t len, int *fd)
35 {
36 	int i, num;
37 	pid_t pid;
38 	char hostname[255];
39 	struct flock lock = {
40 		.l_type = F_WRLCK,
41 		.l_whence = 0,
42 		.l_start = 0,
43 		.l_len = 0
44 	};
45 
46 	num = rand();
47 	pid = getpid();
48 	if (gethostname(hostname, sizeof(hostname)) < 0) {
49 		log_sys_error("gethostname", "");
50 		strcpy(hostname, "nohostname");
51 	}
52 
53 	for (i = 0; i < 20; i++, num++) {
54 
55 		if (dm_snprintf(buffer, len, "%s/.lvm_%s_%d_%d",
56 				 dir, hostname, pid, num) == -1) {
57 			log_err("Not enough space to build temporary file "
58 				"string.");
59 			return 0;
60 		}
61 
62 		*fd = open(buffer, O_CREAT | O_EXCL | O_WRONLY | O_APPEND,
63 			   S_IRUSR | S_IRGRP | S_IROTH |
64 			   S_IWUSR | S_IWGRP | S_IWOTH);
65 		if (*fd < 0)
66 			continue;
67 
68 		if (!fcntl(*fd, F_SETLK, &lock))
69 			return 1;
70 
71 		if (close(*fd))
72 			log_sys_error("close", buffer);
73 	}
74 
75 	return 0;
76 }
77 
78 /*
79  * NFS-safe rename of a temporary file to a common name, designed
80  * to avoid race conditions and not overwrite the destination if
81  * it exists.
82  *
83  * Try to create the new filename as a hard link to the original.
84  * Check the link count of the original file to see if it worked.
85  * (Assumes nothing else touches our temporary file!)  If it
86  * worked, unlink the old filename.
87  */
88 int lvm_rename(const char *old, const char *new)
89 {
90 	struct stat buf;
91 
92 	if (link(old, new)) {
93 		log_error("%s: rename to %s failed: %s", old, new,
94 			  strerror(errno));
95 		return 0;
96 	}
97 
98 	if (stat(old, &buf)) {
99 		log_sys_error("stat", old);
100 		return 0;
101 	}
102 
103 	if (buf.st_nlink != 2) {
104 		log_error("%s: rename to %s failed", old, new);
105 		return 0;
106 	}
107 
108 	if (unlink(old)) {
109 		log_sys_error("unlink", old);
110 		return 0;
111 	}
112 
113 	return 1;
114 }
115 
116 int path_exists(const char *path)
117 {
118 	struct stat info;
119 
120 	if (!*path)
121 		return 0;
122 
123 	if (stat(path, &info) < 0)
124 		return 0;
125 
126 	return 1;
127 }
128 
129 int dir_exists(const char *path)
130 {
131 	struct stat info;
132 
133 	if (!*path)
134 		return 0;
135 
136 	if (stat(path, &info) < 0)
137 		return 0;
138 
139 	if (!S_ISDIR(info.st_mode))
140 		return 0;
141 
142 	return 1;
143 }
144 
145 int is_empty_dir(const char *dir)
146 {
147 	struct dirent *dirent;
148 	DIR *d;
149 
150 	if (!(d = opendir(dir))) {
151 		log_sys_error("opendir", dir);
152 		return 0;
153 	}
154 
155 	while ((dirent = readdir(d)))
156 		if (strcmp(dirent->d_name, ".") && strcmp(dirent->d_name, ".."))
157 			break;
158 
159 	if (closedir(d)) {
160 		log_sys_error("closedir", dir);
161 	}
162 
163 	return dirent ? 0 : 1;
164 }
165 
166 void sync_dir(const char *file)
167 {
168 	int fd;
169 	char *dir, *c;
170 
171 	if (!(dir = dm_strdup(file))) {
172 		log_error("sync_dir failed in strdup");
173 		return;
174 	}
175 
176 	if (!dir_exists(dir)) {
177 		c = dir + strlen(dir);
178 		while (*c != '/' && c > dir)
179 			c--;
180 
181 		if (c == dir)
182 			*c++ = '.';
183 
184 		*c = '\0';
185 	}
186 
187 	if ((fd = open(dir, O_RDONLY)) == -1) {
188 		log_sys_error("open", dir);
189 		goto out;
190 	}
191 
192 	if (fsync(fd) && (errno != EROFS) && (errno != EINVAL))
193 		log_sys_error("fsync", dir);
194 
195 	if (close(fd))
196 		log_sys_error("close", dir);
197 
198       out:
199 	dm_free(dir);
200 }
201 
202 /*
203  * Attempt to obtain fcntl lock on a file, if necessary creating file first
204  * or waiting.
205  * Returns file descriptor on success, else -1.
206  * mode is F_WRLCK or F_RDLCK
207  */
208 int fcntl_lock_file(const char *file, short lock_type, int warn_if_read_only)
209 {
210 	int lockfd;
211 	char *dir;
212 	char *c;
213 	struct flock lock = {
214 		.l_type = lock_type,
215 		.l_whence = 0,
216 		.l_start = 0,
217 		.l_len = 0
218 	};
219 
220 	if (!(dir = dm_strdup(file))) {
221 		log_error("fcntl_lock_file failed in strdup.");
222 		return -1;
223 	}
224 
225 	if ((c = strrchr(dir, '/')))
226 		*c = '\0';
227 
228 	if (!dm_create_dir(dir)) {
229 		dm_free(dir);
230 		return -1;
231 	}
232 
233 	dm_free(dir);
234 
235 	log_very_verbose("Locking %s (%s, %hd)", file,
236 			 (lock_type == F_WRLCK) ? "F_WRLCK" : "F_RDLCK",
237 			 lock_type);
238 	if ((lockfd = open(file, O_RDWR | O_CREAT, 0777)) < 0) {
239 		/* EACCES has been reported on NFS */
240 		if (warn_if_read_only || (errno != EROFS && errno != EACCES))
241 			log_sys_error("open", file);
242 		else
243 			stack;
244 
245 		return -1;
246 	}
247 
248 	if (fcntl(lockfd, F_SETLKW, &lock)) {
249 		log_sys_error("fcntl", file);
250 		close(lockfd);
251 		return -1;
252 	}
253 
254 	return lockfd;
255 }
256 
257 void fcntl_unlock_file(int lockfd)
258 {
259 	struct flock lock = {
260 		.l_type = F_UNLCK,
261 		.l_whence = 0,
262 		.l_start = 0,
263 		.l_len = 0
264 	};
265 
266 	log_very_verbose("Unlocking fd %d", lockfd);
267 
268 	if (fcntl(lockfd, F_SETLK, &lock) == -1)
269 		log_error("fcntl unlock failed on fd %d: %s", lockfd,
270 			  strerror(errno));
271 
272 	if (close(lockfd))
273 		log_error("lock file close failed on fd %d: %s", lockfd,
274 			  strerror(errno));
275 }
276 
277 int lvm_fclose(FILE *fp, const char *filename)
278 {
279 	if (!dm_fclose(fp))
280 		return 0;
281 	if (errno == 0)
282 		log_error("%s: write error", filename);
283 	else
284 		log_sys_error("write error", filename);
285 	return EOF;
286 }
287