Annotation of src/usr.bin/rdistd/filesys.c, Revision 1.10
1.10 ! millert 1: /* $OpenBSD: filesys.c,v 1.9 2003/05/14 01:34:35 millert 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.
1.10 ! millert 15: * 3. Neither the name of the University nor the names of its contributors
1.1 dm 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:
1.9 millert 32: #include "defs.h"
33:
1.1 dm 34: #ifndef lint
1.5 millert 35: #if 0
1.9 millert 36: static char RCSid[] __attribute__((__unused__)) =
37: "$From: filesys.c,v 1.2 1999/08/04 15:57:33 christos Exp $";
1.5 millert 38: #else
1.9 millert 39: static char RCSid[] __attribute__((__unused__)) =
1.10 ! millert 40: "$OpenBSD: filesys.c,v 1.9 2003/05/14 01:34:35 millert Exp $";
1.5 millert 41: #endif
1.1 dm 42:
1.9 millert 43: static char sccsid[] __attribute__((__unused__)) =
44: "@(#)filesys.c";
1.1 dm 45:
1.9 millert 46: static char copyright[] __attribute__((__unused__)) =
1.1 dm 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: */
1.9 millert 63: char *
64: find_file(char *pathname, struct stat *statbuf, int *isvalid)
1.1 dm 65: {
66: static char last_pathname[MAXPATHLEN];
67: static char file[MAXPATHLEN + 3];
68: static struct stat filestat;
1.7 mpech 69: char *p;
1.1 dm 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:
1.9 millert 90: if (strlen(pathname) > sizeof(file) + 3) {
1.1 dm 91: error("%s: Name to large for buffer.", pathname);
1.5 millert 92: return(NULL);
1.1 dm 93: }
94:
95: /*
96: * Save for next time
97: */
1.9 millert 98: (void) strlcpy(last_pathname, pathname, sizeof(last_pathname));
1.1 dm 99:
100: if (*pathname == '/')
1.9 millert 101: (void) strlcpy(file, pathname, sizeof(file));
1.1 dm 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: */
1.9 millert 108: (void) strlcpy(file, "./", sizeof(file));
109: (void) strlcat(file, pathname, sizeof(file));
1.1 dm 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: */
1.9 millert 122: if ((p = (char *) strrchr(file, '/')) != NULL) {
1.4 millert 123: if (strcmp(p, "/.") == 0) {
1.9 millert 124: *p = CNULL;
1.4 millert 125: } else {
1.9 millert 126: *++p = '.';
127: *++p = CNULL;
1.4 millert 128: }
1.1 dm 129: } else {
130: /*
131: * Couldn't find anything, so give up.
132: */
133: debugmsg(DM_MISC, "Cannot find dir of `%s'",
134: pathname);
1.5 millert 135: return(NULL);
1.1 dm 136: }
137: continue;
138: } else {
139: error("%s: lstat failed: %s", pathname, SYSERR);
1.5 millert 140: return(NULL);
1.1 dm 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:
1.5 millert 172: return((file && *file) ? file : NULL);
1.1 dm 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: */
1.9 millert 180: mntent_t *
181: findmnt(struct stat *filest, struct mntinfo *mntinfo)
1.1 dm 182: {
1.7 mpech 183: struct mntinfo *mi;
1.1 dm 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:
1.5 millert 192: return(NULL);
1.1 dm 193: }
194:
195: /*
196: * Is "mnt" a duplicate of any of the mntinfo->mi_mnt elements?
197: */
1.9 millert 198: int
199: isdupmnt(mntent_t *mnt, struct mntinfo *mntinfo)
1.1 dm 200: {
1.7 mpech 201: struct mntinfo *m;
1.1 dm 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: */
1.9 millert 213: void
214: wakeup(int dummy)
1.1 dm 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: */
1.9 millert 224: struct mntinfo *
225: makemntinfo(struct mntinfo *mi)
1.1 dm 226: {
227: FILE *mfp;
1.6 millert 228: static struct mntinfo *mntinfo;
229: struct mntinfo *newmi, *m;
1.1 dm 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);
1.5 millert 237: return(NULL);
1.1 dm 238: }
239:
240: (void) signal(SIGALRM, wakeup);
241: (void) alarm(timeo);
242: if (setjmp(env)) {
243: message(MT_NERROR, "Timeout getting mount info");
1.5 millert 244: return(NULL);
1.1 dm 245: }
246:
247: mntinfo = mi;
1.9 millert 248: while ((mnt = getmountent(mfp)) != NULL) {
1.1 dm 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) {
1.9 millert 281: for (m = mntinfo; m->mi_nxt; m = m->mi_nxt)
282: continue;
1.1 dm 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: */
1.9 millert 301: mntent_t *
302: getmntpt(char *pathname, struct stat *statbuf, int *isvalid)
1.1 dm 303: {
304: static struct mntinfo *mntinfo = NULL;
305: static struct stat filestat;
306: struct stat *pstat;
307: struct mntinfo *tmpmi;
1.7 mpech 308: mntent_t *mnt;
1.1 dm 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))
1.5 millert 319: return(NULL);
1.1 dm 320:
321: /*
322: * Make mntinfo if it doesn't exist.
323: */
324: if (!mntinfo)
1.5 millert 325: mntinfo = makemntinfo(NULL);
1.1 dm 326:
327: /*
328: * Find the mnt that pathname is on.
329: */
1.9 millert 330: if ((mnt = findmnt(pstat, mntinfo)) != NULL)
1.1 dm 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: */
1.9 millert 337: if ((tmpmi = makemntinfo(mntinfo)) != NULL) {
1.1 dm 338: mntinfo = tmpmi;
1.9 millert 339: if ((mnt = findmnt(pstat, mntinfo)) != NULL)
1.1 dm 340: return(mnt);
341: }
342:
343: error("%s: Could not find mount point", pathname);
1.5 millert 344: return(NULL);
1.1 dm 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: */
1.9 millert 353: int
354: is_nfs_mounted(char *path, struct stat *statbuf, int *isvalid)
1.1 dm 355: {
356: mntent_t *mnt;
357:
358: if ((mnt = (mntent_t *) getmntpt(path, statbuf, isvalid)) == NULL)
359: return(-1);
360:
1.2 dm 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))
1.1 dm 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: */
1.9 millert 377: int
378: is_ro_mounted(char *path, struct stat *statbuf, int *isvalid)
1.1 dm 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: */
1.9 millert 396: int
397: is_symlinked(char *path, struct stat *statbuf, int *isvalid)
1.1 dm 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: */
1.9 millert 421: int
422: getfilesysinfo(char *file, long *freespace, long *freefiles)
1.1 dm 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
1.9 millert 444: #if STATFS_TYPE == STATFS_BSD || STATFS_TYPE == STATFS_44BSD
1.1 dm 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: }