1*6e2462f9SRobert Mustacchi /*
2*6e2462f9SRobert Mustacchi  * This file and its contents are supplied under the terms of the
3*6e2462f9SRobert Mustacchi  * Common Development and Distribution License ("CDDL"), version 1.0.
4*6e2462f9SRobert Mustacchi  * You may only use this file in accordance with the terms of version
5*6e2462f9SRobert Mustacchi  * 1.0 of the CDDL.
6*6e2462f9SRobert Mustacchi  *
7*6e2462f9SRobert Mustacchi  * A full copy of the text of the CDDL should have accompanied this
8*6e2462f9SRobert Mustacchi  * source.  A copy of the CDDL is also available via the Internet at
9*6e2462f9SRobert Mustacchi  * http://www.illumos.org/license/CDDL.
10*6e2462f9SRobert Mustacchi  */
11*6e2462f9SRobert Mustacchi 
12*6e2462f9SRobert Mustacchi /*
13*6e2462f9SRobert Mustacchi  * Copyright (c) 2014 Joyent, Inc.  All rights reserved.
14*6e2462f9SRobert Mustacchi  */
15*6e2462f9SRobert Mustacchi 
16*6e2462f9SRobert Mustacchi #include <librename.h>
17*6e2462f9SRobert Mustacchi 
18*6e2462f9SRobert Mustacchi #include <errno.h>
19*6e2462f9SRobert Mustacchi #include <stdlib.h>
20*6e2462f9SRobert Mustacchi #include <string.h>
21*6e2462f9SRobert Mustacchi #include <stdio.h>
22*6e2462f9SRobert Mustacchi #include <sys/debug.h>
23*6e2462f9SRobert Mustacchi #include <sys/types.h>
24*6e2462f9SRobert Mustacchi #include <sys/stat.h>
25*6e2462f9SRobert Mustacchi #include <fcntl.h>
26*6e2462f9SRobert Mustacchi #include <unistd.h>
27*6e2462f9SRobert Mustacchi #include <synch.h>
28*6e2462f9SRobert Mustacchi 
29*6e2462f9SRobert Mustacchi typedef enum librename_atomic_state {
30*6e2462f9SRobert Mustacchi 	LIBRENAME_ATOMIC_INITIAL = 0x0,
31*6e2462f9SRobert Mustacchi 	LIBRENAME_ATOMIC_FSYNC,
32*6e2462f9SRobert Mustacchi 	LIBRENAME_ATOMIC_RENAME,
33*6e2462f9SRobert Mustacchi 	LIBRENAME_ATOMIC_POSTSYNC,
34*6e2462f9SRobert Mustacchi 	LIBRENAME_ATOMIC_COMPLETED
35*6e2462f9SRobert Mustacchi } librename_atomic_state_t;
36*6e2462f9SRobert Mustacchi 
37*6e2462f9SRobert Mustacchi struct librename_atomic {
38*6e2462f9SRobert Mustacchi 	char *lra_fname;			/* RO */
39*6e2462f9SRobert Mustacchi 	char *lra_altname;			/* RO */
40*6e2462f9SRobert Mustacchi 	int lra_dirfd;				/* RO */
41*6e2462f9SRobert Mustacchi 	int lra_tmpfd;				/* RO */
42*6e2462f9SRobert Mustacchi 	mutex_t lra_lock;
43*6e2462f9SRobert Mustacchi 	librename_atomic_state_t lra_state;	/* lra_lock */
44*6e2462f9SRobert Mustacchi };
45*6e2462f9SRobert Mustacchi 
46*6e2462f9SRobert Mustacchi int
librename_atomic_fdinit(int fd,const char * file,const char * prefix,int mode,int flags,librename_atomic_t ** outp)47*6e2462f9SRobert Mustacchi librename_atomic_fdinit(int fd, const char *file, const char *prefix,
48*6e2462f9SRobert Mustacchi     int mode, int flags, librename_atomic_t **outp)
49*6e2462f9SRobert Mustacchi {
50*6e2462f9SRobert Mustacchi 	int ret;
51*6e2462f9SRobert Mustacchi 	int oflags;
52*6e2462f9SRobert Mustacchi 	librename_atomic_t *lrap;
53*6e2462f9SRobert Mustacchi 	struct stat st;
54*6e2462f9SRobert Mustacchi 
55*6e2462f9SRobert Mustacchi 	if (fd < 0 || file == NULL || outp == NULL)
56*6e2462f9SRobert Mustacchi 		return (EINVAL);
57*6e2462f9SRobert Mustacchi 
58*6e2462f9SRobert Mustacchi 	if (flags & ~(LIBRENAME_ATOMIC_NOUNLINK | LIBRENAME_ATOMIC_CLOEXEC))
59*6e2462f9SRobert Mustacchi 		return (EINVAL);
60*6e2462f9SRobert Mustacchi 
61*6e2462f9SRobert Mustacchi 	if (strchr(file, '/') != NULL)
62*6e2462f9SRobert Mustacchi 		return (EINVAL);
63*6e2462f9SRobert Mustacchi 
64*6e2462f9SRobert Mustacchi 	if (prefix != NULL && strchr(prefix, '/') != NULL)
65*6e2462f9SRobert Mustacchi 		return (EINVAL);
66*6e2462f9SRobert Mustacchi 
67*6e2462f9SRobert Mustacchi 	*outp = NULL;
68*6e2462f9SRobert Mustacchi 	lrap = malloc(sizeof (librename_atomic_t));
69*6e2462f9SRobert Mustacchi 	if (lrap == NULL)
70*6e2462f9SRobert Mustacchi 		return (errno);
71*6e2462f9SRobert Mustacchi 
72*6e2462f9SRobert Mustacchi 	if (fstat(fd, &st) != 0) {
73*6e2462f9SRobert Mustacchi 		ret = errno;
74*6e2462f9SRobert Mustacchi 		free(lrap);
75*6e2462f9SRobert Mustacchi 		return (ret);
76*6e2462f9SRobert Mustacchi 	}
77*6e2462f9SRobert Mustacchi 
78*6e2462f9SRobert Mustacchi 	if (!S_ISDIR(st.st_mode)) {
79*6e2462f9SRobert Mustacchi 		free(lrap);
80*6e2462f9SRobert Mustacchi 		return (ENOTDIR);
81*6e2462f9SRobert Mustacchi 	}
82*6e2462f9SRobert Mustacchi 
83*6e2462f9SRobert Mustacchi 	if ((lrap->lra_dirfd = dup(fd)) == -1) {
84*6e2462f9SRobert Mustacchi 		ret = errno;
85*6e2462f9SRobert Mustacchi 		free(lrap);
86*6e2462f9SRobert Mustacchi 		return (ret);
87*6e2462f9SRobert Mustacchi 	}
88*6e2462f9SRobert Mustacchi 
89*6e2462f9SRobert Mustacchi 	lrap->lra_fname = strdup(file);
90*6e2462f9SRobert Mustacchi 	if (lrap->lra_fname == NULL) {
91*6e2462f9SRobert Mustacchi 		ret = errno;
92*6e2462f9SRobert Mustacchi 		VERIFY0(close(lrap->lra_dirfd));
93*6e2462f9SRobert Mustacchi 		free(lrap);
94*6e2462f9SRobert Mustacchi 		return (ret);
95*6e2462f9SRobert Mustacchi 	}
96*6e2462f9SRobert Mustacchi 
97*6e2462f9SRobert Mustacchi 	if (prefix == NULL) {
98*6e2462f9SRobert Mustacchi 		ret = asprintf(&lrap->lra_altname, ".%d.%s", (int)getpid(),
99*6e2462f9SRobert Mustacchi 		    file);
100*6e2462f9SRobert Mustacchi 	} else {
101*6e2462f9SRobert Mustacchi 		ret = asprintf(&lrap->lra_altname, "%s%s", prefix, file);
102*6e2462f9SRobert Mustacchi 	}
103*6e2462f9SRobert Mustacchi 	if (ret == -1) {
104*6e2462f9SRobert Mustacchi 		ret = errno;
105*6e2462f9SRobert Mustacchi 		free(lrap->lra_fname);
106*6e2462f9SRobert Mustacchi 		VERIFY0(close(lrap->lra_dirfd));
107*6e2462f9SRobert Mustacchi 		free(lrap);
108*6e2462f9SRobert Mustacchi 		return (errno);
109*6e2462f9SRobert Mustacchi 	}
110*6e2462f9SRobert Mustacchi 
111*6e2462f9SRobert Mustacchi 	oflags = O_CREAT | O_TRUNC | O_RDWR | O_NOFOLLOW;
112*6e2462f9SRobert Mustacchi 	if (flags & LIBRENAME_ATOMIC_NOUNLINK)
113*6e2462f9SRobert Mustacchi 		oflags |= O_EXCL;
114*6e2462f9SRobert Mustacchi 
115*6e2462f9SRobert Mustacchi 	if (flags & LIBRENAME_ATOMIC_CLOEXEC)
116*6e2462f9SRobert Mustacchi 		oflags |= O_CLOEXEC;
117*6e2462f9SRobert Mustacchi 
118*6e2462f9SRobert Mustacchi 	lrap->lra_tmpfd = openat(lrap->lra_dirfd, lrap->lra_altname,
119*6e2462f9SRobert Mustacchi 	    oflags, mode);
120*6e2462f9SRobert Mustacchi 	if (lrap->lra_tmpfd < 0) {
121*6e2462f9SRobert Mustacchi 		ret = errno;
122*6e2462f9SRobert Mustacchi 		free(lrap->lra_altname);
123*6e2462f9SRobert Mustacchi 		free(lrap->lra_fname);
124*6e2462f9SRobert Mustacchi 		VERIFY0(close(lrap->lra_dirfd));
125*6e2462f9SRobert Mustacchi 		free(lrap);
126*6e2462f9SRobert Mustacchi 		return (ret);
127*6e2462f9SRobert Mustacchi 	}
128*6e2462f9SRobert Mustacchi 
129*6e2462f9SRobert Mustacchi 	VERIFY0(mutex_init(&lrap->lra_lock, USYNC_THREAD, NULL));
130*6e2462f9SRobert Mustacchi 
131*6e2462f9SRobert Mustacchi 	lrap->lra_state = LIBRENAME_ATOMIC_INITIAL;
132*6e2462f9SRobert Mustacchi 	*outp = lrap;
133*6e2462f9SRobert Mustacchi 	return (0);
134*6e2462f9SRobert Mustacchi }
135*6e2462f9SRobert Mustacchi 
136*6e2462f9SRobert Mustacchi int
librename_atomic_init(const char * dir,const char * file,const char * prefix,int mode,int flags,librename_atomic_t ** outp)137*6e2462f9SRobert Mustacchi librename_atomic_init(const char *dir, const char *file, const char *prefix,
138*6e2462f9SRobert Mustacchi     int mode, int flags, librename_atomic_t **outp)
139*6e2462f9SRobert Mustacchi {
140*6e2462f9SRobert Mustacchi 	int fd, ret;
141*6e2462f9SRobert Mustacchi 
142*6e2462f9SRobert Mustacchi 	if ((fd = open(dir, O_RDONLY)) < 0)
143*6e2462f9SRobert Mustacchi 		return (errno);
144*6e2462f9SRobert Mustacchi 
145*6e2462f9SRobert Mustacchi 	ret = librename_atomic_fdinit(fd, file, prefix, mode, flags, outp);
146*6e2462f9SRobert Mustacchi 	VERIFY0(close(fd));
147*6e2462f9SRobert Mustacchi 
148*6e2462f9SRobert Mustacchi 	return (ret);
149*6e2462f9SRobert Mustacchi }
150*6e2462f9SRobert Mustacchi 
151*6e2462f9SRobert Mustacchi int
librename_atomic_fd(librename_atomic_t * lrap)152*6e2462f9SRobert Mustacchi librename_atomic_fd(librename_atomic_t *lrap)
153*6e2462f9SRobert Mustacchi {
154*6e2462f9SRobert Mustacchi 	return (lrap->lra_tmpfd);
155*6e2462f9SRobert Mustacchi }
156*6e2462f9SRobert Mustacchi 
157*6e2462f9SRobert Mustacchi /*
158*6e2462f9SRobert Mustacchi  * To atomically commit a file, we need to go through and do the following:
159*6e2462f9SRobert Mustacchi  *
160*6e2462f9SRobert Mustacchi  *  o fsync the source
161*6e2462f9SRobert Mustacchi  *  o run rename
162*6e2462f9SRobert Mustacchi  *  o fsync the source again and the directory.
163*6e2462f9SRobert Mustacchi  */
164*6e2462f9SRobert Mustacchi int
librename_atomic_commit(librename_atomic_t * lrap)165*6e2462f9SRobert Mustacchi librename_atomic_commit(librename_atomic_t *lrap)
166*6e2462f9SRobert Mustacchi {
167*6e2462f9SRobert Mustacchi 	int ret = 0;
168*6e2462f9SRobert Mustacchi 
169*6e2462f9SRobert Mustacchi 	VERIFY0(mutex_lock(&lrap->lra_lock));
170*6e2462f9SRobert Mustacchi 	if (lrap->lra_state == LIBRENAME_ATOMIC_COMPLETED) {
171*6e2462f9SRobert Mustacchi 		ret = EINVAL;
172*6e2462f9SRobert Mustacchi 		goto out;
173*6e2462f9SRobert Mustacchi 	}
174*6e2462f9SRobert Mustacchi 
175*6e2462f9SRobert Mustacchi 	if (fsync(lrap->lra_tmpfd) != 0) {
176*6e2462f9SRobert Mustacchi 		ret = errno;
177*6e2462f9SRobert Mustacchi 		goto out;
178*6e2462f9SRobert Mustacchi 	}
179*6e2462f9SRobert Mustacchi 	lrap->lra_state = LIBRENAME_ATOMIC_FSYNC;
180*6e2462f9SRobert Mustacchi 
181*6e2462f9SRobert Mustacchi 	if (renameat(lrap->lra_dirfd, lrap->lra_altname, lrap->lra_dirfd,
182*6e2462f9SRobert Mustacchi 	    lrap->lra_fname) != 0) {
183*6e2462f9SRobert Mustacchi 		ret = errno;
184*6e2462f9SRobert Mustacchi 		goto out;
185*6e2462f9SRobert Mustacchi 	}
186*6e2462f9SRobert Mustacchi 	lrap->lra_state = LIBRENAME_ATOMIC_RENAME;
187*6e2462f9SRobert Mustacchi 
188*6e2462f9SRobert Mustacchi 	if (fsync(lrap->lra_tmpfd) != 0) {
189*6e2462f9SRobert Mustacchi 		ret = errno;
190*6e2462f9SRobert Mustacchi 		goto out;
191*6e2462f9SRobert Mustacchi 	}
192*6e2462f9SRobert Mustacchi 	lrap->lra_state = LIBRENAME_ATOMIC_POSTSYNC;
193*6e2462f9SRobert Mustacchi 
194*6e2462f9SRobert Mustacchi 	if (fsync(lrap->lra_dirfd) != 0) {
195*6e2462f9SRobert Mustacchi 		ret = errno;
196*6e2462f9SRobert Mustacchi 		goto out;
197*6e2462f9SRobert Mustacchi 	}
198*6e2462f9SRobert Mustacchi 	lrap->lra_state = LIBRENAME_ATOMIC_COMPLETED;
199*6e2462f9SRobert Mustacchi 
200*6e2462f9SRobert Mustacchi out:
201*6e2462f9SRobert Mustacchi 	VERIFY0(mutex_unlock(&lrap->lra_lock));
202*6e2462f9SRobert Mustacchi 	return (ret);
203*6e2462f9SRobert Mustacchi }
204*6e2462f9SRobert Mustacchi 
205*6e2462f9SRobert Mustacchi void
librename_atomic_fini(librename_atomic_t * lrap)206*6e2462f9SRobert Mustacchi librename_atomic_fini(librename_atomic_t *lrap)
207*6e2462f9SRobert Mustacchi {
208*6e2462f9SRobert Mustacchi 
209*6e2462f9SRobert Mustacchi 	free(lrap->lra_altname);
210*6e2462f9SRobert Mustacchi 	free(lrap->lra_fname);
211*6e2462f9SRobert Mustacchi 	VERIFY0(close(lrap->lra_tmpfd));
212*6e2462f9SRobert Mustacchi 	VERIFY0(close(lrap->lra_dirfd));
213*6e2462f9SRobert Mustacchi 	VERIFY0(mutex_destroy(&lrap->lra_lock));
214*6e2462f9SRobert Mustacchi 	free(lrap);
215*6e2462f9SRobert Mustacchi }
216