Home | History | Annotate | Line # | Download | only in misc
      1 /*	$NetBSD: lvm-file.c,v 1.1.1.3 2009/12/02 00:26:44 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 		     unsigned *seed)
     36 {
     37 	int i, num;
     38 	pid_t pid;
     39 	char hostname[255];
     40 	struct flock lock = {
     41 		.l_type = F_WRLCK,
     42 		.l_whence = 0,
     43 		.l_start = 0,
     44 		.l_len = 0
     45 	};
     46 
     47 	num = rand_r(seed);
     48 	pid = getpid();
     49 	if (gethostname(hostname, sizeof(hostname)) < 0) {
     50 		log_sys_error("gethostname", "");
     51 		strcpy(hostname, "nohostname");
     52 	}
     53 
     54 	for (i = 0; i < 20; i++, num++) {
     55 
     56 		if (dm_snprintf(buffer, len, "%s/.lvm_%s_%d_%d",
     57 				 dir, hostname, pid, num) == -1) {
     58 			log_error("Not enough space to build temporary file "
     59 				  "string.");
     60 			return 0;
     61 		}
     62 
     63 		*fd = open(buffer, O_CREAT | O_EXCL | O_WRONLY | O_APPEND,
     64 			   S_IRUSR | S_IRGRP | S_IROTH |
     65 			   S_IWUSR | S_IWGRP | S_IWOTH);
     66 		if (*fd < 0)
     67 			continue;
     68 
     69 		if (!fcntl(*fd, F_SETLK, &lock))
     70 			return 1;
     71 
     72 		if (close(*fd))
     73 			log_sys_error("close", buffer);
     74 	}
     75 
     76 	return 0;
     77 }
     78 
     79 /*
     80  * NFS-safe rename of a temporary file to a common name, designed
     81  * to avoid race conditions and not overwrite the destination if
     82  * it exists.
     83  *
     84  * Try to create the new filename as a hard link to the original.
     85  * Check the link count of the original file to see if it worked.
     86  * (Assumes nothing else touches our temporary file!)  If it
     87  * worked, unlink the old filename.
     88  */
     89 int lvm_rename(const char *old, const char *new)
     90 {
     91 	struct stat buf;
     92 
     93 	if (link(old, new)) {
     94 		log_error("%s: rename to %s failed: %s", old, new,
     95 			  strerror(errno));
     96 		return 0;
     97 	}
     98 
     99 	if (stat(old, &buf)) {
    100 		log_sys_error("stat", old);
    101 		return 0;
    102 	}
    103 
    104 	if (buf.st_nlink != 2) {
    105 		log_error("%s: rename to %s failed", old, new);
    106 		return 0;
    107 	}
    108 
    109 	if (unlink(old)) {
    110 		log_sys_error("unlink", old);
    111 		return 0;
    112 	}
    113 
    114 	return 1;
    115 }
    116 
    117 int path_exists(const char *path)
    118 {
    119 	struct stat info;
    120 
    121 	if (!*path)
    122 		return 0;
    123 
    124 	if (stat(path, &info) < 0)
    125 		return 0;
    126 
    127 	return 1;
    128 }
    129 
    130 int dir_exists(const char *path)
    131 {
    132 	struct stat info;
    133 
    134 	if (!*path)
    135 		return 0;
    136 
    137 	if (stat(path, &info) < 0)
    138 		return 0;
    139 
    140 	if (!S_ISDIR(info.st_mode))
    141 		return 0;
    142 
    143 	return 1;
    144 }
    145 
    146 int is_empty_dir(const char *dir)
    147 {
    148 	struct dirent *dirent;
    149 	DIR *d;
    150 
    151 	if (!(d = opendir(dir))) {
    152 		log_sys_error("opendir", dir);
    153 		return 0;
    154 	}
    155 
    156 	while ((dirent = readdir(d)))
    157 		if (strcmp(dirent->d_name, ".") && strcmp(dirent->d_name, ".."))
    158 			break;
    159 
    160 	if (closedir(d)) {
    161 		log_sys_error("closedir", dir);
    162 	}
    163 
    164 	return dirent ? 0 : 1;
    165 }
    166 
    167 void sync_dir(const char *file)
    168 {
    169 	int fd;
    170 	char *dir, *c;
    171 
    172 	if (!(dir = dm_strdup(file))) {
    173 		log_error("sync_dir failed in strdup");
    174 		return;
    175 	}
    176 
    177 	if (!dir_exists(dir)) {
    178 		c = dir + strlen(dir);
    179 		while (*c != '/' && c > dir)
    180 			c--;
    181 
    182 		if (c == dir)
    183 			*c++ = '.';
    184 
    185 		*c = '\0';
    186 	}
    187 
    188 	if ((fd = open(dir, O_RDONLY)) == -1) {
    189 		log_sys_error("open", dir);
    190 		goto out;
    191 	}
    192 
    193 	if (fsync(fd) && (errno != EROFS) && (errno != EINVAL))
    194 		log_sys_error("fsync", dir);
    195 
    196 	if (close(fd))
    197 		log_sys_error("close", dir);
    198 
    199       out:
    200 	dm_free(dir);
    201 }
    202 
    203 /*
    204  * Attempt to obtain fcntl lock on a file, if necessary creating file first
    205  * or waiting.
    206  * Returns file descriptor on success, else -1.
    207  * mode is F_WRLCK or F_RDLCK
    208  */
    209 int fcntl_lock_file(const char *file, short lock_type, int warn_if_read_only)
    210 {
    211 	int lockfd;
    212 	char *dir;
    213 	char *c;
    214 	struct flock lock = {
    215 		.l_type = lock_type,
    216 		.l_whence = 0,
    217 		.l_start = 0,
    218 		.l_len = 0
    219 	};
    220 
    221 	if (!(dir = dm_strdup(file))) {
    222 		log_error("fcntl_lock_file failed in strdup.");
    223 		return -1;
    224 	}
    225 
    226 	if ((c = strrchr(dir, '/')))
    227 		*c = '\0';
    228 
    229 	if (!dm_create_dir(dir)) {
    230 		dm_free(dir);
    231 		return -1;
    232 	}
    233 
    234 	dm_free(dir);
    235 
    236 	log_very_verbose("Locking %s (%s, %hd)", file,
    237 			 (lock_type == F_WRLCK) ? "F_WRLCK" : "F_RDLCK",
    238 			 lock_type);
    239 	if ((lockfd = open(file, O_RDWR | O_CREAT, 0777)) < 0) {
    240 		/* EACCES has been reported on NFS */
    241 		if (warn_if_read_only || (errno != EROFS && errno != EACCES))
    242 			log_sys_error("open", file);
    243 		else
    244 			stack;
    245 
    246 		return -1;
    247 	}
    248 
    249 	if (fcntl(lockfd, F_SETLKW, &lock)) {
    250 		log_sys_error("fcntl", file);
    251 		close(lockfd);
    252 		return -1;
    253 	}
    254 
    255 	return lockfd;
    256 }
    257 
    258 void fcntl_unlock_file(int lockfd)
    259 {
    260 	struct flock lock = {
    261 		.l_type = F_UNLCK,
    262 		.l_whence = 0,
    263 		.l_start = 0,
    264 		.l_len = 0
    265 	};
    266 
    267 	log_very_verbose("Unlocking fd %d", lockfd);
    268 
    269 	if (fcntl(lockfd, F_SETLK, &lock) == -1)
    270 		log_error("fcntl unlock failed on fd %d: %s", lockfd,
    271 			  strerror(errno));
    272 
    273 	if (close(lockfd))
    274 		log_error("lock file close failed on fd %d: %s", lockfd,
    275 			  strerror(errno));
    276 }
    277 
    278 int lvm_fclose(FILE *fp, const char *filename)
    279 {
    280 	if (!dm_fclose(fp))
    281 		return 0;
    282 	if (errno == 0)
    283 		log_error("%s: write error", filename);
    284 	else
    285 		log_sys_error("write error", filename);
    286 	return EOF;
    287 }
    288