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
78int
79cachefs_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
158int
159cachefs_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
203int
204cachefs_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
273int
274cachefs_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
318int
319cachefs_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
345int
346cachefs_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
411void
412cachefs_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
446int
447cachefs_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
677int
678cachefs_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
863int
864cachefs_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
942int
943cachefs_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
1000int
1001cachefs_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
1075int
1076cachefs_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
1158char *
1159cachefs_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
1216int
1217cachefs_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
1256time32_t
1257get_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