diff options
Diffstat (limited to 'usr/src/lib/librename/common/librename.c')
-rw-r--r-- | usr/src/lib/librename/common/librename.c | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/usr/src/lib/librename/common/librename.c b/usr/src/lib/librename/common/librename.c new file mode 100644 index 0000000000..1e198a50a3 --- /dev/null +++ b/usr/src/lib/librename/common/librename.c @@ -0,0 +1,229 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Implementation of librename(3RENAME) interfaces. + */ + +#include <librename.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <synch.h> + +typedef enum librename_atomic_state { + LIBRENAME_ATOMIC_INITIAL = 0x0, + LIBRENAME_ATOMIC_FSYNC, + LIBRENAME_ATOMIC_RENAME, + LIBRENAME_ATOMIC_POSTSYNC, + LIBRENAME_ATOMIC_COMPLETED +} librename_atomic_state_t; + +struct librename_atomic { + char *lra_fname; /* RO */ + char *lra_altname; /* RO */ + int lra_dirfd; /* RO */ + int lra_tmpfd; /* RO */ + mutex_t lra_lock; + librename_atomic_state_t lra_state; /* lra_lock */ +}; + +int +librename_atomic_fdinit(int fd, const char *file, const char *prefix, + int mode, int flags, librename_atomic_t **outp) +{ + int ret; + int oflags; + librename_atomic_t *lrap; + struct stat st; + + if (fd < 0 || file == NULL || outp == NULL) + return (EINVAL); + + if (flags & ~(LIBRENAME_ATOMIC_NOUNLINK | LIBRENAME_ATOMIC_CLOEXEC)) + return (EINVAL); + + if (strchr(file, '/') != NULL) + return (EINVAL); + + if (prefix != NULL && strchr(prefix, '/') != NULL) + return (EINVAL); + + *outp = NULL; + lrap = malloc(sizeof (librename_atomic_t)); + if (lrap == NULL) + return (errno); + + if (fstat(fd, &st) != 0) { + ret = errno; + free(lrap); + return (ret); + } + + if (!S_ISDIR(st.st_mode)) { + free(lrap); + return (ENOTDIR); + } + + if ((lrap->lra_dirfd = dup(fd)) == -1) { + ret = errno; + free(lrap); + return (ret); + } + + + lrap->lra_fname = strdup(file); + if (lrap->lra_fname == NULL) { + ret = errno; + if (close(lrap->lra_dirfd) != 0) + abort(); + free(lrap); + return (ret); + } + + if (prefix == NULL) { + ret = asprintf(&lrap->lra_altname, ".%d.%s", (int)getpid(), + file); + } else { + ret = asprintf(&lrap->lra_altname, "%s%s", prefix, file); + } + if (ret == -1) { + ret = errno; + free(lrap->lra_fname); + if (close(lrap->lra_dirfd) != 0) + abort(); + free(lrap); + return (errno); + } + + oflags = O_CREAT | O_TRUNC | O_RDWR | O_NOFOLLOW; + if (flags & LIBRENAME_ATOMIC_NOUNLINK) + oflags |= O_EXCL; + + if (flags & LIBRENAME_ATOMIC_CLOEXEC) + oflags |= O_CLOEXEC; + + lrap->lra_tmpfd = openat(lrap->lra_dirfd, lrap->lra_altname, + oflags, mode); + if (lrap->lra_tmpfd < 0) { + ret = errno; + free(lrap->lra_altname); + free(lrap->lra_fname); + if (close(lrap->lra_dirfd) != 0) + abort(); + free(lrap); + return (ret); + } + + if (mutex_init(&lrap->lra_lock, USYNC_THREAD, NULL) != 0) + abort(); + + lrap->lra_state = LIBRENAME_ATOMIC_INITIAL; + *outp = lrap; + return (0); +} + +int +librename_atomic_init(const char *dir, const char *file, const char *prefix, + int mode, int flags, librename_atomic_t **outp) +{ + int fd, ret; + + if ((fd = open(dir, O_RDONLY)) < 0) + return (errno); + + ret = librename_atomic_fdinit(fd, file, prefix, mode, flags, outp); + if (close(fd) != 0) + abort(); + + return (ret); +} + +int +librename_atomic_fd(librename_atomic_t *lrap) +{ + return (lrap->lra_tmpfd); +} + +/* + * To atomically commit a file, we need to go through and do the following: + * + * o fsync the source + * o run rename + * o fsync the source again and the directory. + */ +int +librename_atomic_commit(librename_atomic_t *lrap) +{ + int ret = 0; + + if (mutex_lock(&lrap->lra_lock) != 0) + abort(); + if (lrap->lra_state == LIBRENAME_ATOMIC_COMPLETED) { + ret = EINVAL; + goto out; + } + + if (fsync(lrap->lra_tmpfd) != 0) { + ret = errno; + goto out; + } + lrap->lra_state = LIBRENAME_ATOMIC_FSYNC; + + if (renameat(lrap->lra_dirfd, lrap->lra_altname, lrap->lra_dirfd, + lrap->lra_fname) != 0) { + ret = errno; + goto out; + } + lrap->lra_state = LIBRENAME_ATOMIC_RENAME; + + if (fsync(lrap->lra_tmpfd) != 0) { + ret = errno; + goto out; + } + lrap->lra_state = LIBRENAME_ATOMIC_POSTSYNC; + + if (fsync(lrap->lra_dirfd) != 0) { + ret = errno; + goto out; + } + lrap->lra_state = LIBRENAME_ATOMIC_COMPLETED; + +out: + if (mutex_unlock(&lrap->lra_lock) != 0) + abort(); + return (ret); +} + +void +librename_atomic_fini(librename_atomic_t *lrap) +{ + + free(lrap->lra_altname); + free(lrap->lra_fname); + if (close(lrap->lra_tmpfd) != 0) + abort(); + if (close(lrap->lra_dirfd) != 0) + abort(); + if (mutex_destroy(&lrap->lra_lock) != 0) + abort(); + free(lrap); +} |