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