Annotation of src/usr.bin/rdistd/filesys.c, Revision 1.4
1.4 ! millert 1: /* $OpenBSD: filesys.c,v 1.3 1996/06/26 05:38:21 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:
36: #ifndef lint
37: static char RCSid[] =
1.4 ! millert 38: "$OpenBSD: filesys.c,v 1.3 1996/06/26 05:38:21 deraadt Exp $";
1.1 dm 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: */
1.4 ! millert 122: if ((p = (char *) strrchr(file, '/'))) {
! 123: if (strcmp(p, "/.") == 0) {
! 124: *p = CNULL;
! 125: } else {
! 126: *++p = '.';
! 127: *++p = CNULL;
! 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);
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:
1.2 dm 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))
1.1 dm 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: }