xref: /titanic_44/usr/src/cmd/fs.d/cachefs/common/subr.c (revision 9dd0f810214fdc8e1af881a9a5c4b6927629ff9e)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Common subroutines used by the programs in these subdirectories.
31  */
32 
33 #include <locale.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <assert.h>
38 #include <unistd.h>
39 #include <limits.h>
40 #include <errno.h>
41 #include <wait.h>
42 #include <ctype.h>
43 #include <fcntl.h>
44 #include <ftw.h>
45 #include <dirent.h>
46 #include <sys/types.h>
47 #include <sys/time.h>
48 #include <utmpx.h>
49 #include <sys/uio.h>
50 #include <sys/param.h>
51 #include <sys/stat.h>
52 #include <sys/fcntl.h>
53 #include <sys/mount.h>
54 #include <sys/mntent.h>
55 #include <sys/mnttab.h>
56 #include <sys/mman.h>
57 #include <sys/fs/cachefs_fs.h>
58 #include "subr.h"
59 
60 /*
61  *
62  *			cachefs_dir_lock
63  *
64  * Description:
65  *	Gets a lock on the cache directory.
66  *	To release the lock, call cachefs_dir_unlock
67  *	with the returned value.
68  * Arguments:
69  *	cachedirp	name of the cache directory
70  *	shared		1 if shared, 0 if not
71  * Returns:
72  *	Returns -1 if the lock cannot be obtained immediatly.
73  *	If the lock is obtained, returns a value >= 0.
74  * Preconditions:
75  *	precond(cachedirp)
76  */
77 
78 int
79 cachefs_dir_lock(const char *cachedirp, int shared)
80 {
81 	int fd;
82 	int xx;
83 	int len;
84 	char buf[MAXPATHLEN];
85 	struct flock fl;
86 	char *strp;
87 	struct stat statb;
88 
89 	/* make a path prefix to the cache directory lock file */
90 	strp = CACHEFS_ROOTRUN;
91 	xx = stat(strp, &statb);
92 	if ((xx < 0) || ((statb.st_mode & S_IFMT) != S_IFDIR))
93 		strp = "/tmp";
94 
95 	/* won't overflow */
96 	len = snprintf(buf, sizeof (buf), "%s/%s", strp, CACHEFS_LOCKDIR_PRE);
97 
98 	if (strlcat(buf, cachedirp, sizeof (buf)) >= sizeof (buf)) {
99 		pr_err(gettext("Cache directory name %s is too long"),
100 			cachedirp);
101 		return (-1);
102 	}
103 
104 	strp = &buf[len];
105 
106 	while (strp = strchr(strp, '/')) { 	/* convert path to a file */
107 		*strp = '_';
108 	}
109 
110 	/*
111 	 * Create and open the cache directory lock file.
112 	 * This file will be <2G.
113 	 */
114 	fd = open(buf, O_RDWR | O_CREAT, 0700);
115 	if (fd == -1) {
116 		pr_err(gettext("Cannot open lock file %s"), buf);
117 		return (-1);
118 	}
119 
120 	/* try to set the lock */
121 	fl.l_type = (shared == 1) ? F_RDLCK : F_WRLCK;
122 	fl.l_whence = 0;
123 	fl.l_start = 1024;
124 	fl.l_len = 1024;
125 	fl.l_sysid = 0;
126 	fl.l_pid = 0;
127 	/* CACHEFS_LOCK_FILE will be <2GB */
128 	xx = fcntl(fd, F_SETLKW, &fl);
129 	if (xx == -1) {
130 		if (errno == EAGAIN) {
131 			pr_err(gettext("Cannot gain access to the "
132 			    "cache directory %s."), cachedirp);
133 		} else {
134 			pr_err(gettext("Unexpected failure on lock file %s %s"),
135 			    buf, strerror(errno));
136 		}
137 		close(fd);
138 		return (-1);
139 	}
140 
141 	/* return the file descriptor which can be used to release the lock */
142 	return (fd);
143 }
144 
145 /*
146  *
147  *			cachefs_dir_unlock
148  *
149  * Description:
150  *	Releases an advisory lock on the cache directory.
151  * Arguments:
152  *	fd	cookie returned by cachefs_dir_lock
153  * Returns:
154  *	Returns -1 if the lock cannot be released or 0 for success.
155  * Preconditions:
156  */
157 
158 int
159 cachefs_dir_unlock(int fd)
160 {
161 	struct flock fl;
162 	int error = 0;
163 	int xx;
164 
165 	/* release the lock */
166 	fl.l_type = F_UNLCK;
167 	fl.l_whence = 0;
168 	fl.l_start = 1024;
169 	fl.l_len = 1024;
170 	fl.l_sysid = 0;
171 	fl.l_pid = 0;
172 	/* fd will be <2GB */
173 	xx = fcntl(fd, F_SETLK, &fl);
174 	if (xx == -1) {
175 		pr_err(gettext("Unexpected failure releasing lock file %s"),
176 			strerror(errno));
177 		error = -1;
178 	}
179 
180 	/* close the lock file */
181 	close(fd);
182 
183 	return (error);
184 }
185 
186 /*
187  *
188  *			cachefs_label_file_get
189  *
190  * Description:
191  *	Gets the contents of a cache label file.
192  *	Performs error checking on the file.
193  * Arguments:
194  *	filep	name of the cache label file
195  *	clabelp	where to put the file contents
196  * Returns:
197  *	Returns 0 for success or -1 if an error occurs.
198  * Preconditions:
199  *	precond(filep)
200  *	precond(clabelp)
201  */
202 
203 int
204 cachefs_label_file_get(const char *filep, struct cache_label *clabelp)
205 {
206 	int xx;
207 	int fd;
208 	struct stat64 statinfo;
209 
210 	/* get info on the file */
211 	xx = lstat64(filep, &statinfo);
212 	if (xx == -1) {
213 		if (errno != ENOENT) {
214 			pr_err(gettext("Cannot stat file %s: %s"),
215 			    filep, strerror(errno));
216 		} else {
217 			pr_err(gettext("File %s does not exist."), filep);
218 		}
219 
220 		return (-1);
221 	}
222 
223 	/* if the file is the wrong type */
224 	if (!S_ISREG(statinfo.st_mode)) {
225 		pr_err(gettext("Cache label file %s corrupted"), filep);
226 		return (-1);
227 	}
228 
229 	/* if the file is the wrong size; it will be <2GB */
230 	if (statinfo.st_size != (offset_t)sizeof (struct cache_label)) {
231 		pr_err(gettext("Cache label file %s wrong size"), filep);
232 		return (-1);
233 	}
234 
235 	/* open the cache label file */
236 	fd = open(filep, O_RDONLY);
237 	if (fd == -1) {
238 		pr_err(gettext("Error opening %s: %s"), filep,
239 		    strerror(errno));
240 		return (-1);
241 	}
242 
243 	/* read the current set of parameters */
244 	xx = read(fd, clabelp, sizeof (struct cache_label));
245 	if (xx != sizeof (struct cache_label)) {
246 		pr_err(gettext("Reading %s failed: %s\n"), filep,
247 		    strerror(errno));
248 		close(fd);
249 		return (-1);
250 	}
251 	close(fd);
252 
253 	/* return success */
254 	return (0);
255 }
256 
257 /*
258  *
259  *			cachefs_label_file_put
260  *
261  * Description:
262  *	Outputs the contents of a cache label object to a file.
263  * Arguments:
264  *	filep	name of the cache label file
265  *	clabelp	where to get the file contents
266  * Returns:
267  *	Returns 0 for success or -1 if an error occurs.
268  * Preconditions:
269  *	precond(filep)
270  *	precond(clabelp)
271  */
272 
273 int
274 cachefs_label_file_put(const char *filep, struct cache_label *clabelp)
275 {
276 	int xx;
277 	int fd;
278 
279 	/* get rid of the file if it already exists */
280 	xx = unlink(filep);
281 	if ((xx == -1) && (errno != ENOENT)) {
282 		pr_err(gettext("Could not remove %s: %s"), filep,
283 		    strerror(errno));
284 		return (-1);
285 	}
286 
287 	/* open the cache label file; this file will be <2GB */
288 	fd = open(filep, O_CREAT | O_RDWR, 0600);
289 	if (fd == -1) {
290 		pr_err(gettext("Error creating %s: %s"), filep,
291 		    strerror(errno));
292 		return (-1);
293 	}
294 
295 	/* write out the cache label object */
296 	xx = write(fd, clabelp, sizeof (struct cache_label));
297 	if (xx != sizeof (struct cache_label)) {
298 		pr_err(gettext("Writing %s failed: %s"), filep,
299 		    strerror(errno));
300 		close(fd);
301 		return (-1);
302 	}
303 
304 	/* make sure the contents get to disk */
305 	if (fsync(fd) != 0) {
306 		pr_err(gettext("Writing %s failed on sync: %s"), filep,
307 		    strerror(errno));
308 		close(fd);
309 		return (-1);
310 	}
311 
312 	close(fd);
313 
314 	/* return success */
315 	return (0);
316 }
317 
318 int
319 cachefs_label_file_vcheck(char *filep, struct cache_label *clabelp)
320 {
321 	/* check for an invalid version number */
322 	if (clabelp->cl_cfsversion != CFSVERSION) {
323 		pr_err(gettext("Cache label file %s corrupted"), filep);
324 		return (-1);
325 	}
326 
327 	return (0);
328 }
329 
330 /*
331  *
332  *			cachefs_inuse
333  *
334  * Description:
335  *	Tests whether or not the cache directory is in use by
336  *	the cache file system.
337  * Arguments:
338  *	cachedirp	name of the file system cache directory
339  * Returns:
340  *	Returns 1 if the cache is in use or an error, 0 if not.
341  * Preconditions:
342  *	precond(cachedirp)
343  */
344 
345 int
346 cachefs_inuse(const char *cachedirp)
347 {
348 	int fd;
349 	int xx;
350 	char buf[MAXPATHLEN];
351 	char *lockp = CACHEFS_LOCK_FILE;
352 	struct flock fl;
353 
354 	/* see if path name is too long */
355 	xx = strlen(cachedirp) + strlen(lockp) + 3;
356 	if (xx >= MAXPATHLEN) {
357 		pr_err(gettext("Cache directory name %s is too long"),
358 		    cachedirp);
359 		return (1);
360 	}
361 
362 	/* make a path to the cache directory lock file */
363 	snprintf(buf, sizeof (buf), "%s/%s", cachedirp, lockp);
364 
365 	/* Open the kernel in use lock file.  This file will be <2GB. */
366 	fd = open(buf, O_RDWR, 0700);
367 	if (fd == -1) {
368 		pr_err(gettext("Cannot open lock file %s"), buf);
369 		return (1);
370 	}
371 
372 	/* test the lock status */
373 	fl.l_type = F_WRLCK;
374 	fl.l_whence = 0;
375 	fl.l_start = 0;
376 	fl.l_len = 1024;
377 	fl.l_sysid = 0;
378 	fl.l_pid = 0;
379 	xx = fcntl(fd, F_GETLK, &fl);
380 	if (xx == -1) {
381 		pr_err(gettext("Unexpected failure on lock file %s %s"),
382 		    buf, strerror(errno));
383 		close(fd);
384 		return (1);
385 	}
386 	close(fd);
387 
388 	if (fl.l_type == F_UNLCK)
389 		xx = 0;
390 	else
391 		xx = 1;
392 
393 	/* return whether or not the cache is in use */
394 	return (xx);
395 }
396 
397 /*
398  *
399  *			cachefs_resouce_size
400  *
401  * Description:
402  *	Returns information about a resource file.
403  * Arguments:
404  *	maxinodes	number of inodes to be managed by the resource file
405  *	rinfop		set to info about the resource file
406  * Returns:
407  * Preconditions:
408  *	precond(rinfop)
409  */
410 
411 void
412 cachefs_resource_size(int maxinodes, struct cachefs_rinfo *rinfop)
413 {
414 	int fsize;
415 
416 	fsize = MAXBSIZE;
417 
418 	rinfop->r_ptroffset = fsize;
419 
420 	fsize += MAXBSIZE * (maxinodes / CACHEFS_RLPMBS);
421 	if ((maxinodes % CACHEFS_RLPMBS) != 0)
422 		fsize += MAXBSIZE;
423 
424 	rinfop->r_fsize = fsize;
425 }
426 
427 /*
428  *
429  *			cachefs_create_cache
430  *
431  * Description:
432  *	Creates the specified cache directory and populates it as
433  *	needed by CFS.
434  * Arguments:
435  *	dirp		the name of the cache directory
436  *	uv		user values (may be NULL)
437  *	clabel		label file contents, or placeholder for this
438  * Returns:
439  *	Returns 0 for success or:
440  *		-1 for an error
441  *		-2 for an error and cache directory partially created
442  * Preconditions:
443  *	precond(dirp)
444  */
445 
446 int
447 cachefs_create_cache(char *dirp, struct cachefs_user_values *uv,
448     struct cache_label *clabel)
449 {
450 	int xx;
451 	char path[CACHEFS_XMAXPATH];
452 	int fd;
453 	void *bufp;
454 	int cnt;
455 	struct cache_usage cu;
456 	FILE *fp;
457 	char *parent;
458 	struct statvfs64 svfs;
459 
460 	cu.cu_blksused = 0;
461 	cu.cu_filesused = 0;
462 	cu.cu_flags = 0;
463 
464 	/* make sure cache dir name is not too long */
465 	if (strlen(dirp) > (size_t)PATH_MAX) {
466 		pr_err(gettext("path name %s is too long."), dirp);
467 		return (-1);
468 	}
469 
470 	/* ensure the path isn't in cachefs */
471 	parent = cachefs_file_to_dir(dirp);
472 	if (parent == NULL) {
473 		pr_err(gettext("Out of memory"));
474 		return (-1);
475 	}
476 	if (statvfs64(parent, &svfs) != 0) {
477 		pr_err(gettext("%s: %s"), parent, strerror(errno));
478 		free(parent);
479 		return (-1);
480 	}
481 	if (strcmp(svfs.f_basetype, CACHEFS_BASETYPE) == 0) {
482 		pr_err(gettext("Cannot create cache in cachefs filesystem"));
483 		free(parent);
484 		return (-1);
485 	}
486 	free(parent);
487 
488 	/* make the directory */
489 	if (mkdir(dirp, 0) == -1) {
490 		switch (errno) {
491 		case EEXIST:
492 			pr_err(gettext("%s already exists."), dirp);
493 			break;
494 
495 		default:
496 			pr_err(gettext("mkdir %s failed: %s"),
497 			    dirp, strerror(errno));
498 		}
499 		return (-1);
500 	}
501 	cu.cu_filesused += 1;
502 	cu.cu_blksused += 1;
503 
504 	/* convert user values to a cache_label */
505 	if (uv != NULL) {
506 		xx = cachefs_convert_uv2cl(uv, clabel, dirp);
507 		if (xx)
508 			return (-2);
509 	}
510 
511 	/*
512 	 * Create the cache directory lock file.
513 	 * Used by the kernel module to indicate the cache is in use.
514 	 * This file will be <2G.
515 	 */
516 	snprintf(path, sizeof (path), "%s/%s", dirp, CACHEFS_LOCK_FILE);
517 	fd = open(path, O_RDWR | O_CREAT, 0700);
518 	if (fd == -1) {
519 		pr_err(gettext("Cannot create lock file %s"), path);
520 		return (-1);
521 	}
522 	close(fd);
523 
524 	/* make the directory for the back file system mount points */
525 	/* note: we do not count this directory in the resources */
526 	snprintf(path, sizeof (path), "%s/%s", dirp, BACKMNT_NAME);
527 	if (mkdir(path, 0700) == -1) {
528 		pr_err(gettext("mkdir %s failed: %s"), path,
529 		    strerror(errno));
530 		return (-2);
531 	}
532 
533 	/* make the directory for lost+found */
534 	/* note: we do not count this directory in the resources */
535 	snprintf(path, sizeof (path), "%s/%s", dirp, CACHEFS_LOSTFOUND_NAME);
536 	if (mkdir(path, 0700) == -1) {
537 		pr_err(gettext("mkdir %s failed: %s"), path,
538 		    strerror(errno));
539 		return (-2);
540 	}
541 
542 	/* make the networker "don't back up" file; this file is <2GB */
543 	xx = 0;
544 	snprintf(path, sizeof (path), "%s/%s", dirp, NOBACKUP_NAME);
545 	if ((fp = fopen(path, "w")) != NULL) {
546 		if (realpath(dirp, path) != NULL) {
547 			fprintf(fp, "<< ./ >>\n");
548 			fprintf(fp, "+skip: .?* *\n");
549 			if (fclose(fp) == 0)
550 				xx = 1;
551 		}
552 	}
553 	if (xx == 0) {
554 		snprintf(path, sizeof (path), "%s/%s", dirp, NOBACKUP_NAME);
555 		pr_err(gettext("can't create %s"), path);
556 		(void) unlink(path);
557 	} else {
558 		cu.cu_filesused += 1;
559 		cu.cu_blksused += 1;
560 	}
561 
562 	/* create the unmount file */
563 	xx = 0;
564 	snprintf(path, sizeof (path), "%s/%s", dirp, CACHEFS_UNMNT_FILE);
565 	if ((fp = fopen(path, "w")) != NULL) {
566 		time32_t btime;
567 
568 		btime = get_boottime();
569 		fwrite((void *)&btime, sizeof (btime), 1, fp);
570 		if (fclose(fp) == 0)
571 			xx = 1;
572 	}
573 	if (xx == 0)
574 		pr_err(gettext("can't create .cfs_unmnt file"));
575 
576 	/* create the cache label file */
577 	snprintf(path, sizeof (path), "%s/%s", dirp, CACHELABEL_NAME);
578 	xx = cachefs_label_file_put(path, clabel);
579 	if (xx == -1) {
580 		pr_err(gettext("creating %s failed."), path);
581 		return (-2);
582 	}
583 	cu.cu_filesused += 1;
584 	cu.cu_blksused += 1;
585 
586 	/* create the cache label duplicate file */
587 	snprintf(path, sizeof (path), "%s/%s.dup", dirp, CACHELABEL_NAME);
588 	xx = cachefs_label_file_put(path, clabel);
589 	if (xx == -1) {
590 		pr_err(gettext("creating %s failed."), path);
591 		return (-2);
592 	}
593 	cu.cu_filesused += 1;
594 	cu.cu_blksused += 1;
595 
596 	/* create the resouce file; this file will be <2GB */
597 	snprintf(path, sizeof (path), "%s/%s", dirp, RESOURCE_NAME);
598 	fd = open(path, O_CREAT | O_RDWR, 0600);
599 	if (fd == -1) {
600 		pr_err(gettext("create %s failed: %s"), path,
601 		    strerror(errno));
602 		return (-2);
603 	}
604 	cu.cu_filesused += 1;
605 
606 	/* allocate a zeroed buffer for filling the resouce file */
607 	bufp = calloc(1, MAXBSIZE);
608 	if (bufp == NULL) {
609 		pr_err(gettext("out of space %d."), MAXBSIZE);
610 		close(fd);
611 		return (-2);
612 	}
613 
614 	/* determine number of MAXBSIZE chunks to make the file */
615 	cnt = 1;	/* for the header */
616 	cnt += clabel->cl_maxinodes / CACHEFS_RLPMBS;
617 	if ((clabel->cl_maxinodes % CACHEFS_RLPMBS) != 0)
618 		++cnt;
619 
620 	/* fill up the file with zeros */
621 	for (xx = 0; xx < cnt; xx++) {
622 		if (write(fd, bufp, MAXBSIZE) != MAXBSIZE) {
623 			pr_err(gettext("write %s failed: %s"), path,
624 			    strerror(errno));
625 			close(fd);
626 			free(bufp);
627 			return (-2);
628 		}
629 	}
630 	free(bufp);
631 	cu.cu_blksused += cnt;
632 
633 	/* position to the begining of the file */
634 	if (lseek(fd, 0, SEEK_SET) == -1) {
635 		pr_err(gettext("lseek %s failed: %s"), path,
636 		    strerror(errno));
637 		close(fd);
638 		return (-2);
639 	}
640 
641 	/* write the cache usage structure */
642 	xx = sizeof (struct cache_usage);
643 	if (write(fd, &cu, xx) != xx) {
644 		pr_err(gettext("cu write %s failed: %s"), path,
645 		    strerror(errno));
646 		close(fd);
647 		return (-2);
648 	}
649 
650 	/* make sure the contents get to disk */
651 	if (fsync(fd) != 0) {
652 		pr_err(gettext("fsync %s failed: %s"), path,
653 		    strerror(errno));
654 		close(fd);
655 		return (-2);
656 	}
657 	close(fd);
658 
659 	/* return success */
660 	return (0);
661 }
662 
663 /*
664  *
665  *			cachefs_delete_all_cache
666  *
667  * Description:
668  *	Delete all caches in cache directory.
669  * Arguments:
670  *	dirp	the pathname of of the cache directory to delete
671  * Returns:
672  *	Returns 0 for success or -1 for an error.
673  * Preconditions:
674  *	precond(dirp)
675  */
676 
677 int
678 cachefs_delete_all_cache(char *dirp)
679 {
680 	DIR *dp;
681 	struct dirent64 *dep;
682 	int xx;
683 	char path[CACHEFS_XMAXPATH];
684 	struct stat64 statinfo;
685 
686 	/* make sure cache dir name is not too long */
687 	if (strlen(dirp) > (size_t)PATH_MAX) {
688 		pr_err(gettext("path name %s is too long."),
689 		    dirp);
690 		return (-1);
691 	}
692 
693 	/* check that dirp is probably a cachefs directory */
694 	snprintf(path, sizeof (path), "%s/%s", dirp, BACKMNT_NAME);
695 	xx = access(path, R_OK | W_OK | X_OK);
696 
697 	snprintf(path, sizeof (path), "%s/%s", dirp, CACHELABEL_NAME);
698 	xx |= access(path, R_OK | W_OK);
699 
700 	if (xx) {
701 		pr_err(gettext("%s does not appear to be a "
702 		    "cachefs cache directory."), dirp);
703 		return (-1);
704 	}
705 
706 	/* remove the lost+found directory if it exists and is empty */
707 	snprintf(path, sizeof (path), "%s/%s", dirp, CACHEFS_LOSTFOUND_NAME);
708 	xx = rmdir(path);
709 	if (xx == -1) {
710 		if (errno == EEXIST) {
711 			pr_err(gettext("Cannot delete cache '%s'.  "
712 			    "First move files in '%s' to a safe location."),
713 			    dirp, path);
714 			return (-1);
715 		} else if (errno != ENOENT) {
716 			pr_err(gettext("rmdir %s failed: %s"), path,
717 			    strerror(errno));
718 			return (-1);
719 		}
720 	}
721 
722 	/* delete the back FS mount point directory if it exists */
723 	snprintf(path, sizeof (path), "%s/%s", dirp, BACKMNT_NAME);
724 	xx = lstat64(path, &statinfo);
725 	if (xx == -1) {
726 		if (errno != ENOENT) {
727 			pr_err(gettext("lstat %s failed: %s"), path,
728 			    strerror(errno));
729 			return (-1);
730 		}
731 	} else {
732 		xx = nftw64(path, cachefs_delete_file, 16,
733 		    FTW_PHYS | FTW_DEPTH | FTW_MOUNT);
734 		if (xx == -1) {
735 			pr_err(gettext("unable to delete %s"), path);
736 			return (-1);
737 		}
738 	}
739 
740 	/* open the cache directory specified */
741 	if ((dp = opendir(dirp)) == NULL) {
742 		pr_err(gettext("cannot open cache directory %s: %s"),
743 		    dirp, strerror(errno));
744 		return (-1);
745 	}
746 
747 	/* read the file names in the cache directory */
748 	while ((dep = readdir64(dp)) != NULL) {
749 		/* ignore . and .. */
750 		if (strcmp(dep->d_name, ".") == 0 ||
751 				strcmp(dep->d_name, "..") == 0)
752 			continue;
753 
754 		/* stat the file */
755 		snprintf(path, sizeof (path), "%s/%s", dirp, dep->d_name);
756 		xx = lstat64(path, &statinfo);
757 		if (xx == -1) {
758 			if (errno == ENOENT) {
759 				/* delete_cache may have nuked a directory */
760 				continue;
761 			}
762 
763 			pr_err(gettext("lstat %s failed: %s"),
764 			    path, strerror(errno));
765 			closedir(dp);
766 			return (-1);
767 		}
768 
769 		/* ignore anything that is not a link */
770 		if (!S_ISLNK(statinfo.st_mode))
771 			continue;
772 
773 		/* delete the file system cache directory */
774 		xx = cachefs_delete_cache(dirp, dep->d_name);
775 		if (xx) {
776 			closedir(dp);
777 			return (-1);
778 		}
779 	}
780 	closedir(dp);
781 
782 	/* remove the cache dir unmount file */
783 	snprintf(path, sizeof (path), "%s/%s", dirp, CACHEFS_UNMNT_FILE);
784 	xx = unlink(path);
785 	if ((xx == -1) && (errno != ENOENT)) {
786 		pr_err(gettext("unlink %s failed: %s"), path,
787 		    strerror(errno));
788 		return (-1);
789 	}
790 
791 	/* remove the cache label file */
792 	snprintf(path, sizeof (path), "%s/%s", dirp, CACHELABEL_NAME);
793 	xx = unlink(path);
794 	if ((xx == -1) && (errno != ENOENT)) {
795 		pr_err(gettext("unlink %s failed: %s"), path,
796 		    strerror(errno));
797 		return (-1);
798 	}
799 
800 	/* remove the cache label duplicate file */
801 	snprintf(path, sizeof (path), "%s/%s.dup", dirp, CACHELABEL_NAME);
802 	xx = unlink(path);
803 	if ((xx == -1) && (errno != ENOENT)) {
804 		pr_err(gettext("unlink %s failed: %s"), path,
805 		    strerror(errno));
806 		return (-1);
807 	}
808 
809 	/* remove the resource file */
810 	snprintf(path, sizeof (path), "%s/%s", dirp, RESOURCE_NAME);
811 	xx = unlink(path);
812 	if ((xx == -1) && (errno != ENOENT)) {
813 		pr_err(gettext("unlink %s failed: %s"), path,
814 		    strerror(errno));
815 		return (-1);
816 	}
817 
818 	/* remove the cachefslog file if it exists */
819 	snprintf(path, sizeof (path), "%s/%s", dirp, LOG_STATUS_NAME);
820 	(void) unlink(path);
821 
822 	/* remove the networker "don't back up" file if it exists */
823 	snprintf(path, sizeof (path), "%s/%s", dirp, NOBACKUP_NAME);
824 	(void) unlink(path);
825 
826 	/* remove the lock file */
827 	snprintf(path, sizeof (path), "%s/%s", dirp, CACHEFS_LOCK_FILE);
828 	xx = unlink(path);
829 	if ((xx == -1) && (errno != ENOENT)) {
830 		pr_err(gettext("unlink %s failed: %s"), path,
831 		    strerror(errno));
832 		return (-1);
833 	}
834 
835 	/* remove the directory */
836 	xx = rmdir(dirp);
837 	if (xx == -1) {
838 		pr_err(gettext("rmdir %s failed: %s"), dirp,
839 		    strerror(errno));
840 		return (-1);
841 	}
842 
843 	/* return success */
844 	return (0);
845 }
846 
847 /*
848  *
849  *			cachefs_delete_cache
850  *
851  * Description:
852  *	Deletes the specified file system cache.
853  * Arguments:
854  *	dirp	cache directory name
855  *	namep	file system cache directory to delete
856  * Returns:
857  *	Returns 0 for success, -1 for failure.
858  * Preconditions:
859  *	precond(dirp)
860  *	precond(namep)
861  */
862 
863 int
864 cachefs_delete_cache(char *dirp, char *namep)
865 {
866 	char path[CACHEFS_XMAXPATH];
867 	char buf[CACHEFS_XMAXPATH];
868 	int xx;
869 	struct stat64 statinfo;
870 
871 	/* make sure cache dir name is not too long */
872 	if (strlen(dirp) > (size_t)PATH_MAX) {
873 		pr_err(gettext("path name %s is too long."),
874 		    dirp);
875 		return (-1);
876 	}
877 
878 	/* construct the path name of the file system cache directory */
879 	snprintf(path, sizeof (path), "%s/%s", dirp, namep);
880 
881 	/* stat the specified file */
882 	xx = lstat64(path, &statinfo);
883 	if (xx == -1) {
884 		pr_err(gettext("lstat %s failed: %s"), path,
885 		    strerror(errno));
886 		return (-1);
887 	}
888 
889 	/* make sure name is a symbolic link */
890 	if (!S_ISLNK(statinfo.st_mode)) {
891 		pr_err(gettext("\"%s\" is not a valid cache id."), namep);
892 		return (-1);
893 	}
894 
895 	/* read the contents of the symbolic link */
896 	xx = readlink(path, buf, sizeof (buf));
897 	if (xx == -1) {
898 		pr_err(gettext("Readlink of %s failed: %s"), path,
899 		    strerror(errno));
900 		return (-1);
901 	}
902 	buf[xx] = '\0';
903 
904 	/* remove the directory */
905 	snprintf(path, sizeof (path), "%s/%s", dirp, buf);
906 	xx = nftw64(path, cachefs_delete_file, 16,
907 	    FTW_PHYS | FTW_DEPTH | FTW_MOUNT);
908 	if (xx == -1) {
909 		pr_err(gettext("directory walk of %s failed."), dirp);
910 		return (-1);
911 	}
912 
913 	/* delete the link */
914 	snprintf(path, sizeof (path), "%s/%s", dirp, namep);
915 	if (unlink(path) == -1) {
916 		pr_err(gettext("unlink %s failed: %s"), path,
917 		    strerror(errno));
918 		return (-1);
919 	}
920 
921 	/* return success */
922 	return (0);
923 }
924 
925 /*
926  *
927  *			cachefs_delete_file
928  *
929  * Description:
930  *	Remove a file or directory; called by nftw64().
931  * Arguments:
932  *	namep	pathname of the file
933  *	statp	stat info about the file
934  *	flg	info about file
935  *	ftwp	depth information
936  * Returns:
937  *	Returns 0 for success, -1 for failure.
938  * Preconditions:
939  *	precond(namep)
940  */
941 
942 int
943 cachefs_delete_file(const char *namep, const struct stat64 *statp, int flg,
944     struct FTW *ftwp)
945 {
946 	/* ignore . and .. */
947 	if (strcmp(namep, ".") == 0 || strcmp(namep, "..") == 0)
948 		return (0);
949 
950 	switch (flg) {
951 	case FTW_F:	/* files */
952 	case FTW_SL:
953 		if (unlink(namep) == -1) {
954 			pr_err(gettext("unlink %s failed: %s"),
955 			    namep, strerror(errno));
956 			return (-1);
957 		}
958 		break;
959 
960 	case FTW_DP:	/* directories that have their children processed */
961 		if (rmdir(namep) == -1) {
962 			pr_err(gettext("rmdir %s failed: %s"),
963 			    namep, strerror(errno));
964 			return (-1);
965 		}
966 		break;
967 
968 	case FTW_D:	/* ignore directories if children not processed */
969 		break;
970 
971 	default:
972 		pr_err(gettext("failure on file %s, flg %d."),
973 		    namep, flg);
974 		return (-1);
975 	}
976 
977 	/* return success */
978 	return (0);
979 }
980 
981 /*
982  *
983  *			cachefs_convert_uv2cl
984  *
985  * Description:
986  *	Copies the contents of a cachefs_user_values object into a
987  *	cache_label object, performing the necessary conversions.
988  * Arguments:
989  *	uvp	cachefs_user_values to copy from
990  *	clp	cache_label to copy into
991  *	dirp	cache directory
992  * Returns:
993  *	Returns 0 for success, -1 for an error.
994  * Preconditions:
995  *	precond(uvp)
996  *	precond(clp)
997  *	precond(dirp)
998  */
999 
1000 int
1001 cachefs_convert_uv2cl(const struct cachefs_user_values *uvp,
1002     struct cache_label *clp, const char *dirp)
1003 {
1004 	struct statvfs64 fs;
1005 	int xx;
1006 	double ftmp;
1007 	double temp;
1008 
1009 	/* get file system information */
1010 	xx = statvfs64(dirp, &fs);
1011 	if (xx == -1) {
1012 		pr_err(gettext("statvfs %s failed: %s"), dirp,
1013 		    strerror(errno));
1014 		return (-1);
1015 	}
1016 
1017 	ftmp = (double)fs.f_frsize / (double)MAXBSIZE;
1018 
1019 	/* front fs is less than 1 terabyte */
1020 	temp = (double)uvp->uv_maxblocks / 100.0 *
1021 	    (double)fs.f_blocks * ftmp + .5;
1022 	clp->cl_maxblks = temp < (double)INT_MAX ? (int)temp : INT_MAX;
1023 
1024 	temp = (double)uvp->uv_minblocks / 100.0 *
1025 	    (double)fs.f_blocks * ftmp + .5;
1026 	clp->cl_blockmin = temp < (double)INT_MAX ? (int)temp : INT_MAX;
1027 
1028 	temp = (double)uvp->uv_threshblocks / 100.0 *
1029 	    (double)fs.f_blocks * ftmp + .5;
1030 	clp->cl_blocktresh = temp < (double)INT_MAX ? (int)temp : INT_MAX;
1031 
1032 	temp = (double)uvp->uv_maxfiles / 100.0 * (double)fs.f_files + .5;
1033 	clp->cl_maxinodes = temp < (double)INT_MAX ? (int)temp : INT_MAX;
1034 
1035 	temp = (double)uvp->uv_minfiles / 100.0 * (double)fs.f_files + .5;
1036 	clp->cl_filemin = temp < (double)INT_MAX ? (int)temp : INT_MAX;
1037 
1038 	temp = (double)uvp->uv_threshfiles / 100.0 * (double)fs.f_files +.5;
1039 	clp->cl_filetresh = temp < (double)INT_MAX ? (int)temp : INT_MAX;
1040 
1041 	ftmp = (double)(1024 * 1024) / (double)MAXBSIZE;
1042 	clp->cl_maxfiles = uvp->uv_maxfilesize * ftmp + .5;
1043 
1044 	clp->cl_blkhiwat = uvp->uv_hiblocks / 100.0 * clp->cl_maxblks + .5;
1045 	clp->cl_blklowat = uvp->uv_lowblocks / 100.0 * clp->cl_maxblks + .5;
1046 
1047 	clp->cl_filehiwat = uvp->uv_hifiles / 100.0 * clp->cl_maxinodes + .5;
1048 	clp->cl_filelowat = uvp->uv_lowfiles / 100.0 * clp->cl_maxinodes + .5;
1049 
1050 	clp->cl_cfsversion = CFSVERSION;
1051 
1052 	/* return success */
1053 	return (0);
1054 }
1055 
1056 /*
1057  *
1058  *			cachefs_convert_cl2uv
1059  *
1060  * Description:
1061  *	Copies the contents of a cache_label object into a
1062  *	cachefs_user_values object, performing the necessary conversions.
1063  * Arguments:
1064  *	clp	cache_label to copy from
1065  *	uvp	cachefs_user_values to copy into
1066  *	dirp	cache directory
1067  * Returns:
1068  *	Returns 0 for success, -1 for an error.
1069  * Preconditions:
1070  *	precond(clp)
1071  *	precond(uvp)
1072  *	precond(dirp)
1073  */
1074 
1075 int
1076 cachefs_convert_cl2uv(const struct cache_label *clp,
1077     struct cachefs_user_values *uvp, const char *dirp)
1078 {
1079 	struct statvfs64 fs;
1080 	int xx;
1081 	double temp;
1082 	double ftmp;
1083 	long long ltmp;
1084 
1085 	/* get file system information */
1086 	xx = statvfs64(dirp, &fs);
1087 	if (xx == -1) {
1088 		pr_err(gettext("statvfs %s failed: %s"), dirp,
1089 		    strerror(errno));
1090 		return (-1);
1091 	}
1092 
1093 #define	BOUND(yy) \
1094 	yy = (yy < 0) ? 0 : yy; \
1095 	yy = (yy > 100) ? 100 : yy;
1096 
1097 	ftmp = (double)MAXBSIZE / (double)fs.f_frsize;
1098 
1099 	temp = (double)clp->cl_maxblks * ftmp /
1100 	    (double)fs.f_blocks * 100. + .5;
1101 	BOUND(temp);
1102 	uvp->uv_maxblocks = (int)temp;
1103 
1104 	temp = (double)clp->cl_blockmin * ftmp /
1105 	    (double)fs.f_blocks * 100. + .5;
1106 	BOUND(temp);
1107 	uvp->uv_minblocks = (int)temp;
1108 
1109 	temp = (double)clp->cl_blocktresh * ftmp /
1110 	    (double)fs.f_blocks * 100. + .5;
1111 	BOUND(temp);
1112 	uvp->uv_threshblocks = (int)temp;
1113 
1114 	temp = ((double)clp->cl_maxinodes / fs.f_files) * 100. + .5;
1115 	BOUND(temp);
1116 	uvp->uv_maxfiles = (int)temp;
1117 
1118 	temp = ((double)clp->cl_filemin / fs.f_files) * 100. + .5;
1119 	BOUND(temp);
1120 	uvp->uv_minfiles = (int)temp;
1121 
1122 	temp = ((double)clp->cl_filetresh / fs.f_files) * 100. + .5;
1123 	BOUND(temp);
1124 	uvp->uv_threshfiles = (int)temp;
1125 
1126 	ltmp = ((long long)clp->cl_maxfiles * MAXBSIZE);
1127 	uvp->uv_maxfilesize = (ltmp + (MAXBSIZE / 2)) / (1024 * 1024);
1128 
1129 	xx = ((double)clp->cl_blkhiwat / clp->cl_maxblks) * 100. + .5;
1130 	BOUND(xx);
1131 	uvp->uv_hiblocks = xx;
1132 
1133 	xx = ((double)clp->cl_blklowat / clp->cl_maxblks) * 100. + .5;
1134 	BOUND(xx);
1135 	uvp->uv_lowblocks = xx;
1136 
1137 	xx = ((double)clp->cl_filehiwat / clp->cl_maxinodes) * 100. + .5;
1138 	BOUND(xx);
1139 	uvp->uv_hifiles = xx;
1140 
1141 	xx = ((double)clp->cl_filelowat / clp->cl_maxinodes) * 100. + .5;
1142 	BOUND(xx);
1143 	uvp->uv_lowfiles = xx;
1144 
1145 	/* return success */
1146 	return (0);
1147 }
1148 
1149 /*
1150  * cachefs_file_to_dir
1151  *
1152  * takes in a path, and returns the parent directory of that path.
1153  *
1154  * it's the caller's responsibility to free the pointer returned by
1155  * this function.
1156  */
1157 
1158 char *
1159 cachefs_file_to_dir(const char *path)
1160 {
1161 	char *rc, *cp;
1162 
1163 	if (path == NULL)
1164 		return (NULL);
1165 
1166 	rc = strdup(path);
1167 	if (rc == NULL)
1168 		return (NULL);
1169 
1170 	if ((cp = strrchr(rc, '/')) == NULL) {
1171 
1172 		/*
1173 		 * if no slashes at all, return "." (current directory).
1174 		 */
1175 
1176 		(void) free(rc);
1177 		rc = strdup(".");
1178 
1179 	} else if (cp == rc) {
1180 
1181 		/*
1182 		 * else, if the last '/' is the first character, chop
1183 		 * off from there (i.e. return "/").
1184 		 */
1185 
1186 		rc[1] = '\0';
1187 
1188 	} else {
1189 
1190 		/*
1191 		 * else, we have a path like /foo/bar or foo/bar.
1192 		 * chop off from the last '/'.
1193 		 */
1194 
1195 		*cp = '\0';
1196 
1197 	}
1198 
1199 	return (rc);
1200 }
1201 
1202 /*
1203  *			cachefs_clean_flag_test
1204  *
1205  * Description:
1206  *	Tests whether or not the clean flag on the file system
1207  *	is set.
1208  * Arguments:
1209  *	cachedirp	name of the the file system cache directory
1210  * Returns:
1211  *	Returns 1 if the cache was shut down cleanly, 0 if not.
1212  * Preconditions:
1213  *	precond(cachedirp)
1214  */
1215 
1216 int
1217 cachefs_clean_flag_test(const char *cachedirp)
1218 {
1219 	char *namep;
1220 	int xx;
1221 	char buf[MAXPATHLEN];
1222 	int fd;
1223 	struct cache_usage cu;
1224 
1225 	/* construct the path name of the resource file */
1226 	namep = RESOURCE_NAME;
1227 	xx = strlen(cachedirp) + strlen(namep) + 3;
1228 	if (xx >= MAXPATHLEN) {
1229 		pr_err(gettext("Path name too long %s/%s"),
1230 		    cachedirp, namep);
1231 		return (39);
1232 	}
1233 	snprintf(buf, sizeof (buf), "%s/%s", cachedirp, namep);
1234 
1235 	/* open the file; it will be <2GB */
1236 	fd = open(buf, O_RDONLY);
1237 	if (fd == -1) {
1238 		pr_err(gettext("Cannot open %s: %s"), buf, strerror(errno));
1239 		return (0);
1240 	}
1241 
1242 	/* read the cache_usage structure */
1243 	xx = read(fd, &cu, sizeof (cu));
1244 	if (xx != sizeof (cu)) {
1245 		pr_err(gettext("Error reading %s: %d %s"), buf,
1246 		    xx, strerror(errno));
1247 		close(fd);
1248 		return (0);
1249 	}
1250 	close(fd);
1251 
1252 	/* return state of the cache */
1253 	return ((cu.cu_flags & CUSAGE_ACTIVE) == 0);
1254 }
1255 
1256 time32_t
1257 get_boottime()
1258 {
1259 	struct utmpx id, *putmp;
1260 
1261 	id.ut_type = BOOT_TIME;
1262 	setutxent();
1263 	if ((putmp = getutxid(&id)) != NULL)
1264 		return ((time32_t)putmp->ut_tv.tv_sec);
1265 	return (-1);
1266 }
1267