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