xref: /netbsd-src/sbin/mount_qemufwcfg/virtdir.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /* $NetBSD: virtdir.c,v 1.3 2017/11/26 03:51:45 christos Exp $ */
2 
3 /*
4  * Copyright � 2007 Alistair Crooks.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote
15  *    products derived from this software without specific prior written
16  *    permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
19  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
24  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <sys/types.h>
32 #include <sys/param.h>
33 #include <sys/stat.h>
34 
35 #include <stdio.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <util.h>
40 
41 #include "virtdir.h"
42 
43  /* utility comparison routine for sorting and searching */
44 static int
45 compare(const void *vp1, const void *vp2)
46 {
47 	const virt_dirent_t *tp1 = vp1;
48 	const virt_dirent_t *tp2 = vp2;
49 
50 	return strcmp(tp1->name, tp2->name);
51 }
52 
53 /* ensure intermediate directories exist */
54 static void
55 mkdirs(virtdir_t *tp, const char *path, size_t size)
56 {
57 	virt_dirent_t	*ep;
58 	char		 name[MAXPATHLEN];
59 	char		*slash;
60 
61 	(void) strlcpy(name, path, sizeof(name));
62 	for (slash = name + 1 ; (slash = strchr(slash + 1, '/')) != NULL ; ) {
63 		*slash = '\0';
64 		if ((ep = virtdir_find(tp, name, strlen(name))) == NULL) {
65 			virtdir_add(tp, name, strlen(name), 'd', NULL, 0, 0);
66 		}
67 		*slash = '/';
68 	}
69 }
70 
71 /* get rid of multiple slashes in input */
72 static size_t
73 normalise(const char *name, size_t namelen, char *path, size_t pathsize)
74 {
75 	const char	*np;
76 	char		*pp;
77 	int		 done;
78 
79 	for (pp = path, np = name, done = 0 ; !done &&
80 	    (size_t)(pp - path) < pathsize - 1 &&
81 	    (size_t)(np - name) <= namelen; ) {
82 		switch (*np) {
83 		case '/':
84 			if (pp == path || *(pp - 1) != '/') {
85 				*pp++ = *np;
86 			}
87 			np += 1;
88 			break;
89 		case '\0':
90 			done = 1;
91 			break;
92 		default:
93 			*pp++ = *np++;
94 			break;
95 		}
96 	}
97 	/* XXX - trailing slash? */
98 	*pp = '\0';
99 	return (size_t)(pp - path);
100 }
101 
102 /* initialise the tree */
103 int
104 virtdir_init(virtdir_t *tp, const char *rootdir, const struct stat *d,
105     const struct stat *f, const struct stat *l)
106 {
107 	tp->dir = *d;
108 	tp->dir.st_mode = S_IFDIR | 0755;
109 	tp->dir.st_nlink = 2;
110 	tp->file = *f;
111 	tp->file.st_mode = S_IFREG | 0644;
112 	tp->file.st_nlink = 1;
113 	tp->lnk = *l;
114 	tp->lnk.st_mode = S_IFLNK | 0644;
115 	tp->lnk.st_nlink = 1;
116 	if (rootdir != NULL) {
117 		tp->rootdir = estrdup(rootdir);
118 	}
119 	return 1;
120 }
121 
122 /* add an entry to the tree */
123 int
124 virtdir_add(virtdir_t *tp, const char *name, size_t size, uint8_t type,
125     const char *tgt, size_t tgtlen, uint16_t select)
126 {
127 	char		path[MAXPATHLEN];
128 	size_t		pathlen;
129 
130 	pathlen = normalise(name, size, path, sizeof(path));
131 	if (virtdir_find(tp, path, pathlen) != NULL) {
132 		/* attempt to add a duplicate directory entry */
133 		return 0;
134 	}
135 	if (tp->c == tp->size || tp->size == 0) {
136 		tp->size += 10;
137 		tp->v = erealloc(tp->v, tp->size * sizeof(*tp->v));
138 	}
139 	tp->v[tp->c].namelen = pathlen;
140 	tp->v[tp->c].name = estrndup(path, pathlen);
141 	tp->v[tp->c].d_name = strrchr(tp->v[tp->c].name, '/') + 1;
142 	tp->v[tp->c].type = type;
143 	tp->v[tp->c].ino = (ino_t) random() & 0xfffff;
144 	tp->v[tp->c].tgtlen = tgtlen;
145 	if (tgt != NULL) {
146 		tp->v[tp->c].tgt = estrndup(tgt, tgtlen);
147 	}
148 	tp->v[tp->c].select = select;
149 	tp->c += 1;
150 	qsort(tp->v, tp->c, sizeof(tp->v[0]), compare);
151 	mkdirs(tp, path, pathlen);
152 	return 1;
153 }
154 
155 /* find an entry in the tree */
156 virt_dirent_t *
157 virtdir_find(virtdir_t *tp, const char *name, size_t namelen)
158 {
159 	virt_dirent_t	e;
160 	char		path[MAXPATHLEN];
161 
162 	(void) memset(&e, 0, sizeof(e));
163 	e.namelen = normalise(name, namelen, path, sizeof(path));
164 	e.name = path;
165 	return bsearch(&e, tp->v, tp->c, sizeof(tp->v[0]), compare);
166 }
167 
168 /* return the virtual offset in the tree */
169 off_t
170 virtdir_offset(const virtdir_t *tp, const virt_dirent_t *dp)
171 {
172 	return dp - tp->v;
173 }
174 
175 /* analogous to opendir(3) - open a directory, save information, and
176 * return a pointer to the dynamically allocated structure */
177 VIRTDIR *
178 openvirtdir(virtdir_t *tp, const char *d)
179 {
180 	VIRTDIR	*dirp;
181 
182 	dirp = emalloc(sizeof(*dirp));
183 	dirp->dirname = estrdup(d);
184 	dirp->dirnamelen = strlen(d);
185 	dirp->tp = tp;
186 	dirp->i = 0;
187 	return dirp;
188 }
189 
190 /* analogous to readdir(3) - read the next entry in the directory that
191 * was opened, and return a pointer to it */
192 virt_dirent_t *
193 readvirtdir(VIRTDIR *dirp)
194 {
195 	char	*from;
196 
197 	for ( ; dirp->i < dirp->tp->c; dirp->i++) {
198 		from = (strcmp(dirp->dirname, "/") == 0) ?
199 		    &dirp->tp->v[dirp->i].name[1] :
200 		    &dirp->tp->v[dirp->i].name[dirp->dirnamelen + 1];
201 		if (strncmp(dirp->tp->v[dirp->i].name, dirp->dirname,
202 		    dirp->dirnamelen) == 0 &&
203 		    *from != '\0' && strchr(from, '/') == NULL) {
204 			return &dirp->tp->v[dirp->i++];
205 		}
206 	}
207 	return NULL;
208 }
209 
210 /* free the storage associated with the virtual directory structure */
211 void
212 closevirtdir(VIRTDIR *dirp)
213 {
214 	free(dirp->dirname);
215 	free(dirp);
216 }
217