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