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