GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/rdistd/filesys.c Lines: 0 129 0.0 %
Date: 2016-12-06 Branches: 0 102 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: filesys.c,v 1.19 2015/01/21 04:08:37 guenther 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. Neither the name of the University nor the names of its contributors
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
32
#include <sys/types.h>
33
#include <sys/mount.h>
34
#include <errno.h>
35
#include <string.h>
36
#include <unistd.h>
37
38
#include "server.h"
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
 */
52
char *
53
find_file(char *pathname, struct stat *statbuf, int *isvalid)
54
{
55
	static char last_pathname[PATH_MAX];
56
	static char file[PATH_MAX + 3];
57
	static struct stat filestat;
58
	char *p;
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
79
	if (strlen(pathname) > sizeof(file) + 3) {
80
		error("%s: Name to large for buffer.", pathname);
81
	        return(NULL);
82
	}
83
84
	/*
85
	 * Save for next time
86
	 */
87
	(void) strlcpy(last_pathname, pathname, sizeof(last_pathname));
88
89
	if (*pathname == '/')
90
	        (void) strlcpy(file, pathname, sizeof(file));
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
		 */
97
	        (void) strlcpy(file, "./", sizeof(file));
98
		(void) strlcat(file, pathname, sizeof(file));
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
			 */
111
			if ((p = strrchr(file, '/')) != NULL) {
112
				if (strcmp(p, "/.") == 0) {
113
					*p = CNULL;
114
				} else {
115
					*++p = '.';
116
					*++p = CNULL;
117
				}
118
			} else {
119
				/*
120
				 * Couldn't find anything, so give up.
121
				 */
122
				debugmsg(DM_MISC, "Cannot find dir of `%s'",
123
					 pathname);
124
				return(NULL);
125
			}
126
			continue;
127
		} else {
128
			error("%s: lstat failed: %s", pathname, SYSERR);
129
			return(NULL);
130
		}
131
	}
132
133
	if (statbuf)
134
		bcopy(&filestat, 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 = 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
161
	return(*file ? file : NULL);
162
}
163
164
165
/*
166
 * Find the device that "filest" is on in the "mntinfo" linked list.
167
 */
168
mntent_t *
169
findmnt(struct stat *filest, struct mntinfo *mntinfo)
170
{
171
	struct mntinfo *mi;
172
173
	for (mi = mntinfo; mi; mi = mi->mi_nxt) {
174
		if (mi->mi_mnt->me_flags & MEFLAG_IGNORE)
175
			continue;
176
		if (filest->st_dev == mi->mi_dev)
177
			return(mi->mi_mnt);
178
	}
179
180
	return(NULL);
181
}
182
183
/*
184
 * Is "mnt" a duplicate of any of the mntinfo->mi_mnt elements?
185
 */
186
int
187
isdupmnt(mntent_t *mnt, struct mntinfo *mntinfo)
188
{
189
	struct mntinfo *m;
190
191
	for (m = mntinfo; m; m = m->mi_nxt)
192
		if (strcmp(m->mi_mnt->me_path, mnt->me_path) == 0)
193
			return(1);
194
195
	return(0);
196
}
197
198
/*
199
 * Alarm clock
200
 */
201
void
202
wakeup(int dummy)
203
{
204
	debugmsg(DM_CALL, "wakeup() in filesys.c called");
205
	longjmp(env, 1);
206
}
207
208
/*
209
 * Make a linked list of mntinfo structures.
210
 * Use "mi" as the base of the list if it's non NULL.
211
 */
212
struct mntinfo *
213
makemntinfo(struct mntinfo *mi)
214
{
215
	static struct mntinfo *mntinfo;
216
	struct mntinfo *newmi, *m;
217
	struct stat mntstat;
218
	mntent_t *mnt;
219
	int timeo = 310;
220
221
	if (!setmountent()) {
222
		message(MT_NERROR, "setmntent failed: %s", SYSERR);
223
		return(NULL);
224
	}
225
226
	(void) signal(SIGALRM, wakeup);
227
	(void) alarm(timeo);
228
	if (setjmp(env)) {
229
		message(MT_NERROR, "Timeout getting mount info");
230
		return(NULL);
231
	}
232
233
	mntinfo = mi;
234
	while ((mnt = getmountent()) != NULL) {
235
		debugmsg(DM_MISC, "mountent = '%s'", mnt->me_path);
236
237
		/*
238
		 * Make sure we don't already have it for some reason
239
		 */
240
		if (isdupmnt(mnt, mntinfo))
241
			continue;
242
243
		/*
244
		 * Get stat info
245
		 */
246
		if (stat(mnt->me_path, &mntstat) != 0) {
247
			message(MT_WARNING, "%s: Cannot stat filesystem: %s",
248
				mnt->me_path, SYSERR);
249
			continue;
250
		}
251
252
		/*
253
		 * Create new entry
254
		 */
255
		newmi = xcalloc(1, sizeof(*newmi));
256
		newmi->mi_mnt = newmountent(mnt);
257
		newmi->mi_dev = mntstat.st_dev;
258
259
		/*
260
		 * Add entry to list
261
		 */
262
		if (mntinfo) {
263
			for (m = mntinfo; m->mi_nxt; m = m->mi_nxt)
264
				continue;
265
			m->mi_nxt = newmi;
266
		} else
267
			mntinfo = newmi;
268
	}
269
270
	alarm(0);
271
	endmountent();
272
273
	return(mntinfo);
274
}
275
276
/*
277
 * Given a name like /usr/src/etc/foo.c returns the mntent
278
 * structure for the file system it lives in.
279
 *
280
 * If "statbuf" is not NULL it is used as the stat buffer too avoid
281
 * stat()'ing the file again back in server.c.
282
 */
283
mntent_t *
284
getmntpt(char *pathname, struct stat *statbuf, int *isvalid)
285
{
286
	static struct mntinfo *mntinfo = NULL;
287
	static struct stat filestat;
288
	struct stat *pstat;
289
	struct mntinfo *tmpmi;
290
	mntent_t *mnt;
291
292
	/*
293
	 * Use the supplied stat buffer if not NULL or our own.
294
	 */
295
	if (statbuf)
296
		pstat = statbuf;
297
	else
298
		pstat = &filestat;
299
300
	if (!find_file(pathname, pstat, isvalid))
301
	        return(NULL);
302
303
	/*
304
	 * Make mntinfo if it doesn't exist.
305
	 */
306
	if (!mntinfo)
307
		mntinfo = makemntinfo(NULL);
308
309
	/*
310
	 * Find the mnt that pathname is on.
311
	 */
312
	if ((mnt = findmnt(pstat, mntinfo)) != NULL)
313
		return(mnt);
314
315
	/*
316
	 * We failed to find correct mnt, so maybe it's a newly
317
	 * mounted filesystem.  We rebuild mntinfo and try again.
318
	 */
319
	if ((tmpmi = makemntinfo(mntinfo)) != NULL) {
320
		mntinfo = tmpmi;
321
		if ((mnt = findmnt(pstat, mntinfo)) != NULL)
322
			return(mnt);
323
	}
324
325
	error("%s: Could not find mount point", pathname);
326
	return(NULL);
327
}
328
329
330
/*
331
 * Is "path" NFS mounted?  Return 1 if it is, 0 if not, or -1 on error.
332
 */
333
int
334
is_nfs_mounted(char *path, struct stat *statbuf, int *isvalid)
335
{
336
	mntent_t *mnt;
337
338
	if ((mnt = getmntpt(path, statbuf, isvalid)) == NULL)
339
		return(-1);
340
341
	if (mnt->me_flags & MEFLAG_NFS)
342
		return(1);
343
344
	return(0);
345
}
346
347
/*
348
 * Is "path" on a read-only mounted filesystem?
349
 * Return 1 if it is, 0 if not, or -1 on error.
350
 */
351
int
352
is_ro_mounted(char *path, struct stat *statbuf, int *isvalid)
353
{
354
	mntent_t *mnt;
355
356
	if ((mnt = getmntpt(path, statbuf, isvalid)) == NULL)
357
		return(-1);
358
359
	if (mnt->me_flags & MEFLAG_READONLY)
360
		return(1);
361
362
	return(0);
363
}
364
365
/*
366
 * Is "path" a symlink?
367
 * Return 1 if it is, 0 if not, or -1 on error.
368
 */
369
int
370
is_symlinked(char *path, struct stat *statbuf, int *isvalid)
371
{
372
	static struct stat stb;
373
374
	if (!(*isvalid)) {
375
		if (lstat(path, &stb) != 0)
376
			return(-1);
377
		statbuf = &stb;
378
	}
379
380
	if (S_ISLNK(statbuf->st_mode))
381
		return(1);
382
383
	return(0);
384
}
385
386
/*
387
 * Get filesystem information for "file".  Set freespace
388
 * to the amount of free (available) space and number of free
389
 * files (inodes) on the filesystem "file" resides on.
390
 * Returns 0 on success or -1 on failure.
391
 * Filesystem values < 0 indicate unsupported or unavailable
392
 * information.
393
 */
394
int
395
getfilesysinfo(char *file, int64_t *freespace, int64_t *freefiles)
396
{
397
	struct statfs statfsbuf;
398
	char *mntpt;
399
	int64_t val;
400
	int t, r;
401
402
	/*
403
	 * Get the mount point of the file.
404
	 */
405
	mntpt = find_file(file, NULL, &t);
406
	if (!mntpt) {
407
		debugmsg(DM_MISC, "unknown mount point for `%s'", file);
408
		return(-1);
409
	}
410
411
	r = statfs(mntpt, &statfsbuf);
412
	if (r < 0) {
413
		error("%s: Cannot statfs filesystem: %s.", mntpt, SYSERR);
414
		return(-1);
415
	}
416
417
	/*
418
	 * If values are < 0, then assume the value is unsupported
419
	 * or unavailable for that filesystem type.
420
	 */
421
	val = -1;
422
	if (statfsbuf.f_bavail >= 0)
423
		val = (statfsbuf.f_bavail * (statfsbuf.f_bsize / 512)) / 2;
424
	*freespace = val;
425
426
	val = -1;
427
	if (statfsbuf.f_favail >= 0)
428
		val = statfsbuf.f_favail;
429
	*freefiles = val;
430
431
	return(0);
432
}