2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
13 * Copyright (c) 2014 Joyent, Inc. All rights reserved.
16 #include <librename.h>
22 #include <sys/debug.h>
23 #include <sys/types.h>
29 typedef enum librename_atomic_state
{
30 LIBRENAME_ATOMIC_INITIAL
= 0x0,
31 LIBRENAME_ATOMIC_FSYNC
,
32 LIBRENAME_ATOMIC_RENAME
,
33 LIBRENAME_ATOMIC_POSTSYNC
,
34 LIBRENAME_ATOMIC_COMPLETED
35 } librename_atomic_state_t
;
37 struct librename_atomic
{
38 char *lra_fname
; /* RO */
39 char *lra_altname
; /* RO */
40 int lra_dirfd
; /* RO */
41 int lra_tmpfd
; /* RO */
43 librename_atomic_state_t lra_state
; /* lra_lock */
47 librename_atomic_fdinit(int fd
, const char *file
, const char *prefix
,
48 int mode
, int flags
, librename_atomic_t
**outp
)
52 librename_atomic_t
*lrap
;
55 if (fd
< 0 || file
== NULL
|| outp
== NULL
)
58 if (flags
& ~(LIBRENAME_ATOMIC_NOUNLINK
| LIBRENAME_ATOMIC_CLOEXEC
))
61 if (strchr(file
, '/') != NULL
)
64 if (prefix
!= NULL
&& strchr(prefix
, '/') != NULL
)
68 lrap
= malloc(sizeof (librename_atomic_t
));
72 if (fstat(fd
, &st
) != 0) {
78 if (!S_ISDIR(st
.st_mode
)) {
83 if ((lrap
->lra_dirfd
= dup(fd
)) == -1) {
89 lrap
->lra_fname
= strdup(file
);
90 if (lrap
->lra_fname
== NULL
) {
92 VERIFY0(close(lrap
->lra_dirfd
));
98 ret
= asprintf(&lrap
->lra_altname
, ".%d.%s", (int)getpid(),
101 ret
= asprintf(&lrap
->lra_altname
, "%s%s", prefix
, file
);
105 free(lrap
->lra_fname
);
106 VERIFY0(close(lrap
->lra_dirfd
));
111 oflags
= O_CREAT
| O_TRUNC
| O_RDWR
| O_NOFOLLOW
;
112 if (flags
& LIBRENAME_ATOMIC_NOUNLINK
)
115 if (flags
& LIBRENAME_ATOMIC_CLOEXEC
)
118 lrap
->lra_tmpfd
= openat(lrap
->lra_dirfd
, lrap
->lra_altname
,
120 if (lrap
->lra_tmpfd
< 0) {
122 free(lrap
->lra_altname
);
123 free(lrap
->lra_fname
);
124 VERIFY0(close(lrap
->lra_dirfd
));
129 VERIFY0(mutex_init(&lrap
->lra_lock
, USYNC_THREAD
, NULL
));
131 lrap
->lra_state
= LIBRENAME_ATOMIC_INITIAL
;
137 librename_atomic_init(const char *dir
, const char *file
, const char *prefix
,
138 int mode
, int flags
, librename_atomic_t
**outp
)
142 if ((fd
= open(dir
, O_RDONLY
)) < 0)
145 ret
= librename_atomic_fdinit(fd
, file
, prefix
, mode
, flags
, outp
);
152 librename_atomic_fd(librename_atomic_t
*lrap
)
154 return (lrap
->lra_tmpfd
);
158 * To atomically commit a file, we need to go through and do the following:
162 * o fsync the source again and the directory.
165 librename_atomic_commit(librename_atomic_t
*lrap
)
169 VERIFY0(mutex_lock(&lrap
->lra_lock
));
170 if (lrap
->lra_state
== LIBRENAME_ATOMIC_COMPLETED
) {
175 if (fsync(lrap
->lra_tmpfd
) != 0) {
179 lrap
->lra_state
= LIBRENAME_ATOMIC_FSYNC
;
181 if (renameat(lrap
->lra_dirfd
, lrap
->lra_altname
, lrap
->lra_dirfd
,
182 lrap
->lra_fname
) != 0) {
186 lrap
->lra_state
= LIBRENAME_ATOMIC_RENAME
;
188 if (fsync(lrap
->lra_tmpfd
) != 0) {
192 lrap
->lra_state
= LIBRENAME_ATOMIC_POSTSYNC
;
194 if (fsync(lrap
->lra_dirfd
) != 0) {
198 lrap
->lra_state
= LIBRENAME_ATOMIC_COMPLETED
;
201 VERIFY0(mutex_unlock(&lrap
->lra_lock
));
206 librename_atomic_fini(librename_atomic_t
*lrap
)
209 free(lrap
->lra_altname
);
210 free(lrap
->lra_fname
);
211 VERIFY0(close(lrap
->lra_tmpfd
));
212 VERIFY0(close(lrap
->lra_dirfd
));
213 VERIFY0(mutex_destroy(&lrap
->lra_lock
));