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