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 https://opensource.org/licenses/CDDL-1.0. 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 <ctype.h> 28 #include <stdio.h> 29 #include <errno.h> 30 #include <libshare.h> 31 #include <unistd.h> 32 #include "nfs.h" 33 34 35 /* 36 * nfs_exports_[lock|unlock] are used to guard against conconcurrent 37 * updates to the exports file. Each protocol is responsible for 38 * providing the necessary locking to ensure consistency. 39 */ 40 static int 41 nfs_exports_lock(const char *name, int *nfs_lock_fd) 42 { 43 int err; 44 45 *nfs_lock_fd = open(name, O_RDWR | O_CREAT | O_CLOEXEC, 0600); 46 if (*nfs_lock_fd == -1) { 47 err = errno; 48 fprintf(stderr, "failed to lock %s: %s\n", name, strerror(err)); 49 return (err); 50 } 51 52 while ((err = flock(*nfs_lock_fd, LOCK_EX)) != 0 && errno == EINTR) 53 ; 54 if (err != 0) { 55 err = errno; 56 fprintf(stderr, "failed to lock %s: %s\n", name, strerror(err)); 57 (void) close(*nfs_lock_fd); 58 *nfs_lock_fd = -1; 59 return (err); 60 } 61 62 return (0); 63 } 64 65 static void 66 nfs_exports_unlock(const char *name, int *nfs_lock_fd) 67 { 68 verify(*nfs_lock_fd > 0); 69 70 if (flock(*nfs_lock_fd, LOCK_UN) != 0) 71 fprintf(stderr, "failed to unlock %s: %s\n", 72 name, strerror(errno)); 73 74 (void) close(*nfs_lock_fd); 75 *nfs_lock_fd = -1; 76 } 77 78 struct tmpfile { 79 /* 80 * This only needs to be as wide as ZFS_EXPORTS_FILE and mktemp suffix, 81 * 64 is more than enough. 82 */ 83 char name[64]; 84 FILE *fp; 85 }; 86 87 static boolean_t 88 nfs_init_tmpfile(const char *prefix, const char *mdir, struct tmpfile *tmpf) 89 { 90 if (mdir != NULL && 91 mkdir(mdir, 0755) < 0 && 92 errno != EEXIST) { 93 fprintf(stderr, "failed to create %s: %s\n", 94 mdir, strerror(errno)); 95 return (B_FALSE); 96 } 97 98 strcpy(tmpf->name, prefix); 99 strcat(tmpf->name, ".XXXXXXXX"); 100 101 int fd = mkostemp(tmpf->name, O_CLOEXEC); 102 if (fd == -1) { 103 fprintf(stderr, "Unable to create temporary file: %s", 104 strerror(errno)); 105 return (B_FALSE); 106 } 107 108 tmpf->fp = fdopen(fd, "w+"); 109 if (tmpf->fp == NULL) { 110 fprintf(stderr, "Unable to reopen temporary file: %s", 111 strerror(errno)); 112 close(fd); 113 return (B_FALSE); 114 } 115 116 return (B_TRUE); 117 } 118 119 static void 120 nfs_abort_tmpfile(struct tmpfile *tmpf) 121 { 122 unlink(tmpf->name); 123 fclose(tmpf->fp); 124 } 125 126 static int 127 nfs_fini_tmpfile(const char *exports, struct tmpfile *tmpf) 128 { 129 if (fflush(tmpf->fp) != 0) { 130 fprintf(stderr, "Failed to write to temporary file: %s\n", 131 strerror(errno)); 132 nfs_abort_tmpfile(tmpf); 133 return (SA_SYSTEM_ERR); 134 } 135 136 if (rename(tmpf->name, exports) == -1) { 137 fprintf(stderr, "Unable to rename %s -> %s: %s\n", 138 tmpf->name, exports, strerror(errno)); 139 nfs_abort_tmpfile(tmpf); 140 return (SA_SYSTEM_ERR); 141 } 142 143 (void) fchmod(fileno(tmpf->fp), 0644); 144 fclose(tmpf->fp); 145 return (SA_OK); 146 } 147 148 int 149 nfs_escape_mountpoint(const char *mp, char **out, boolean_t *need_free) 150 { 151 if (strpbrk(mp, "\t\n\v\f\r \\") == NULL) { 152 *out = (char *)mp; 153 *need_free = B_FALSE; 154 return (SA_OK); 155 } else { 156 size_t len = strlen(mp); 157 *out = malloc(len * 4 + 1); 158 if (!*out) 159 return (SA_NO_MEMORY); 160 *need_free = B_TRUE; 161 162 char *oc = *out; 163 for (const char *c = mp; c < mp + len; ++c) 164 if (memchr("\t\n\v\f\r \\", *c, 165 strlen("\t\n\v\f\r \\"))) { 166 sprintf(oc, "\\%03hho", *c); 167 oc += 4; 168 } else 169 *oc++ = *c; 170 *oc = '\0'; 171 } 172 173 return (SA_OK); 174 } 175 176 static int 177 nfs_process_exports(const char *exports, const char *mountpoint, 178 boolean_t (*cbk)(void *userdata, char *line, boolean_t found_mountpoint), 179 void *userdata) 180 { 181 int error = SA_OK; 182 boolean_t cont = B_TRUE; 183 184 FILE *oldfp = fopen(exports, "re"); 185 if (oldfp != NULL) { 186 boolean_t need_mp_free; 187 char *mp; 188 if ((error = nfs_escape_mountpoint(mountpoint, 189 &mp, &need_mp_free)) != SA_OK) { 190 (void) fclose(oldfp); 191 return (error); 192 } 193 194 char *buf = NULL, *sep; 195 size_t buflen = 0, mplen = strlen(mp); 196 197 while (cont && getline(&buf, &buflen, oldfp) != -1) { 198 if (buf[0] == '\n' || buf[0] == '#') 199 continue; 200 201 cont = cbk(userdata, buf, 202 (sep = strpbrk(buf, "\t \n")) != NULL && 203 sep - buf == mplen && 204 strncmp(buf, mp, mplen) == 0); 205 } 206 free(buf); 207 if (need_mp_free) 208 free(mp); 209 210 if (ferror(oldfp) != 0) 211 error = ferror(oldfp); 212 213 if (fclose(oldfp) != 0) { 214 fprintf(stderr, "Unable to close file %s: %s\n", 215 exports, strerror(errno)); 216 error = error != SA_OK ? error : SA_SYSTEM_ERR; 217 } 218 } 219 220 return (error); 221 } 222 223 static boolean_t 224 nfs_copy_entries_cb(void *userdata, char *line, boolean_t found_mountpoint) 225 { 226 FILE *newfp = userdata; 227 if (!found_mountpoint) 228 fputs(line, newfp); 229 return (B_TRUE); 230 } 231 232 /* 233 * Copy all entries from the exports file (if it exists) to newfp, 234 * omitting any entries for the specified mountpoint. 235 */ 236 static int 237 nfs_copy_entries(FILE *newfp, const char *exports, const char *mountpoint) 238 { 239 fputs(FILE_HEADER, newfp); 240 241 int error = nfs_process_exports( 242 exports, mountpoint, nfs_copy_entries_cb, newfp); 243 244 if (error == SA_OK && ferror(newfp) != 0) 245 error = ferror(newfp); 246 247 return (error); 248 } 249 250 int 251 nfs_toggle_share(const char *lockfile, const char *exports, 252 const char *expdir, sa_share_impl_t impl_share, 253 int(*cbk)(sa_share_impl_t impl_share, FILE *tmpfile)) 254 { 255 int error, nfs_lock_fd = -1; 256 struct tmpfile tmpf; 257 258 if (!nfs_init_tmpfile(exports, expdir, &tmpf)) 259 return (SA_SYSTEM_ERR); 260 261 error = nfs_exports_lock(lockfile, &nfs_lock_fd); 262 if (error != 0) { 263 nfs_abort_tmpfile(&tmpf); 264 return (error); 265 } 266 267 error = nfs_copy_entries(tmpf.fp, exports, impl_share->sa_mountpoint); 268 if (error != SA_OK) 269 goto fullerr; 270 271 error = cbk(impl_share, tmpf.fp); 272 if (error != SA_OK) 273 goto fullerr; 274 275 error = nfs_fini_tmpfile(exports, &tmpf); 276 nfs_exports_unlock(lockfile, &nfs_lock_fd); 277 return (error); 278 279 fullerr: 280 nfs_abort_tmpfile(&tmpf); 281 nfs_exports_unlock(lockfile, &nfs_lock_fd); 282 return (error); 283 } 284 285 void 286 nfs_reset_shares(const char *lockfile, const char *exports) 287 { 288 int nfs_lock_fd = -1; 289 290 if (nfs_exports_lock(lockfile, &nfs_lock_fd) == 0) { 291 (void) ! truncate(exports, 0); 292 nfs_exports_unlock(lockfile, &nfs_lock_fd); 293 } 294 } 295 296 static boolean_t 297 nfs_is_shared_cb(void *userdata, char *line, boolean_t found_mountpoint) 298 { 299 (void) line; 300 301 boolean_t *found = userdata; 302 *found = found_mountpoint; 303 return (!found_mountpoint); 304 } 305 306 boolean_t 307 nfs_is_shared_impl(const char *exports, sa_share_impl_t impl_share) 308 { 309 boolean_t found = B_FALSE; 310 nfs_process_exports(exports, impl_share->sa_mountpoint, 311 nfs_is_shared_cb, &found); 312 return (found); 313 } 314