Annotation of src/usr.bin/rdistd/filesys.c, Revision 1.3
1.3 ! deraadt 1: /* $OpenBSD$ */
! 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.3 ! deraadt 38: "$OpenBSD: filesys.c,v 1.2 1996/03/05 03:16:17 dm 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: */
122: if (p = (char *) strrchr(file, '/')) {
123: *++p = '.';
124: *++p = CNULL;
125: } else {
126: /*
127: * Couldn't find anything, so give up.
128: */
129: debugmsg(DM_MISC, "Cannot find dir of `%s'",
130: pathname);
131: return((char *) NULL);
132: }
133: continue;
134: } else {
135: error("%s: lstat failed: %s", pathname, SYSERR);
136: return((char *) NULL);
137: }
138: }
139:
140: if (statbuf)
141: bcopy((char *) &filestat, (char *) statbuf, sizeof(filestat));
142:
143: /*
144: * Trim the "/." that we added.
145: */
146: p = &file[strlen(file) - 1];
147: if (*p == '.')
148: *p-- = CNULL;
149: for ( ; p && *p && *p == '/' && p != file; --p)
150: *p = CNULL;
151:
152: /*
153: * If this file is a symlink we really want the parent directory
154: * name in case the symlink points to another filesystem.
155: */
156: if (S_ISLNK(filestat.st_mode))
157: if ((p = (char *) strrchr(file, '/')) && *p+1) {
158: /* Is this / (root)? */
159: if (p == file)
160: file[1] = CNULL;
161: else
162: *p = CNULL;
163: }
164:
165: if (strcmp(pathname, file) == 0)
166: *isvalid = 1;
167:
168: return((file && *file) ? file : (char *)NULL);
169: }
170:
171: #if defined(NFS_CHECK) || defined(RO_CHECK)
172:
173: /*
174: * Find the device that "filest" is on in the "mntinfo" linked list.
175: */
176: mntent_t *findmnt(filest, mntinfo)
177: struct stat *filest;
178: struct mntinfo *mntinfo;
179: {
180: register struct mntinfo *mi;
181:
182: for (mi = mntinfo; mi; mi = mi->mi_nxt) {
183: if (mi->mi_mnt->me_flags & MEFLAG_IGNORE)
184: continue;
185: if (filest->st_dev == mi->mi_statb->st_dev)
186: return(mi->mi_mnt);
187: }
188:
189: return((mntent_t *) NULL);
190: }
191:
192: /*
193: * Is "mnt" a duplicate of any of the mntinfo->mi_mnt elements?
194: */
195: int isdupmnt(mnt, mntinfo)
196: mntent_t *mnt;
197: struct mntinfo *mntinfo;
198: {
199: register struct mntinfo *m;
200:
201: for (m = mntinfo; m; m = m->mi_nxt)
202: if (strcmp(m->mi_mnt->me_path, mnt->me_path) == 0)
203: return(1);
204:
205: return(0);
206: }
207:
208: /*
209: * Alarm clock
210: */
211: void wakeup()
212: {
213: debugmsg(DM_CALL, "wakeup() in filesys.c called");
214: longjmp(env, 1);
215: }
216:
217: /*
218: * Make a linked list of mntinfo structures.
219: * Use "mi" as the base of the list if it's non NULL.
220: */
221: struct mntinfo *makemntinfo(mi)
222: struct mntinfo *mi;
223: {
224: FILE *mfp;
225: static struct mntinfo *mntinfo, *newmi, *m;
226: struct stat mntstat;
227: mntent_t *mnt;
228: int timeo = 310;
229:
230: if (!(mfp = setmountent(MOUNTED_FILE, "r"))) {
231: message(MT_NERROR, "%s: setmntent failed: %s",
232: MOUNTED_FILE, SYSERR);
233: return((struct mntinfo *) NULL);
234: }
235:
236: (void) signal(SIGALRM, wakeup);
237: (void) alarm(timeo);
238: if (setjmp(env)) {
239: message(MT_NERROR, "Timeout getting mount info");
240: return((struct mntinfo *) NULL);
241: }
242:
243: mntinfo = mi;
244: while (mnt = getmountent(mfp)) {
245: debugmsg(DM_MISC, "mountent = '%s' (%s)",
246: mnt->me_path, mnt->me_type);
247:
248: /*
249: * Make sure we don't already have it for some reason
250: */
251: if (isdupmnt(mnt, mntinfo))
252: continue;
253:
254: /*
255: * Get stat info
256: */
257: if (stat(mnt->me_path, &mntstat) != 0) {
258: message(MT_WARNING, "%s: Cannot stat filesystem: %s",
259: mnt->me_path, SYSERR);
260: continue;
261: }
262:
263: /*
264: * Create new entry
265: */
266: newmi = (struct mntinfo *) xcalloc(1, sizeof(struct mntinfo));
267: newmi->mi_mnt = newmountent(mnt);
268: newmi->mi_statb =
269: (struct stat *) xcalloc(1, sizeof(struct stat));
270: bcopy((char *) &mntstat, (char *) newmi->mi_statb,
271: sizeof(struct stat));
272:
273: /*
274: * Add entry to list
275: */
276: if (mntinfo) {
277: for (m = mntinfo; m && m->mi_nxt; m = m->mi_nxt);
278: m->mi_nxt = newmi;
279: } else
280: mntinfo = newmi;
281: }
282:
283: (void) alarm(0);
284: (void) endmountent(mfp);
285:
286: return(mntinfo);
287: }
288:
289: /*
290: * Given a name like /usr/src/etc/foo.c returns the mntent
291: * structure for the file system it lives in.
292: *
293: * If "statbuf" is not NULL it is used as the stat buffer too avoid
294: * stat()'ing the file again back in server.c.
295: */
296: mntent_t *getmntpt(pathname, statbuf, isvalid)
297: char *pathname;
298: struct stat *statbuf;
299: int *isvalid;
300: {
301: static struct mntinfo *mntinfo = NULL;
302: static struct stat filestat;
303: struct stat *pstat;
304: struct mntinfo *tmpmi;
305: register mntent_t *mnt;
306:
307: /*
308: * Use the supplied stat buffer if not NULL or our own.
309: */
310: if (statbuf)
311: pstat = statbuf;
312: else
313: pstat = &filestat;
314:
315: if (!find_file(pathname, pstat, isvalid))
316: return((mntent_t *) NULL);
317:
318: /*
319: * Make mntinfo if it doesn't exist.
320: */
321: if (!mntinfo)
322: mntinfo = makemntinfo((struct mntinfo *) NULL);
323:
324: /*
325: * Find the mnt that pathname is on.
326: */
327: if (mnt = findmnt(pstat, mntinfo))
328: return(mnt);
329:
330: /*
331: * We failed to find correct mnt, so maybe it's a newly
332: * mounted filesystem. We rebuild mntinfo and try again.
333: */
334: if (tmpmi = makemntinfo(mntinfo)) {
335: mntinfo = tmpmi;
336: if (mnt = findmnt(pstat, mntinfo))
337: return(mnt);
338: }
339:
340: error("%s: Could not find mount point", pathname);
341: return((mntent_t *) NULL);
342: }
343:
344: #endif /* NFS_CHECK || RO_CHECK */
345:
346: #if defined(NFS_CHECK)
347: /*
348: * Is "path" NFS mounted? Return 1 if it is, 0 if not, or -1 on error.
349: */
350: int is_nfs_mounted(path, statbuf, isvalid)
351: char *path;
352: struct stat *statbuf;
353: int *isvalid;
354: {
355: mntent_t *mnt;
356:
357: if ((mnt = (mntent_t *) getmntpt(path, statbuf, isvalid)) == NULL)
358: return(-1);
359:
1.2 dm 360: /*
361: * We treat "cachefs" just like NFS
362: */
363: if ((strcmp(mnt->me_type, METYPE_NFS) == 0) ||
364: (strcmp(mnt->me_type, "cachefs") == 0))
1.1 dm 365: return(1);
366:
367: return(0);
368: }
369: #endif /* NFS_CHECK */
370:
371: #if defined(RO_CHECK)
372: /*
373: * Is "path" on a read-only mounted filesystem?
374: * Return 1 if it is, 0 if not, or -1 on error.
375: */
376: int is_ro_mounted(path, statbuf, isvalid)
377: char *path;
378: struct stat *statbuf;
379: int *isvalid;
380: {
381: mntent_t *mnt;
382:
383: if ((mnt = (mntent_t *) getmntpt(path, statbuf, isvalid)) == NULL)
384: return(-1);
385:
386: if (mnt->me_flags & MEFLAG_READONLY)
387: return(1);
388:
389: return(0);
390: }
391: #endif /* RO_CHECK */
392:
393: /*
394: * Is "path" a symlink?
395: * Return 1 if it is, 0 if not, or -1 on error.
396: */
397: int is_symlinked(path, statbuf, isvalid)
398: /*ARGSUSED*/
399: char *path;
400: struct stat *statbuf;
401: int *isvalid;
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: */
425: int getfilesysinfo(file, freespace, freefiles)
426: char *file;
427: long *freespace;
428: long *freefiles;
429: {
430: #if defined(STATFS_TYPE)
431: static statfs_t statfsbuf;
432: char *mntpt;
433: int t, r;
434:
435: /*
436: * Get the mount point of the file.
437: */
438: mntpt = find_file(file, NULL, &t);
439: if (!mntpt) {
440: debugmsg(DM_MISC, "unknown mount point for `%s'", file);
441: return(-1);
442: }
443:
444: /*
445: * Stat the filesystem (system specific)
446: */
447: #if STATFS_TYPE == STATFS_SYSV
448: r = statfs(mntpt, &statfsbuf, sizeof(statfs_t), 0);
449: #endif
450: #if STATFS_TYPE == STATFS_BSD
451: r = statfs(mntpt, &statfsbuf);
452: #endif
453: #if STATFS_TYPE == STATFS_OSF1
454: r = statfs(mntpt, &statfsbuf, sizeof(statfs_t));
455: #endif
456:
457: if (r < 0) {
458: error("%s: Cannot statfs filesystem: %s.", mntpt, SYSERR);
459: return(-1);
460: }
461:
462: /*
463: * If values are < 0, then assume the value is unsupported
464: * or unavailable for that filesystem type.
465: */
466: if (statfsbuf.f_bavail >= 0)
467: *freespace = (statfsbuf.f_bavail * (statfsbuf.f_bsize / 512))
468: / 2;
469:
470: /*
471: * BROKEN_STATFS means that statfs() does not set fields
472: * to < 0 if the field is unsupported for the filesystem type.
473: */
474: #if defined(BROKEN_STATFS)
475: if (statfsbuf.f_ffree > 0)
476: #else
477: if (statfsbuf.f_ffree >= 0)
478: #endif /* BROKEN_STATFS */
479: *freefiles = statfsbuf.f_ffree;
480:
481: #else /* !STATFS_TYPE */
482:
483: *freespace = *freefiles = -1;
484:
485: #endif /* STATFS_TYPE */
486:
487: return(0);
488: }