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 while ((err = flock(nfs_lock_fd, LOCK_EX)) != 0 && errno == EINTR) 54 ; 55 if (err != 0) { 56 err = errno; 57 fprintf(stderr, "failed to lock %s: %s\n", name, strerror(err)); 58 (void) close(nfs_lock_fd); 59 nfs_lock_fd = -1; 60 return (err); 61 } 62 63 return (0); 64 } 65 66 static void 67 nfs_exports_unlock(const char *name) 68 { 69 verify(nfs_lock_fd > 0); 70 71 if (flock(nfs_lock_fd, LOCK_UN) != 0) { 72 fprintf(stderr, "failed to unlock %s: %s\n", 73 name, strerror(errno)); 74 } 75 76 (void) close(nfs_lock_fd); 77 nfs_lock_fd = -1; 78 } 79 80 struct tmpfile { 81 /* 82 * This only needs to be as wide as ZFS_EXPORTS_FILE and mktemp suffix, 83 * 64 is more than enough. 84 */ 85 char name[64]; 86 FILE *fp; 87 }; 88 89 static boolean_t 90 nfs_init_tmpfile(const char *prefix, const char *mdir, struct tmpfile *tmpf) 91 { 92 if (mdir != NULL && 93 mkdir(mdir, 0755) < 0 && 94 errno != EEXIST) { 95 fprintf(stderr, "failed to create %s: %s\n", 96 mdir, strerror(errno)); 97 return (B_FALSE); 98 } 99 100 strcpy(tmpf->name, prefix); 101 strcat(tmpf->name, ".XXXXXXXX"); 102 103 int fd = mkostemp(tmpf->name, O_CLOEXEC); 104 if (fd == -1) { 105 fprintf(stderr, "Unable to create temporary file: %s", 106 strerror(errno)); 107 return (B_FALSE); 108 } 109 110 tmpf->fp = fdopen(fd, "w+"); 111 if (tmpf->fp == NULL) { 112 fprintf(stderr, "Unable to reopen temporary file: %s", 113 strerror(errno)); 114 close(fd); 115 return (B_FALSE); 116 } 117 118 return (B_TRUE); 119 } 120 121 static void 122 nfs_abort_tmpfile(struct tmpfile *tmpf) 123 { 124 unlink(tmpf->name); 125 fclose(tmpf->fp); 126 } 127 128 static int 129 nfs_fini_tmpfile(const char *exports, struct tmpfile *tmpf) 130 { 131 if (fflush(tmpf->fp) != 0) { 132 fprintf(stderr, "Failed to write to temporary file: %s\n", 133 strerror(errno)); 134 nfs_abort_tmpfile(tmpf); 135 return (SA_SYSTEM_ERR); 136 } 137 138 if (rename(tmpf->name, exports) == -1) { 139 fprintf(stderr, "Unable to rename %s -> %s: %s\n", 140 tmpf->name, exports, strerror(errno)); 141 nfs_abort_tmpfile(tmpf); 142 return (SA_SYSTEM_ERR); 143 } 144 145 (void) fchmod(fileno(tmpf->fp), 0644); 146 fclose(tmpf->fp); 147 return (SA_OK); 148 } 149 150 static int 151 nfs_process_exports(const char *exports, const char *mountpoint, 152 boolean_t (*cbk)(void *userdata, char *line, boolean_t found_mountpoint), 153 void *userdata) 154 { 155 int error = SA_OK; 156 boolean_t cont = B_TRUE; 157 158 FILE *oldfp = fopen(exports, "re"); 159 if (oldfp != NULL) { 160 char *buf = NULL, *sep; 161 size_t buflen = 0, mplen = strlen(mountpoint); 162 163 while (cont && getline(&buf, &buflen, oldfp) != -1) { 164 if (buf[0] == '\n' || buf[0] == '#') 165 continue; 166 167 cont = cbk(userdata, buf, 168 (sep = strpbrk(buf, "\t \n")) != NULL && 169 sep - buf == mplen && 170 strncmp(buf, mountpoint, mplen) == 0); 171 } 172 free(buf); 173 174 if (ferror(oldfp) != 0) 175 error = ferror(oldfp); 176 177 if (fclose(oldfp) != 0) { 178 fprintf(stderr, "Unable to close file %s: %s\n", 179 exports, strerror(errno)); 180 error = error != SA_OK ? error : SA_SYSTEM_ERR; 181 } 182 } 183 184 return (error); 185 } 186 187 static boolean_t 188 nfs_copy_entries_cb(void *userdata, char *line, boolean_t found_mountpoint) 189 { 190 FILE *newfp = userdata; 191 if (!found_mountpoint) 192 fputs(line, newfp); 193 return (B_TRUE); 194 } 195 196 /* 197 * Copy all entries from the exports file (if it exists) to newfp, 198 * omitting any entries for the specified mountpoint. 199 */ 200 static int 201 nfs_copy_entries(FILE *newfp, const char *exports, const char *mountpoint) 202 { 203 fputs(FILE_HEADER, newfp); 204 205 int error = nfs_process_exports( 206 exports, mountpoint, nfs_copy_entries_cb, newfp); 207 208 if (error == SA_OK && ferror(newfp) != 0) 209 error = ferror(newfp); 210 211 return (error); 212 } 213 214 int 215 nfs_toggle_share(const char *lockfile, const char *exports, 216 const char *expdir, sa_share_impl_t impl_share, 217 int(*cbk)(sa_share_impl_t impl_share, FILE *tmpfile)) 218 { 219 int error; 220 struct tmpfile tmpf; 221 222 if (!nfs_init_tmpfile(exports, expdir, &tmpf)) 223 return (SA_SYSTEM_ERR); 224 225 error = nfs_exports_lock(lockfile); 226 if (error != 0) { 227 nfs_abort_tmpfile(&tmpf); 228 return (error); 229 } 230 231 error = nfs_copy_entries(tmpf.fp, exports, impl_share->sa_mountpoint); 232 if (error != SA_OK) 233 goto fullerr; 234 235 error = cbk(impl_share, tmpf.fp); 236 if (error != SA_OK) 237 goto fullerr; 238 239 error = nfs_fini_tmpfile(exports, &tmpf); 240 nfs_exports_unlock(lockfile); 241 return (error); 242 243 fullerr: 244 nfs_abort_tmpfile(&tmpf); 245 nfs_exports_unlock(lockfile); 246 return (error); 247 } 248 249 static boolean_t 250 nfs_is_shared_cb(void *userdata, char *line, boolean_t found_mountpoint) 251 { 252 (void) line; 253 254 boolean_t *found = userdata; 255 *found = found_mountpoint; 256 return (!found_mountpoint); 257 } 258 259 boolean_t 260 nfs_is_shared_impl(const char *exports, sa_share_impl_t impl_share) 261 { 262 boolean_t found = B_FALSE; 263 nfs_process_exports(exports, impl_share->sa_mountpoint, 264 nfs_is_shared_cb, &found); 265 return (found); 266 } 267