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