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