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