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