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