| /* |
| * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. |
| * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. |
| * |
| * This file is part of LVM2. |
| * |
| * This copyrighted material is made available to anyone wishing to use, |
| * modify, copy, or redistribute it subject to the terms and conditions |
| * of the GNU Lesser General Public License v.2.1. |
| * |
| * You should have received a copy of the GNU Lesser General Public License |
| * along with this program; if not, write to the Free Software Foundation, |
| * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| #include "lib.h" |
| #include "lvm-file.h" |
| |
| #include <unistd.h> |
| #include <sys/stat.h> |
| #include <sys/file.h> |
| #include <fcntl.h> |
| #include <dirent.h> |
| |
| /* |
| * Creates a temporary filename, and opens a descriptor to the |
| * file. Both the filename and descriptor are needed so we can |
| * rename the file after successfully writing it. Grab |
| * NFS-supported exclusive fcntl discretionary lock. |
| */ |
| int create_temp_name(const char *dir, char *buffer, size_t len, int *fd, |
| unsigned *seed) |
| { |
| const struct flock lock = { .l_type = F_WRLCK }; |
| int i, num; |
| pid_t pid; |
| char hostname[255]; |
| char *p; |
| |
| num = rand_r(seed); |
| pid = getpid(); |
| if (gethostname(hostname, sizeof(hostname)) < 0) { |
| log_sys_error("gethostname", ""); |
| strcpy(hostname, "nohostname"); |
| } else { |
| /* Replace any '/' with '?' found in the hostname. */ |
| p = hostname; |
| while ((p = strchr(p, '/'))) |
| *p = '?'; |
| } |
| |
| for (i = 0; i < 20; i++, num++) { |
| |
| if (dm_snprintf(buffer, len, "%s/.lvm_%s_%d_%d", |
| dir, hostname, pid, num) == -1) { |
| log_error("Not enough space to build temporary file " |
| "string."); |
| return 0; |
| } |
| |
| *fd = open(buffer, O_CREAT | O_EXCL | O_WRONLY | O_APPEND, |
| S_IRUSR | S_IRGRP | S_IROTH | |
| S_IWUSR | S_IWGRP | S_IWOTH); |
| if (*fd < 0) |
| continue; |
| |
| if (!fcntl(*fd, F_SETLK, &lock)) |
| return 1; |
| |
| if (close(*fd)) |
| log_sys_error("close", buffer); |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * NFS-safe rename of a temporary file to a common name, designed |
| * to avoid race conditions and not overwrite the destination if |
| * it exists. |
| * |
| * Try to create the new filename as a hard link to the original. |
| * Check the link count of the original file to see if it worked. |
| * (Assumes nothing else touches our temporary file!) If it |
| * worked, unlink the old filename. |
| */ |
| int lvm_rename(const char *old, const char *new) |
| { |
| struct stat buf; |
| |
| if (link(old, new)) { |
| log_error("%s: rename to %s failed: %s", old, new, |
| strerror(errno)); |
| return 0; |
| } |
| |
| if (stat(old, &buf)) { |
| log_sys_error("stat", old); |
| return 0; |
| } |
| |
| if (buf.st_nlink != 2) { |
| log_error("%s: rename to %s failed", old, new); |
| return 0; |
| } |
| |
| if (unlink(old)) { |
| log_sys_error("unlink", old); |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| int path_exists(const char *path) |
| { |
| struct stat info; |
| |
| if (!*path) |
| return 0; |
| |
| if (stat(path, &info) < 0) |
| return 0; |
| |
| return 1; |
| } |
| |
| int dir_exists(const char *path) |
| { |
| struct stat info; |
| |
| if (!*path) |
| return 0; |
| |
| if (stat(path, &info) < 0) |
| return 0; |
| |
| if (!S_ISDIR(info.st_mode)) |
| return 0; |
| |
| return 1; |
| } |
| |
| int is_empty_dir(const char *dir) |
| { |
| struct dirent *dirent; |
| DIR *d; |
| |
| if (!(d = opendir(dir))) { |
| log_sys_error("opendir", dir); |
| return 0; |
| } |
| |
| while ((dirent = readdir(d))) |
| if (strcmp(dirent->d_name, ".") && strcmp(dirent->d_name, "..")) |
| break; |
| |
| if (closedir(d)) |
| log_sys_error("closedir", dir); |
| |
| return dirent ? 0 : 1; |
| } |
| |
| void sync_dir(const char *file) |
| { |
| int fd; |
| char *dir, *c; |
| |
| if (!(dir = dm_strdup(file))) { |
| log_error("sync_dir failed in strdup"); |
| return; |
| } |
| |
| if (!dir_exists(dir)) { |
| c = dir + strlen(dir); |
| while (*c != '/' && c > dir) |
| c--; |
| |
| if (c == dir) |
| *c++ = '.'; |
| |
| *c = '\0'; |
| } |
| |
| if ((fd = open(dir, O_RDONLY)) == -1) { |
| log_sys_error("open", dir); |
| goto out; |
| } |
| |
| if (fsync(fd) && (errno != EROFS) && (errno != EINVAL)) |
| log_sys_error("fsync", dir); |
| |
| if (close(fd)) |
| log_sys_error("close", dir); |
| |
| out: |
| dm_free(dir); |
| } |
| |
| /* |
| * Attempt to obtain fcntl lock on a file, if necessary creating file first |
| * or waiting. |
| * Returns file descriptor on success, else -1. |
| * mode is F_WRLCK or F_RDLCK |
| */ |
| int fcntl_lock_file(const char *file, short lock_type, int warn_if_read_only) |
| { |
| const struct flock lock = { .l_type = lock_type }; |
| int lockfd; |
| char *dir; |
| char *c; |
| |
| if (!(dir = dm_strdup(file))) { |
| log_error("fcntl_lock_file failed in strdup."); |
| return -1; |
| } |
| |
| if ((c = strrchr(dir, '/'))) |
| *c = '\0'; |
| |
| if (!dm_create_dir(dir)) { |
| dm_free(dir); |
| return -1; |
| } |
| |
| dm_free(dir); |
| |
| log_very_verbose("Locking %s (%s, %hd)", file, |
| (lock_type == F_WRLCK) ? "F_WRLCK" : "F_RDLCK", |
| lock_type); |
| if ((lockfd = open(file, O_RDWR | O_CREAT, 0777)) < 0) { |
| /* EACCES has been reported on NFS */ |
| if (warn_if_read_only || (errno != EROFS && errno != EACCES)) |
| log_sys_error("open", file); |
| else |
| stack; |
| |
| return -1; |
| } |
| |
| if (fcntl(lockfd, F_SETLKW, &lock)) { |
| log_sys_error("fcntl", file); |
| if (close(lockfd)) |
| log_sys_error("close", file); |
| return -1; |
| } |
| |
| return lockfd; |
| } |
| |
| void fcntl_unlock_file(int lockfd) |
| { |
| const struct flock lock = { .l_type = F_UNLCK }; |
| |
| log_very_verbose("Unlocking fd %d", lockfd); |
| |
| if (fcntl(lockfd, F_SETLK, &lock) == -1) |
| log_sys_error("fcntl", ""); |
| |
| if (close(lockfd)) |
| log_sys_error("close",""); |
| } |
| |
| int lvm_fclose(FILE *fp, const char *filename) |
| { |
| if (!dm_fclose(fp)) |
| return 0; |
| |
| if (errno == 0) |
| log_error("%s: write error", filename); |
| else |
| log_sys_error("write error", filename); |
| |
| return EOF; |
| } |
| |
| void lvm_stat_ctim(struct timespec *ctim, const struct stat *buf) |
| { |
| #ifdef HAVE_STAT_ST_CTIM |
| *ctim = buf->st_ctim; |
| #else |
| ctim->tv_sec = buf->st_ctime; |
| ctim->tv_nsec = 0; |
| #endif |
| } |