Annotation of src/usr.bin/rdistd/filesys.c, Revision 1.2
1.1 dm 1: /*
2: * Copyright (c) 1983 Regents of the University of California.
3: * All rights reserved.
4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: * 1. Redistributions of source code must retain the above copyright
9: * notice, this list of conditions and the following disclaimer.
10: * 2. Redistributions in binary form must reproduce the above copyright
11: * notice, this list of conditions and the following disclaimer in the
12: * documentation and/or other materials provided with the distribution.
13: * 3. All advertising materials mentioning features or use of this software
14: * must display the following acknowledgement:
15: * This product includes software developed by the University of
16: * California, Berkeley and its contributors.
17: * 4. Neither the name of the University nor the names of its contributors
18: * may be used to endorse or promote products derived from this software
19: * without specific prior written permission.
20: *
21: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31: * SUCH DAMAGE.
32: */
33:
34: #ifndef lint
35: static char RCSid[] =
1.2 ! dm 36: "$Id: filesys.c,v 6.24 1996/01/30 01:57:07 mcooper Exp $";
1.1 dm 37:
38: static char sccsid[] = "@(#)filesys.c";
39:
40: static char copyright[] =
41: "@(#) Copyright (c) 1983 Regents of the University of California.\n\
42: All rights reserved.\n";
43: #endif /* not lint */
44:
45: /*
46: * This file contains functions dealing with getting info
47: * about mounted filesystems.
48: */
49:
50: #include "defs.h"
51: #include "filesys.h"
52:
53: jmp_buf env;
54:
55: /*
56: * Given a pathname, find the fullest component that exists.
57: * If statbuf is not NULL, set it to point at our stat buffer.
58: */
59: char *find_file(pathname, statbuf, isvalid)
60: char *pathname;
61: struct stat *statbuf;
62: int *isvalid;
63: {
64: static char last_pathname[MAXPATHLEN];
65: static char file[MAXPATHLEN + 3];
66: static struct stat filestat;
67: register char *p;
68:
69: /*
70: * Mark the statbuf as invalid to start with.
71: */
72: *isvalid = 0;
73:
74: /*
75: * If this is the same pathname as the last time, and
76: * the file buffer is valid and we're doing the same stat()
77: * or lstat(), then set statbuf to the last filestat and
78: * return the last file we found.
79: */
80: if (strcmp(pathname, last_pathname) == 0 && file[0]) {
81: if (statbuf)
82: statbuf = &filestat;
83: if (strcmp(pathname, file) == 0)
84: *isvalid = 1;
85: return(file);
86: }
87:
88: if ((int)strlen(pathname) > sizeof(file)+3) {
89: error("%s: Name to large for buffer.", pathname);
90: return((char *) NULL);
91: }
92:
93: /*
94: * Save for next time
95: */
96: (void) strcpy(last_pathname, pathname);
97:
98: if (*pathname == '/')
99: (void) strcpy(file, pathname);
100: else {
101: /*
102: * Ensure we have a directory (".") in our path
103: * so we have something to stat in case the file
104: * does not exist.
105: */
106: (void) strcpy(file, "./");
107: (void) strcat(file, pathname);
108: }
109:
110: while (lstat(file, &filestat) != 0) {
111: /*
112: * Trim the last part of the pathname to try next level up
113: */
114: if (errno == ENOENT) {
115: /*
116: * Trim file name to get directory name.
117: * Normally we want to change /dir1/dir2/file
118: * into "/dir1/dir2/."
119: */
120: if (p = (char *) strrchr(file, '/')) {
121: *++p = '.';
122: *++p = CNULL;
123: } else {
124: /*
125: * Couldn't find anything, so give up.
126: */
127: debugmsg(DM_MISC, "Cannot find dir of `%s'",
128: pathname);
129: return((char *) NULL);
130: }
131: continue;
132: } else {
133: error("%s: lstat failed: %s", pathname, SYSERR);
134: return((char *) NULL);
135: }
136: }
137:
138: if (statbuf)
139: bcopy((char *) &filestat, (char *) statbuf, sizeof(filestat));
140:
141: /*
142: * Trim the "/." that we added.
143: */
144: p = &file[strlen(file) - 1];
145: if (*p == '.')
146: *p-- = CNULL;
147: for ( ; p && *p && *p == '/' && p != file; --p)
148: *p = CNULL;
149:
150: /*
151: * If this file is a symlink we really want the parent directory
152: * name in case the symlink points to another filesystem.
153: */
154: if (S_ISLNK(filestat.st_mode))
155: if ((p = (char *) strrchr(file, '/')) && *p+1) {
156: /* Is this / (root)? */
157: if (p == file)
158: file[1] = CNULL;
159: else
160: *p = CNULL;
161: }
162:
163: if (strcmp(pathname, file) == 0)
164: *isvalid = 1;
165:
166: return((file && *file) ? file : (char *)NULL);
167: }
168:
169: #if defined(NFS_CHECK) || defined(RO_CHECK)
170:
171: /*
172: * Find the device that "filest" is on in the "mntinfo" linked list.
173: */
174: mntent_t *findmnt(filest, mntinfo)
175: struct stat *filest;
176: struct mntinfo *mntinfo;
177: {
178: register struct mntinfo *mi;
179:
180: for (mi = mntinfo; mi; mi = mi->mi_nxt) {
181: if (mi->mi_mnt->me_flags & MEFLAG_IGNORE)
182: continue;
183: if (filest->st_dev == mi->mi_statb->st_dev)
184: return(mi->mi_mnt);
185: }
186:
187: return((mntent_t *) NULL);
188: }
189:
190: /*
191: * Is "mnt" a duplicate of any of the mntinfo->mi_mnt elements?
192: */
193: int isdupmnt(mnt, mntinfo)
194: mntent_t *mnt;
195: struct mntinfo *mntinfo;
196: {
197: register struct mntinfo *m;
198:
199: for (m = mntinfo; m; m = m->mi_nxt)
200: if (strcmp(m->mi_mnt->me_path, mnt->me_path) == 0)
201: return(1);
202:
203: return(0);
204: }
205:
206: /*
207: * Alarm clock
208: */
209: void wakeup()
210: {
211: debugmsg(DM_CALL, "wakeup() in filesys.c called");
212: longjmp(env, 1);
213: }
214:
215: /*
216: * Make a linked list of mntinfo structures.
217: * Use "mi" as the base of the list if it's non NULL.
218: */
219: struct mntinfo *makemntinfo(mi)
220: struct mntinfo *mi;
221: {
222: FILE *mfp;
223: static struct mntinfo *mntinfo, *newmi, *m;
224: struct stat mntstat;
225: mntent_t *mnt;
226: int timeo = 310;
227:
228: if (!(mfp = setmountent(MOUNTED_FILE, "r"))) {
229: message(MT_NERROR, "%s: setmntent failed: %s",
230: MOUNTED_FILE, SYSERR);
231: return((struct mntinfo *) NULL);
232: }
233:
234: (void) signal(SIGALRM, wakeup);
235: (void) alarm(timeo);
236: if (setjmp(env)) {
237: message(MT_NERROR, "Timeout getting mount info");
238: return((struct mntinfo *) NULL);
239: }
240:
241: mntinfo = mi;
242: while (mnt = getmountent(mfp)) {
243: debugmsg(DM_MISC, "mountent = '%s' (%s)",
244: mnt->me_path, mnt->me_type);
245:
246: /*
247: * Make sure we don't already have it for some reason
248: */
249: if (isdupmnt(mnt, mntinfo))
250: continue;
251:
252: /*
253: * Get stat info
254: */
255: if (stat(mnt->me_path, &mntstat) != 0) {
256: message(MT_WARNING, "%s: Cannot stat filesystem: %s",
257: mnt->me_path, SYSERR);
258: continue;
259: }
260:
261: /*
262: * Create new entry
263: */
264: newmi = (struct mntinfo *) xcalloc(1, sizeof(struct mntinfo));
265: newmi->mi_mnt = newmountent(mnt);
266: newmi->mi_statb =
267: (struct stat *) xcalloc(1, sizeof(struct stat));
268: bcopy((char *) &mntstat, (char *) newmi->mi_statb,
269: sizeof(struct stat));
270:
271: /*
272: * Add entry to list
273: */
274: if (mntinfo) {
275: for (m = mntinfo; m && m->mi_nxt; m = m->mi_nxt);
276: m->mi_nxt = newmi;
277: } else
278: mntinfo = newmi;
279: }
280:
281: (void) alarm(0);
282: (void) endmountent(mfp);
283:
284: return(mntinfo);
285: }
286:
287: /*
288: * Given a name like /usr/src/etc/foo.c returns the mntent
289: * structure for the file system it lives in.
290: *
291: * If "statbuf" is not NULL it is used as the stat buffer too avoid
292: * stat()'ing the file again back in server.c.
293: */
294: mntent_t *getmntpt(pathname, statbuf, isvalid)
295: char *pathname;
296: struct stat *statbuf;
297: int *isvalid;
298: {
299: static struct mntinfo *mntinfo = NULL;
300: static struct stat filestat;
301: struct stat *pstat;
302: struct mntinfo *tmpmi;
303: register mntent_t *mnt;
304:
305: /*
306: * Use the supplied stat buffer if not NULL or our own.
307: */
308: if (statbuf)
309: pstat = statbuf;
310: else
311: pstat = &filestat;
312:
313: if (!find_file(pathname, pstat, isvalid))
314: return((mntent_t *) NULL);
315:
316: /*
317: * Make mntinfo if it doesn't exist.
318: */
319: if (!mntinfo)
320: mntinfo = makemntinfo((struct mntinfo *) NULL);
321:
322: /*
323: * Find the mnt that pathname is on.
324: */
325: if (mnt = findmnt(pstat, mntinfo))
326: return(mnt);
327:
328: /*
329: * We failed to find correct mnt, so maybe it's a newly
330: * mounted filesystem. We rebuild mntinfo and try again.
331: */
332: if (tmpmi = makemntinfo(mntinfo)) {
333: mntinfo = tmpmi;
334: if (mnt = findmnt(pstat, mntinfo))
335: return(mnt);
336: }
337:
338: error("%s: Could not find mount point", pathname);
339: return((mntent_t *) NULL);
340: }
341:
342: #endif /* NFS_CHECK || RO_CHECK */
343:
344: #if defined(NFS_CHECK)
345: /*
346: * Is "path" NFS mounted? Return 1 if it is, 0 if not, or -1 on error.
347: */
348: int is_nfs_mounted(path, statbuf, isvalid)
349: char *path;
350: struct stat *statbuf;
351: int *isvalid;
352: {
353: mntent_t *mnt;
354:
355: if ((mnt = (mntent_t *) getmntpt(path, statbuf, isvalid)) == NULL)
356: return(-1);
357:
1.2 ! dm 358: /*
! 359: * We treat "cachefs" just like NFS
! 360: */
! 361: if ((strcmp(mnt->me_type, METYPE_NFS) == 0) ||
! 362: (strcmp(mnt->me_type, "cachefs") == 0))
1.1 dm 363: return(1);
364:
365: return(0);
366: }
367: #endif /* NFS_CHECK */
368:
369: #if defined(RO_CHECK)
370: /*
371: * Is "path" on a read-only mounted filesystem?
372: * Return 1 if it is, 0 if not, or -1 on error.
373: */
374: int is_ro_mounted(path, statbuf, isvalid)
375: char *path;
376: struct stat *statbuf;
377: int *isvalid;
378: {
379: mntent_t *mnt;
380:
381: if ((mnt = (mntent_t *) getmntpt(path, statbuf, isvalid)) == NULL)
382: return(-1);
383:
384: if (mnt->me_flags & MEFLAG_READONLY)
385: return(1);
386:
387: return(0);
388: }
389: #endif /* RO_CHECK */
390:
391: /*
392: * Is "path" a symlink?
393: * Return 1 if it is, 0 if not, or -1 on error.
394: */
395: int is_symlinked(path, statbuf, isvalid)
396: /*ARGSUSED*/
397: char *path;
398: struct stat *statbuf;
399: int *isvalid;
400: {
401: static struct stat stb;
402:
403: if (!(*isvalid)) {
404: if (lstat(path, &stb) != 0)
405: return(-1);
406: statbuf = &stb;
407: }
408:
409: if (S_ISLNK(statbuf->st_mode))
410: return(1);
411:
412: return(0);
413: }
414:
415: /*
416: * Get filesystem information for "file". Set freespace
417: * to the amount of free (available) space and number of free
418: * files (inodes) on the filesystem "file" resides on.
419: * Returns 0 on success or -1 on failure.
420: * Filesystem values < 0 indicate unsupported or unavailable
421: * information.
422: */
423: int getfilesysinfo(file, freespace, freefiles)
424: char *file;
425: long *freespace;
426: long *freefiles;
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
448: #if STATFS_TYPE == STATFS_BSD
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: }