1 /* $OpenBSD: filesys.c,v 1.7 2001/11/19 19:02:15 mpech 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.7 2001/11/19 19:02:15 mpech 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 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 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 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; 235 struct mntinfo *newmi, *m; 236 struct stat mntstat; 237 mntent_t *mnt; 238 int timeo = 310; 239 240 if (!(mfp = setmountent(MOUNTED_FILE, "r"))) { 241 message(MT_NERROR, "%s: setmntent failed: %s", 242 MOUNTED_FILE, SYSERR); 243 return(NULL); 244 } 245 246 (void) signal(SIGALRM, wakeup); 247 (void) alarm(timeo); 248 if (setjmp(env)) { 249 message(MT_NERROR, "Timeout getting mount info"); 250 return(NULL); 251 } 252 253 mntinfo = mi; 254 while ((mnt = getmountent(mfp))) { 255 debugmsg(DM_MISC, "mountent = '%s' (%s)", 256 mnt->me_path, mnt->me_type); 257 258 /* 259 * Make sure we don't already have it for some reason 260 */ 261 if (isdupmnt(mnt, mntinfo)) 262 continue; 263 264 /* 265 * Get stat info 266 */ 267 if (stat(mnt->me_path, &mntstat) != 0) { 268 message(MT_WARNING, "%s: Cannot stat filesystem: %s", 269 mnt->me_path, SYSERR); 270 continue; 271 } 272 273 /* 274 * Create new entry 275 */ 276 newmi = (struct mntinfo *) xcalloc(1, sizeof(struct mntinfo)); 277 newmi->mi_mnt = newmountent(mnt); 278 newmi->mi_statb = 279 (struct stat *) xcalloc(1, sizeof(struct stat)); 280 bcopy((char *) &mntstat, (char *) newmi->mi_statb, 281 sizeof(struct stat)); 282 283 /* 284 * Add entry to list 285 */ 286 if (mntinfo) { 287 for (m = mntinfo; m->mi_nxt; m = m->mi_nxt); 288 m->mi_nxt = newmi; 289 } else 290 mntinfo = newmi; 291 } 292 293 (void) alarm(0); 294 (void) endmountent(mfp); 295 296 return(mntinfo); 297 } 298 299 /* 300 * Given a name like /usr/src/etc/foo.c returns the mntent 301 * structure for the file system it lives in. 302 * 303 * If "statbuf" is not NULL it is used as the stat buffer too avoid 304 * stat()'ing the file again back in server.c. 305 */ 306 mntent_t *getmntpt(pathname, statbuf, isvalid) 307 char *pathname; 308 struct stat *statbuf; 309 int *isvalid; 310 { 311 static struct mntinfo *mntinfo = NULL; 312 static struct stat filestat; 313 struct stat *pstat; 314 struct mntinfo *tmpmi; 315 mntent_t *mnt; 316 317 /* 318 * Use the supplied stat buffer if not NULL or our own. 319 */ 320 if (statbuf) 321 pstat = statbuf; 322 else 323 pstat = &filestat; 324 325 if (!find_file(pathname, pstat, isvalid)) 326 return(NULL); 327 328 /* 329 * Make mntinfo if it doesn't exist. 330 */ 331 if (!mntinfo) 332 mntinfo = makemntinfo(NULL); 333 334 /* 335 * Find the mnt that pathname is on. 336 */ 337 if ((mnt = findmnt(pstat, mntinfo))) 338 return(mnt); 339 340 /* 341 * We failed to find correct mnt, so maybe it's a newly 342 * mounted filesystem. We rebuild mntinfo and try again. 343 */ 344 if ((tmpmi = makemntinfo(mntinfo))) { 345 mntinfo = tmpmi; 346 if ((mnt = findmnt(pstat, mntinfo))) 347 return(mnt); 348 } 349 350 error("%s: Could not find mount point", pathname); 351 return(NULL); 352 } 353 354 #endif /* NFS_CHECK || RO_CHECK */ 355 356 #if defined(NFS_CHECK) 357 /* 358 * Is "path" NFS mounted? Return 1 if it is, 0 if not, or -1 on error. 359 */ 360 int is_nfs_mounted(path, statbuf, isvalid) 361 char *path; 362 struct stat *statbuf; 363 int *isvalid; 364 { 365 mntent_t *mnt; 366 367 if ((mnt = (mntent_t *) getmntpt(path, statbuf, isvalid)) == NULL) 368 return(-1); 369 370 /* 371 * We treat "cachefs" just like NFS 372 */ 373 if ((strcmp(mnt->me_type, METYPE_NFS) == 0) || 374 (strcmp(mnt->me_type, "cachefs") == 0)) 375 return(1); 376 377 return(0); 378 } 379 #endif /* NFS_CHECK */ 380 381 #if defined(RO_CHECK) 382 /* 383 * Is "path" on a read-only mounted filesystem? 384 * Return 1 if it is, 0 if not, or -1 on error. 385 */ 386 int is_ro_mounted(path, statbuf, isvalid) 387 char *path; 388 struct stat *statbuf; 389 int *isvalid; 390 { 391 mntent_t *mnt; 392 393 if ((mnt = (mntent_t *) getmntpt(path, statbuf, isvalid)) == NULL) 394 return(-1); 395 396 if (mnt->me_flags & MEFLAG_READONLY) 397 return(1); 398 399 return(0); 400 } 401 #endif /* RO_CHECK */ 402 403 /* 404 * Is "path" a symlink? 405 * Return 1 if it is, 0 if not, or -1 on error. 406 */ 407 int is_symlinked(path, statbuf, isvalid) 408 /*ARGSUSED*/ 409 char *path; 410 struct stat *statbuf; 411 int *isvalid; 412 { 413 static struct stat stb; 414 415 if (!(*isvalid)) { 416 if (lstat(path, &stb) != 0) 417 return(-1); 418 statbuf = &stb; 419 } 420 421 if (S_ISLNK(statbuf->st_mode)) 422 return(1); 423 424 return(0); 425 } 426 427 /* 428 * Get filesystem information for "file". Set freespace 429 * to the amount of free (available) space and number of free 430 * files (inodes) on the filesystem "file" resides on. 431 * Returns 0 on success or -1 on failure. 432 * Filesystem values < 0 indicate unsupported or unavailable 433 * information. 434 */ 435 int getfilesysinfo(file, freespace, freefiles) 436 char *file; 437 long *freespace; 438 long *freefiles; 439 { 440 #if defined(STATFS_TYPE) 441 static statfs_t statfsbuf; 442 char *mntpt; 443 int t, r; 444 445 /* 446 * Get the mount point of the file. 447 */ 448 mntpt = find_file(file, NULL, &t); 449 if (!mntpt) { 450 debugmsg(DM_MISC, "unknown mount point for `%s'", file); 451 return(-1); 452 } 453 454 /* 455 * Stat the filesystem (system specific) 456 */ 457 #if STATFS_TYPE == STATFS_SYSV 458 r = statfs(mntpt, &statfsbuf, sizeof(statfs_t), 0); 459 #endif 460 #if STATFS_TYPE == STATFS_BSD 461 r = statfs(mntpt, &statfsbuf); 462 #endif 463 #if STATFS_TYPE == STATFS_OSF1 464 r = statfs(mntpt, &statfsbuf, sizeof(statfs_t)); 465 #endif 466 467 if (r < 0) { 468 error("%s: Cannot statfs filesystem: %s.", mntpt, SYSERR); 469 return(-1); 470 } 471 472 /* 473 * If values are < 0, then assume the value is unsupported 474 * or unavailable for that filesystem type. 475 */ 476 if (statfsbuf.f_bavail >= 0) 477 *freespace = (statfsbuf.f_bavail * (statfsbuf.f_bsize / 512)) 478 / 2; 479 480 /* 481 * BROKEN_STATFS means that statfs() does not set fields 482 * to < 0 if the field is unsupported for the filesystem type. 483 */ 484 #if defined(BROKEN_STATFS) 485 if (statfsbuf.f_ffree > 0) 486 #else 487 if (statfsbuf.f_ffree >= 0) 488 #endif /* BROKEN_STATFS */ 489 *freefiles = statfsbuf.f_ffree; 490 491 #else /* !STATFS_TYPE */ 492 493 *freespace = *freefiles = -1; 494 495 #endif /* STATFS_TYPE */ 496 497 return(0); 498 } 499