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 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* Copyright (c) 1988 AT&T */ 27 /* All Rights Reserved */ 28 29 #include "lint.h" 30 #include <mtlib.h> 31 #include <stdio.h> 32 #include <sys/types.h> 33 #include <sys/stat.h> 34 #include <sys/mnttab.h> 35 #include <sys/mntio.h> 36 #include <string.h> 37 #include <ctype.h> 38 #include <errno.h> 39 #include <stdlib.h> 40 #include <thread.h> 41 #include <synch.h> 42 #include <libc.h> 43 #include <unistd.h> 44 #include "tsd.h" 45 #include <atomic.h> 46 #include <strings.h> 47 48 static int getmntent_compat(FILE *fp, struct mnttab *mp); 49 50 #define GETTOK_R(xx, ll, tmp)\ 51 if ((mp->xx = (char *)strtok_r(ll, sepstr, tmp)) == NULL)\ 52 return (MNT_TOOFEW);\ 53 if (strcmp(mp->xx, dash) == 0)\ 54 mp->xx = NULL 55 56 #define DIFF(xx)\ 57 (mrefp->xx != NULL && (mgetp->xx == NULL ||\ 58 strcmp(mrefp->xx, mgetp->xx) != 0)) 59 60 #define SDIFF(xx, typem, typer)\ 61 ((mgetp->xx == NULL) || (stat64(mgetp->xx, &statb) == -1) ||\ 62 ((statb.st_mode & S_IFMT) != typem) ||\ 63 (statb.st_rdev != typer)) 64 65 static const char sepstr[] = " \t\n"; 66 static const char dash[] = "-"; 67 68 typedef struct { 69 size_t buflen; 70 char *buf; 71 } thread_data_t; 72 73 static void 74 destroy_thread_data(void *arg) 75 { 76 thread_data_t *thread_data = arg; 77 78 if (thread_data->buf != NULL) { 79 free(thread_data->buf); 80 thread_data->buf = NULL; 81 } 82 thread_data->buflen = 0; 83 } 84 85 static char * 86 getmntbuf(size_t size) 87 { 88 thread_data_t *thread_data; 89 90 thread_data = tsdalloc(_T_GETMNTENT, 91 sizeof (thread_data_t), destroy_thread_data); 92 if (thread_data == NULL) 93 return (NULL); 94 if (thread_data->buf == NULL || 95 thread_data->buflen < size) { 96 if (thread_data->buf != NULL) 97 free(thread_data->buf); 98 thread_data->buflen = 0; 99 if ((thread_data->buf = malloc(size)) == NULL) 100 return (NULL); 101 thread_data->buflen = size; 102 } 103 return (thread_data->buf); 104 } 105 106 static int 107 getmntany_compat(FILE *fp, struct mnttab *mgetp, struct mnttab *mrefp) 108 { 109 int ret, bstat; 110 mode_t bmode; 111 dev_t brdev; 112 struct stat64 statb; 113 114 /* 115 * Ignore specials that don't correspond to real devices to avoid doing 116 * unnecessary lookups in stat64(). 117 */ 118 if (mrefp->mnt_special && mrefp->mnt_special[0] == '/' && 119 stat64(mrefp->mnt_special, &statb) == 0 && 120 ((bmode = (statb.st_mode & S_IFMT)) == S_IFBLK || 121 bmode == S_IFCHR)) { 122 bstat = 1; 123 brdev = statb.st_rdev; 124 } else { 125 bstat = 0; 126 } 127 128 while ((ret = getmntent_compat(fp, mgetp)) == 0 && 129 ((bstat == 0 && DIFF(mnt_special)) || 130 (bstat == 1 && SDIFF(mnt_special, bmode, brdev)) || 131 DIFF(mnt_mountp) || 132 DIFF(mnt_fstype) || 133 DIFF(mnt_mntopts) || 134 DIFF(mnt_time))) 135 ; 136 137 return (ret); 138 } 139 140 int 141 getmntany(FILE *fp, struct mnttab *mgetp, struct mnttab *mrefp) 142 { 143 struct mntentbuf embuf; 144 char *copyp, *bufp; 145 int ret; 146 147 148 /* 149 * We collect all of the text strings pointed to by members of the 150 * user's preferences struct into a single buffer. At the same time 151 * populate the members of the results struct to point to the 152 * corresponding words. We then ask the kernel to figure out the 153 * rest; if this is a non-mntfs file then we handover to 154 * getmntany_compat(). 155 */ 156 if ((copyp = bufp = getmntbuf(MNT_LINE_MAX)) == NULL) { 157 errno = ENOMEM; 158 return (-1); 159 } 160 bzero(mgetp, sizeof (struct mnttab)); 161 if (mrefp->mnt_special) { 162 mgetp->mnt_special = copyp; 163 copyp += snprintf(mgetp->mnt_special, MNT_LINE_MAX, "%s", 164 mrefp->mnt_special) + 1; 165 } 166 if (mrefp->mnt_mountp) { 167 mgetp->mnt_mountp = copyp; 168 copyp += snprintf(mgetp->mnt_mountp, 169 bufp + MNT_LINE_MAX - copyp, "%s", mrefp->mnt_mountp) + 1; 170 } 171 if (mrefp->mnt_fstype) { 172 mgetp->mnt_fstype = copyp; 173 copyp += snprintf(mgetp->mnt_fstype, 174 bufp + MNT_LINE_MAX - copyp, "%s", mrefp->mnt_fstype) + 1; 175 } 176 if (mrefp->mnt_mntopts) { 177 mgetp->mnt_mntopts = copyp; 178 copyp += snprintf(mgetp->mnt_mntopts, 179 bufp + MNT_LINE_MAX - copyp, "%s", mrefp->mnt_mntopts) + 1; 180 } 181 if (mrefp->mnt_time) { 182 mgetp->mnt_time = copyp; 183 (void) snprintf(mgetp->mnt_time, bufp + MNT_LINE_MAX - copyp, 184 "%s", mrefp->mnt_time); 185 } 186 187 embuf.mbuf_emp = (struct extmnttab *)mgetp; 188 embuf.mbuf_bufsize = MNT_LINE_MAX; 189 embuf.mbuf_buf = bufp; 190 191 switch (ret = ioctl(fileno(fp), MNTIOC_GETMNTANY, &embuf)) { 192 case 0: 193 /* Success. */ 194 return (0); 195 case MNTFS_EOF: 196 return (-1); 197 case MNTFS_TOOLONG: 198 return (MNT_TOOLONG); 199 default: 200 /* A failure of some kind. */ 201 if (errno == ENOTTY) 202 return (getmntany_compat(fp, mgetp, mrefp)); 203 else 204 return (ret); 205 } 206 } 207 208 /* 209 * Common code for getmntent() and getextmntent(). 210 * 211 * These functions serve to populate a structure supplied by the user. Common 212 * to both struct mnttab and struct extmnttab is a set of pointers to the 213 * individual text fields that form an entry in /etc/mnttab. We arrange for the 214 * text itself to be stored in some thread-local storage, and for the kernel to 215 * populate both this buffer and the structure directly. 216 * 217 * If getmntent() passes a file that isn't provided by mntfs then we assume that 218 * it is a simple text file and give it to getmntent_compat() to parse. For 219 * getextmntent() we give up; it requires major and minor numbers that only the 220 * kernel can provide. 221 */ 222 static int 223 getmntent_common(FILE *fp, struct extmnttab *emp, int command) 224 { 225 struct mntentbuf embuf; 226 static size_t bufsize = MNT_LINE_MAX; 227 int ret; 228 229 embuf.mbuf_emp = emp; 230 embuf.mbuf_bufsize = bufsize; 231 if ((embuf.mbuf_buf = getmntbuf(embuf.mbuf_bufsize)) == NULL) { 232 errno = ENOMEM; 233 return (-1); 234 } 235 236 while ((ret = ioctl(fileno(fp), command, &embuf)) == MNTFS_TOOLONG) { 237 /* The buffer wasn't large enough. */ 238 (void) atomic_swap_ulong((unsigned long *)&bufsize, 239 2 * embuf.mbuf_bufsize); 240 embuf.mbuf_bufsize = bufsize; 241 if ((embuf.mbuf_buf = getmntbuf(embuf.mbuf_bufsize)) == NULL) { 242 errno = ENOMEM; 243 return (-1); 244 } 245 } 246 247 switch (ret) { 248 case 0: 249 /* 250 * We were successful, but we may have to enforce getmntent()'s 251 * documented limit on the line length. 252 */ 253 if (command == MNTIOC_GETMNTENT && 254 (emp->mnt_time + strlen(emp->mnt_time) + 1 - 255 emp->mnt_special > MNT_LINE_MAX)) 256 return (MNT_TOOLONG); 257 else 258 return (0); 259 case MNTFS_EOF: 260 /* EOF. */ 261 return (-1); 262 default: 263 /* A non-mntfs file. */ 264 if (command == MNTIOC_GETMNTENT) 265 return (getmntent_compat(fp, (struct mnttab *)emp)); 266 else 267 return (ret); 268 } 269 } 270 271 int 272 getmntent(FILE *fp, struct mnttab *mp) 273 { 274 return (getmntent_common(fp, (struct extmnttab *)mp, MNTIOC_GETMNTENT)); 275 } 276 277 /*ARGSUSED*/ 278 int 279 getextmntent(FILE *fp, struct extmnttab *emp, size_t len) 280 { 281 return (getmntent_common(fp, emp, MNTIOC_GETEXTMNTENT)); 282 } 283 284 char * 285 mntopt(char **p) 286 { 287 char *cp = *p; 288 char *retstr; 289 290 while (*cp && isspace(*cp)) 291 cp++; 292 293 retstr = cp; 294 while (*cp && *cp != ',') 295 cp++; 296 297 if (*cp) { 298 *cp = '\0'; 299 cp++; 300 } 301 302 *p = cp; 303 return (retstr); 304 } 305 306 char * 307 hasmntopt(struct mnttab *mnt, char *opt) 308 { 309 char tmpopts[MNT_LINE_MAX]; 310 char *f, *opts = tmpopts; 311 size_t len; 312 313 if (mnt->mnt_mntopts == NULL) 314 return (NULL); 315 (void) strcpy(opts, mnt->mnt_mntopts); 316 len = strlen(opt); 317 f = mntopt(&opts); 318 for (; *f; f = mntopt(&opts)) { 319 /* 320 * Match only complete substrings. For options 321 * which use a delimiter (such as 'retry=3'), 322 * treat the delimiter as the end of the substring. 323 */ 324 if (strncmp(opt, f, len) == 0 && 325 (f[len] == '\0' || !isalnum(f[len]))) 326 return (f - tmpopts + mnt->mnt_mntopts); 327 } 328 return (NULL); 329 } 330 331 void 332 resetmnttab(FILE *fp) 333 { 334 rewind(fp); 335 } 336 337 /* 338 * Compatibility for non-mntfs files. For backwards compatibility, we continue 339 * to have to support this broken interface. Note that getextmntent() has 340 * always failed when using a file other than /etc/mnttab, because it relies on 341 * an ioctl() call. 342 */ 343 static int 344 getaline(char *lp, FILE *fp) 345 { 346 char *cp; 347 348 while ((lp = fgets(lp, MNT_LINE_MAX, fp)) != NULL) { 349 if (strlen(lp) == MNT_LINE_MAX-1 && lp[MNT_LINE_MAX-2] != '\n') 350 return (MNT_TOOLONG); 351 352 for (cp = lp; *cp == ' ' || *cp == '\t'; cp++) 353 ; 354 355 if (*cp != '#' && *cp != '\n') 356 return (0); 357 } 358 return (-1); 359 } 360 361 static int 362 getmntent_compat(FILE *fp, struct mnttab *mp) 363 { 364 int ret; 365 char *tmp; 366 char *line = getmntbuf(MNT_LINE_MAX); 367 368 if (line == NULL) { 369 errno = ENOMEM; 370 return (-1); 371 } 372 373 /* skip leading spaces and comments */ 374 if ((ret = getaline(line, fp)) != 0) 375 return (ret); 376 377 /* split up each field */ 378 GETTOK_R(mnt_special, line, &tmp); 379 GETTOK_R(mnt_mountp, NULL, &tmp); 380 GETTOK_R(mnt_fstype, NULL, &tmp); 381 GETTOK_R(mnt_mntopts, NULL, &tmp); 382 GETTOK_R(mnt_time, NULL, &tmp); 383 384 /* check for too many fields */ 385 if (strtok_r(NULL, sepstr, &tmp) != NULL) 386 return (MNT_TOOMANY); 387 388 return (0); 389 } 390