1 /* $OpenBSD: filesys.c,v 1.10 2003/06/03 02:56:15 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. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include "defs.h" 33 34 #ifndef lint 35 #if 0 36 static char RCSid[] __attribute__((__unused__)) = 37 "$From: filesys.c,v 1.2 1999/08/04 15:57:33 christos Exp $"; 38 #else 39 static char RCSid[] __attribute__((__unused__)) = 40 "$OpenBSD: filesys.c,v 1.10 2003/06/03 02:56:15 millert Exp $"; 41 #endif 42 43 static char sccsid[] __attribute__((__unused__)) = 44 "@(#)filesys.c"; 45 46 static char copyright[] __attribute__((__unused__)) = 47 "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 48 All rights reserved.\n"; 49 #endif /* not lint */ 50 51 /* 52 * This file contains functions dealing with getting info 53 * about mounted filesystems. 54 */ 55 56 57 jmp_buf env; 58 59 /* 60 * Given a pathname, find the fullest component that exists. 61 * If statbuf is not NULL, set it to point at our stat buffer. 62 */ 63 char * 64 find_file(char *pathname, struct stat *statbuf, int *isvalid) 65 { 66 static char last_pathname[MAXPATHLEN]; 67 static char file[MAXPATHLEN + 3]; 68 static struct stat filestat; 69 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 (strlen(pathname) > sizeof(file) + 3) { 91 error("%s: Name to large for buffer.", pathname); 92 return(NULL); 93 } 94 95 /* 96 * Save for next time 97 */ 98 (void) strlcpy(last_pathname, pathname, sizeof(last_pathname)); 99 100 if (*pathname == '/') 101 (void) strlcpy(file, pathname, sizeof(file)); 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) strlcpy(file, "./", sizeof(file)); 109 (void) strlcat(file, pathname, sizeof(file)); 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, '/')) != NULL) { 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(NULL); 136 } 137 continue; 138 } else { 139 error("%s: lstat failed: %s", pathname, SYSERR); 140 return(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 : 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 * 181 findmnt(struct stat *filest, struct mntinfo *mntinfo) 182 { 183 struct mntinfo *mi; 184 185 for (mi = mntinfo; mi; mi = mi->mi_nxt) { 186 if (mi->mi_mnt->me_flags & MEFLAG_IGNORE) 187 continue; 188 if (filest->st_dev == mi->mi_statb->st_dev) 189 return(mi->mi_mnt); 190 } 191 192 return(NULL); 193 } 194 195 /* 196 * Is "mnt" a duplicate of any of the mntinfo->mi_mnt elements? 197 */ 198 int 199 isdupmnt(mntent_t *mnt, struct mntinfo *mntinfo) 200 { 201 struct mntinfo *m; 202 203 for (m = mntinfo; m; m = m->mi_nxt) 204 if (strcmp(m->mi_mnt->me_path, mnt->me_path) == 0) 205 return(1); 206 207 return(0); 208 } 209 210 /* 211 * Alarm clock 212 */ 213 void 214 wakeup(int dummy) 215 { 216 debugmsg(DM_CALL, "wakeup() in filesys.c called"); 217 longjmp(env, 1); 218 } 219 220 /* 221 * Make a linked list of mntinfo structures. 222 * Use "mi" as the base of the list if it's non NULL. 223 */ 224 struct mntinfo * 225 makemntinfo(struct mntinfo *mi) 226 { 227 FILE *mfp; 228 static struct mntinfo *mntinfo; 229 struct 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(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(NULL); 245 } 246 247 mntinfo = mi; 248 while ((mnt = getmountent(mfp)) != NULL) { 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->mi_nxt; m = m->mi_nxt) 282 continue; 283 m->mi_nxt = newmi; 284 } else 285 mntinfo = newmi; 286 } 287 288 (void) alarm(0); 289 (void) endmountent(mfp); 290 291 return(mntinfo); 292 } 293 294 /* 295 * Given a name like /usr/src/etc/foo.c returns the mntent 296 * structure for the file system it lives in. 297 * 298 * If "statbuf" is not NULL it is used as the stat buffer too avoid 299 * stat()'ing the file again back in server.c. 300 */ 301 mntent_t * 302 getmntpt(char *pathname, struct stat *statbuf, int *isvalid) 303 { 304 static struct mntinfo *mntinfo = NULL; 305 static struct stat filestat; 306 struct stat *pstat; 307 struct mntinfo *tmpmi; 308 mntent_t *mnt; 309 310 /* 311 * Use the supplied stat buffer if not NULL or our own. 312 */ 313 if (statbuf) 314 pstat = statbuf; 315 else 316 pstat = &filestat; 317 318 if (!find_file(pathname, pstat, isvalid)) 319 return(NULL); 320 321 /* 322 * Make mntinfo if it doesn't exist. 323 */ 324 if (!mntinfo) 325 mntinfo = makemntinfo(NULL); 326 327 /* 328 * Find the mnt that pathname is on. 329 */ 330 if ((mnt = findmnt(pstat, mntinfo)) != NULL) 331 return(mnt); 332 333 /* 334 * We failed to find correct mnt, so maybe it's a newly 335 * mounted filesystem. We rebuild mntinfo and try again. 336 */ 337 if ((tmpmi = makemntinfo(mntinfo)) != NULL) { 338 mntinfo = tmpmi; 339 if ((mnt = findmnt(pstat, mntinfo)) != NULL) 340 return(mnt); 341 } 342 343 error("%s: Could not find mount point", pathname); 344 return(NULL); 345 } 346 347 #endif /* NFS_CHECK || RO_CHECK */ 348 349 #if defined(NFS_CHECK) 350 /* 351 * Is "path" NFS mounted? Return 1 if it is, 0 if not, or -1 on error. 352 */ 353 int 354 is_nfs_mounted(char *path, struct stat *statbuf, int *isvalid) 355 { 356 mntent_t *mnt; 357 358 if ((mnt = (mntent_t *) getmntpt(path, statbuf, isvalid)) == NULL) 359 return(-1); 360 361 /* 362 * We treat "cachefs" just like NFS 363 */ 364 if ((strcmp(mnt->me_type, METYPE_NFS) == 0) || 365 (strcmp(mnt->me_type, "cachefs") == 0)) 366 return(1); 367 368 return(0); 369 } 370 #endif /* NFS_CHECK */ 371 372 #if defined(RO_CHECK) 373 /* 374 * Is "path" on a read-only mounted filesystem? 375 * Return 1 if it is, 0 if not, or -1 on error. 376 */ 377 int 378 is_ro_mounted(char *path, struct stat *statbuf, int *isvalid) 379 { 380 mntent_t *mnt; 381 382 if ((mnt = (mntent_t *) getmntpt(path, statbuf, isvalid)) == NULL) 383 return(-1); 384 385 if (mnt->me_flags & MEFLAG_READONLY) 386 return(1); 387 388 return(0); 389 } 390 #endif /* RO_CHECK */ 391 392 /* 393 * Is "path" a symlink? 394 * Return 1 if it is, 0 if not, or -1 on error. 395 */ 396 int 397 is_symlinked(char *path, struct stat *statbuf, int *isvalid) 398 { 399 static struct stat stb; 400 401 if (!(*isvalid)) { 402 if (lstat(path, &stb) != 0) 403 return(-1); 404 statbuf = &stb; 405 } 406 407 if (S_ISLNK(statbuf->st_mode)) 408 return(1); 409 410 return(0); 411 } 412 413 /* 414 * Get filesystem information for "file". Set freespace 415 * to the amount of free (available) space and number of free 416 * files (inodes) on the filesystem "file" resides on. 417 * Returns 0 on success or -1 on failure. 418 * Filesystem values < 0 indicate unsupported or unavailable 419 * information. 420 */ 421 int 422 getfilesysinfo(char *file, long *freespace, long *freefiles) 423 { 424 #if defined(STATFS_TYPE) 425 static statfs_t statfsbuf; 426 char *mntpt; 427 int t, r; 428 429 /* 430 * Get the mount point of the file. 431 */ 432 mntpt = find_file(file, NULL, &t); 433 if (!mntpt) { 434 debugmsg(DM_MISC, "unknown mount point for `%s'", file); 435 return(-1); 436 } 437 438 /* 439 * Stat the filesystem (system specific) 440 */ 441 #if STATFS_TYPE == STATFS_SYSV 442 r = statfs(mntpt, &statfsbuf, sizeof(statfs_t), 0); 443 #endif 444 #if STATFS_TYPE == STATFS_BSD || STATFS_TYPE == STATFS_44BSD 445 r = statfs(mntpt, &statfsbuf); 446 #endif 447 #if STATFS_TYPE == STATFS_OSF1 448 r = statfs(mntpt, &statfsbuf, sizeof(statfs_t)); 449 #endif 450 451 if (r < 0) { 452 error("%s: Cannot statfs filesystem: %s.", mntpt, SYSERR); 453 return(-1); 454 } 455 456 /* 457 * If values are < 0, then assume the value is unsupported 458 * or unavailable for that filesystem type. 459 */ 460 if (statfsbuf.f_bavail >= 0) 461 *freespace = (statfsbuf.f_bavail * (statfsbuf.f_bsize / 512)) 462 / 2; 463 464 /* 465 * BROKEN_STATFS means that statfs() does not set fields 466 * to < 0 if the field is unsupported for the filesystem type. 467 */ 468 #if defined(BROKEN_STATFS) 469 if (statfsbuf.f_ffree > 0) 470 #else 471 if (statfsbuf.f_ffree >= 0) 472 #endif /* BROKEN_STATFS */ 473 *freefiles = statfsbuf.f_ffree; 474 475 #else /* !STATFS_TYPE */ 476 477 *freespace = *freefiles = -1; 478 479 #endif /* STATFS_TYPE */ 480 481 return(0); 482 } 483