xref: /freebsd/sys/contrib/openzfs/lib/libshare/nfs.c (revision 77013d11e6483b970af25e13c9b892075742f7e5)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <sys/file.h>
26 #include <fcntl.h>
27 #include <stdio.h>
28 #include <errno.h>
29 #include <libshare.h>
30 #include "nfs.h"
31 
32 
33 static int nfs_lock_fd = -1;
34 
35 
36 /*
37  * nfs_exports_[lock|unlock] are used to guard against conconcurrent
38  * updates to the exports file. Each protocol is responsible for
39  * providing the necessary locking to ensure consistency.
40  */
41 static int
42 nfs_exports_lock(const char *name)
43 {
44 	int err;
45 
46 	nfs_lock_fd = open(name, O_RDWR | O_CREAT | O_CLOEXEC, 0600);
47 	if (nfs_lock_fd == -1) {
48 		err = errno;
49 		fprintf(stderr, "failed to lock %s: %s\n", name, strerror(err));
50 		return (err);
51 	}
52 
53 	if (flock(nfs_lock_fd, LOCK_EX) != 0) {
54 		err = errno;
55 		fprintf(stderr, "failed to lock %s: %s\n", name, strerror(err));
56 		(void) close(nfs_lock_fd);
57 		nfs_lock_fd = -1;
58 		return (err);
59 	}
60 
61 	return (0);
62 }
63 
64 static void
65 nfs_exports_unlock(const char *name)
66 {
67 	verify(nfs_lock_fd > 0);
68 
69 	if (flock(nfs_lock_fd, LOCK_UN) != 0) {
70 		fprintf(stderr, "failed to unlock %s: %s\n",
71 		    name, strerror(errno));
72 	}
73 
74 	(void) close(nfs_lock_fd);
75 	nfs_lock_fd = -1;
76 }
77 
78 static char *
79 nfs_init_tmpfile(const char *prefix, const char *mdir)
80 {
81 	char *tmpfile = NULL;
82 	struct stat sb;
83 
84 	if (mdir != NULL &&
85 	    stat(mdir, &sb) < 0 &&
86 	    mkdir(mdir, 0755) < 0) {
87 		fprintf(stderr, "failed to create %s: %s\n",
88 		    mdir, strerror(errno));
89 		return (NULL);
90 	}
91 
92 	if (asprintf(&tmpfile, "%s.XXXXXXXX", prefix) == -1) {
93 		fprintf(stderr, "Unable to allocate temporary file\n");
94 		return (NULL);
95 	}
96 
97 	int fd = mkostemp(tmpfile, O_CLOEXEC);
98 	if (fd == -1) {
99 		fprintf(stderr, "Unable to create temporary file: %s",
100 		    strerror(errno));
101 		free(tmpfile);
102 		return (NULL);
103 	}
104 	close(fd);
105 	return (tmpfile);
106 }
107 
108 static int
109 nfs_fini_tmpfile(const char *exports, char *tmpfile)
110 {
111 	if (rename(tmpfile, exports) == -1) {
112 		fprintf(stderr, "Unable to rename %s: %s\n", tmpfile,
113 		    strerror(errno));
114 		unlink(tmpfile);
115 		free(tmpfile);
116 		return (SA_SYSTEM_ERR);
117 	}
118 	free(tmpfile);
119 	return (SA_OK);
120 }
121 
122 int
123 nfs_toggle_share(const char *lockfile, const char *exports,
124     const char *expdir, sa_share_impl_t impl_share,
125     int(*cbk)(sa_share_impl_t impl_share, char *filename))
126 {
127 	int error;
128 	char *filename;
129 
130 	if ((filename = nfs_init_tmpfile(exports, expdir)) == NULL)
131 		return (SA_SYSTEM_ERR);
132 
133 	error = nfs_exports_lock(lockfile);
134 	if (error != 0) {
135 		unlink(filename);
136 		free(filename);
137 		return (error);
138 	}
139 
140 	error = nfs_copy_entries(filename, impl_share->sa_mountpoint);
141 	if (error != SA_OK)
142 		goto fullerr;
143 
144 	error = cbk(impl_share, filename);
145 	if (error != SA_OK)
146 		goto fullerr;
147 
148 	error = nfs_fini_tmpfile(exports, filename);
149 	nfs_exports_unlock(lockfile);
150 	return (error);
151 
152 fullerr:
153 	unlink(filename);
154 	free(filename);
155 	nfs_exports_unlock(lockfile);
156 	return (error);
157 }
158