1 /* $OpenBSD: filesys.c,v 1.5 1998/06/26 21:20:48 millert Exp $ */ 2 3 /* 4 * Copyright (c) 1983 Regents of the University of California. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #ifndef lint 37 #if 0 38 static char RCSid[] = 39 "$From: filesys.c,v 6.24 1996/01/30 01:57:07 mcooper Exp $"; 40 #else 41 static char RCSid[] = 42 "$OpenBSD: filesys.c,v 1.5 1998/06/26 21:20:48 millert Exp $"; 43 #endif 44 45 static char sccsid[] = "@(#)filesys.c"; 46 47 static char copyright[] = 48 "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 49 All rights reserved.\n"; 50 #endif /* not lint */ 51 52 /* 53 * This file contains functions dealing with getting info 54 * about mounted filesystems. 55 */ 56 57 #include "defs.h" 58 #include "filesys.h" 59 60 jmp_buf env; 61 62 /* 63 * Given a pathname, find the fullest component that exists. 64 * If statbuf is not NULL, set it to point at our stat buffer. 65 */ 66 char *find_file(pathname, statbuf, isvalid) 67 char *pathname; 68 struct stat *statbuf; 69 int *isvalid; 70 { 71 static char last_pathname[MAXPATHLEN]; 72 static char file[MAXPATHLEN + 3]; 73 static struct stat filestat; 74 register char *p; 75 76 /* 77 * Mark the statbuf as invalid to start with. 78 */ 79 *isvalid = 0; 80 81 /* 82 * If this is the same pathname as the last time, and 83 * the file buffer is valid and we're doing the same stat() 84 * or lstat(), then set statbuf to the last filestat and 85 * return the last file we found. 86 */ 87 if (strcmp(pathname, last_pathname) == 0 && file[0]) { 88 if (statbuf) 89 statbuf = &filestat; 90 if (strcmp(pathname, file) == 0) 91 *isvalid = 1; 92 return(file); 93 } 94 95 if ((int)strlen(pathname) > sizeof(file)+3) { 96 error("%s: Name to large for buffer.", pathname); 97 return(NULL); 98 } 99 100 /* 101 * Save for next time 102 */ 103 (void) strcpy(last_pathname, pathname); 104 105 if (*pathname == '/') 106 (void) strcpy(file, pathname); 107 else { 108 /* 109 * Ensure we have a directory (".") in our path 110 * so we have something to stat in case the file 111 * does not exist. 112 */ 113 (void) strcpy(file, "./"); 114 (void) strcat(file, pathname); 115 } 116 117 while (lstat(file, &filestat) != 0) { 118 /* 119 * Trim the last part of the pathname to try next level up 120 */ 121 if (errno == ENOENT) { 122 /* 123 * Trim file name to get directory name. 124 * Normally we want to change /dir1/dir2/file 125 * into "/dir1/dir2/." 126 */ 127 if ((p = (char *) strrchr(file, '/'))) { 128 if (strcmp(p, "/.") == 0) { 129 *p = CNULL; 130 } else { 131 *++p = '.'; 132 *++p = CNULL; 133 } 134 } else { 135 /* 136 * Couldn't find anything, so give up. 137 */ 138 debugmsg(DM_MISC, "Cannot find dir of `%s'", 139 pathname); 140 return(NULL); 141 } 142 continue; 143 } else { 144 error("%s: lstat failed: %s", pathname, SYSERR); 145 return(NULL); 146 } 147 } 148 149 if (statbuf) 150 bcopy((char *) &filestat, (char *) statbuf, sizeof(filestat)); 151 152 /* 153 * Trim the "/." that we added. 154 */ 155 p = &file[strlen(file) - 1]; 156 if (*p == '.') 157 *p-- = CNULL; 158 for ( ; p && *p && *p == '/' && p != file; --p) 159 *p = CNULL; 160 161 /* 162 * If this file is a symlink we really want the parent directory 163 * name in case the symlink points to another filesystem. 164 */ 165 if (S_ISLNK(filestat.st_mode)) 166 if ((p = (char *) strrchr(file, '/')) && *p+1) { 167 /* Is this / (root)? */ 168 if (p == file) 169 file[1] = CNULL; 170 else 171 *p = CNULL; 172 } 173 174 if (strcmp(pathname, file) == 0) 175 *isvalid = 1; 176 177 return((file && *file) ? file : NULL); 178 } 179 180 #if defined(NFS_CHECK) || defined(RO_CHECK) 181 182 /* 183 * Find the device that "filest" is on in the "mntinfo" linked list. 184 */ 185 mntent_t *findmnt(filest, mntinfo) 186 struct stat *filest; 187 struct mntinfo *mntinfo; 188 { 189 register struct mntinfo *mi; 190 191 for (mi = mntinfo; mi; mi = mi->mi_nxt) { 192 if (mi->mi_mnt->me_flags & MEFLAG_IGNORE) 193 continue; 194 if (filest->st_dev == mi->mi_statb->st_dev) 195 return(mi->mi_mnt); 196 } 197 198 return(NULL); 199 } 200 201 /* 202 * Is "mnt" a duplicate of any of the mntinfo->mi_mnt elements? 203 */ 204 int isdupmnt(mnt, mntinfo) 205 mntent_t *mnt; 206 struct mntinfo *mntinfo; 207 { 208 register struct mntinfo *m; 209 210 for (m = mntinfo; m; m = m->mi_nxt) 211 if (strcmp(m->mi_mnt->me_path, mnt->me_path) == 0) 212 return(1); 213 214 return(0); 215 } 216 217 /* 218 * Alarm clock 219 */ 220 void wakeup() 221 { 222 debugmsg(DM_CALL, "wakeup() in filesys.c called"); 223 longjmp(env, 1); 224 } 225 226 /* 227 * Make a linked list of mntinfo structures. 228 * Use "mi" as the base of the list if it's non NULL. 229 */ 230 struct mntinfo *makemntinfo(mi) 231 struct mntinfo *mi; 232 { 233 FILE *mfp; 234 static struct mntinfo *mntinfo, *newmi, *m; 235 struct stat mntstat; 236 mntent_t *mnt; 237 int timeo = 310; 238 239 if (!(mfp = setmountent(MOUNTED_FILE, "r"))) { 240 message(MT_NERROR, "%s: setmntent failed: %s", 241 MOUNTED_FILE, SYSERR); 242 return(NULL); 243 } 244 245 (void) signal(SIGALRM, wakeup); 246 (void) alarm(timeo); 247 if (setjmp(env)) { 248 message(MT_NERROR, "Timeout getting mount info"); 249 return(NULL); 250 } 251 252 mntinfo = mi; 253 while ((mnt = getmountent(mfp))) { 254 debugmsg(DM_MISC, "mountent = '%s' (%s)", 255 mnt->me_path, mnt->me_type); 256 257 /* 258 * Make sure we don't already have it for some reason 259 */ 260 if (isdupmnt(mnt, mntinfo)) 261 continue; 262 263 /* 264 * Get stat info 265 */ 266 if (stat(mnt->me_path, &mntstat) != 0) { 267 message(MT_WARNING, "%s: Cannot stat filesystem: %s", 268 mnt->me_path, SYSERR); 269 continue; 270 } 271 272 /* 273 * Create new entry 274 */ 275 newmi = (struct mntinfo *) xcalloc(1, sizeof(struct mntinfo)); 276 newmi->mi_mnt = newmountent(mnt); 277 newmi->mi_statb = 278 (struct stat *) xcalloc(1, sizeof(struct stat)); 279 bcopy((char *) &mntstat, (char *) newmi->mi_statb, 280 sizeof(struct stat)); 281 282 /* 283 * Add entry to list 284 */ 285 if (mntinfo) { 286 for (m = mntinfo; m && m->mi_nxt; m = m->mi_nxt); 287 m->mi_nxt = newmi; 288 } else 289 mntinfo = newmi; 290 } 291 292 (void) alarm(0); 293 (void) endmountent(mfp); 294 295 return(mntinfo); 296 } 297 298 /* 299 * Given a name like /usr/src/etc/foo.c returns the mntent 300 * structure for the file system it lives in. 301 * 302 * If "statbuf" is not NULL it is used as the stat buffer too avoid 303 * stat()'ing the file again back in server.c. 304 */ 305 mntent_t *getmntpt(pathname, statbuf, isvalid) 306 char *pathname; 307 struct stat *statbuf; 308 int *isvalid; 309 { 310 static struct mntinfo *mntinfo = NULL; 311 static struct stat filestat; 312 struct stat *pstat; 313 struct mntinfo *tmpmi; 314 register mntent_t *mnt; 315 316 /* 317 * Use the supplied stat buffer if not NULL or our own. 318 */ 319 if (statbuf) 320 pstat = statbuf; 321 else 322 pstat = &filestat; 323 324 if (!find_file(pathname, pstat, isvalid)) 325 return(NULL); 326 327 /* 328 * Make mntinfo if it doesn't exist. 329 */ 330 if (!mntinfo) 331 mntinfo = makemntinfo(NULL); 332 333 /* 334 * Find the mnt that pathname is on. 335 */ 336 if ((mnt = findmnt(pstat, mntinfo))) 337 return(mnt); 338 339 /* 340 * We failed to find correct mnt, so maybe it's a newly 341 * mounted filesystem. We rebuild mntinfo and try again. 342 */ 343 if ((tmpmi = makemntinfo(mntinfo))) { 344 mntinfo = tmpmi; 345 if ((mnt = findmnt(pstat, mntinfo))) 346 return(mnt); 347 } 348 349 error("%s: Could not find mount point", pathname); 350 return(NULL); 351 } 352 353 #endif /* NFS_CHECK || RO_CHECK */ 354 355 #if defined(NFS_CHECK) 356 /* 357 * Is "path" NFS mounted? Return 1 if it is, 0 if not, or -1 on error. 358 */ 359 int is_nfs_mounted(path, statbuf, isvalid) 360 char *path; 361 struct stat *statbuf; 362 int *isvalid; 363 { 364 mntent_t *mnt; 365 366 if ((mnt = (mntent_t *) getmntpt(path, statbuf, isvalid)) == NULL) 367 return(-1); 368 369 /* 370 * We treat "cachefs" just like NFS 371 */ 372 if ((strcmp(mnt->me_type, METYPE_NFS) == 0) || 373 (strcmp(mnt->me_type, "cachefs") == 0)) 374 return(1); 375 376 return(0); 377 } 378 #endif /* NFS_CHECK */ 379 380 #if defined(RO_CHECK) 381 /* 382 * Is "path" on a read-only mounted filesystem? 383 * Return 1 if it is, 0 if not, or -1 on error. 384 */ 385 int is_ro_mounted(path, statbuf, isvalid) 386 char *path; 387 struct stat *statbuf; 388 int *isvalid; 389 { 390 mntent_t *mnt; 391 392 if ((mnt = (mntent_t *) getmntpt(path, statbuf, isvalid)) == NULL) 393 return(-1); 394 395 if (mnt->me_flags & MEFLAG_READONLY) 396 return(1); 397 398 return(0); 399 } 400 #endif /* RO_CHECK */ 401 402 /* 403 * Is "path" a symlink? 404 * Return 1 if it is, 0 if not, or -1 on error. 405 */ 406 int is_symlinked(path, statbuf, isvalid) 407 /*ARGSUSED*/ 408 char *path; 409 struct stat *statbuf; 410 int *isvalid; 411 { 412 static struct stat stb; 413 414 if (!(*isvalid)) { 415 if (lstat(path, &stb) != 0) 416 return(-1); 417 statbuf = &stb; 418 } 419 420 if (S_ISLNK(statbuf->st_mode)) 421 return(1); 422 423 return(0); 424 } 425 426 /* 427 * Get filesystem information for "file". Set freespace 428 * to the amount of free (available) space and number of free 429 * files (inodes) on the filesystem "file" resides on. 430 * Returns 0 on success or -1 on failure. 431 * Filesystem values < 0 indicate unsupported or unavailable 432 * information. 433 */ 434 int getfilesysinfo(file, freespace, freefiles) 435 char *file; 436 long *freespace; 437 long *freefiles; 438 { 439 #if defined(STATFS_TYPE) 440 static statfs_t statfsbuf; 441 char *mntpt; 442 int t, r; 443 444 /* 445 * Get the mount point of the file. 446 */ 447 mntpt = find_file(file, NULL, &t); 448 if (!mntpt) { 449 debugmsg(DM_MISC, "unknown mount point for `%s'", file); 450 return(-1); 451 } 452 453 /* 454 * Stat the filesystem (system specific) 455 */ 456 #if STATFS_TYPE == STATFS_SYSV 457 r = statfs(mntpt, &statfsbuf, sizeof(statfs_t), 0); 458 #endif 459 #if STATFS_TYPE == STATFS_BSD 460 r = statfs(mntpt, &statfsbuf); 461 #endif 462 #if STATFS_TYPE == STATFS_OSF1 463 r = statfs(mntpt, &statfsbuf, sizeof(statfs_t)); 464 #endif 465 466 if (r < 0) { 467 error("%s: Cannot statfs filesystem: %s.", mntpt, SYSERR); 468 return(-1); 469 } 470 471 /* 472 * If values are < 0, then assume the value is unsupported 473 * or unavailable for that filesystem type. 474 */ 475 if (statfsbuf.f_bavail >= 0) 476 *freespace = (statfsbuf.f_bavail * (statfsbuf.f_bsize / 512)) 477 / 2; 478 479 /* 480 * BROKEN_STATFS means that statfs() does not set fields 481 * to < 0 if the field is unsupported for the filesystem type. 482 */ 483 #if defined(BROKEN_STATFS) 484 if (statfsbuf.f_ffree > 0) 485 #else 486 if (statfsbuf.f_ffree >= 0) 487 #endif /* BROKEN_STATFS */ 488 *freefiles = statfsbuf.f_ffree; 489 490 #else /* !STATFS_TYPE */ 491 492 *freespace = *freefiles = -1; 493 494 #endif /* STATFS_TYPE */ 495 496 return(0); 497 } 498