xref: /netbsd-src/tests/fs/puffs/h_dtfs/dtfs_subr.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /*	$NetBSD: dtfs_subr.c,v 1.3 2011/03/01 15:19:49 pooka Exp $	*/
2 
3 /*
4  * Copyright (c) 2006  Antti Kantee.  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  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/types.h>
29 #include <sys/time.h>
30 
31 #include <assert.h>
32 #include <err.h>
33 #include <errno.h>
34 #include <puffs.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <util.h>
39 
40 #include "dtfs.h"
41 
42 void
43 dtfs_baseattrs(struct vattr *vap, enum vtype type, ino_t id)
44 {
45 	struct timeval tv;
46 	struct timespec ts;
47 
48 	gettimeofday(&tv, NULL);
49 	TIMEVAL_TO_TIMESPEC(&tv, &ts);
50 
51 	vap->va_type = type;
52 	if (type == VDIR) {
53 		vap->va_mode = 0777;
54 		vap->va_nlink = 1;	/* n + 1 after adding dent */
55 	} else {
56 		vap->va_mode = 0666;
57 		vap->va_nlink = 0;	/* n + 1 */
58 	}
59 	vap->va_uid = 0;
60 	vap->va_gid = 0;
61 	vap->va_fileid = id;
62 	vap->va_size = 0;
63 	vap->va_blocksize = getpagesize();
64 	vap->va_gen = random();
65 	vap->va_flags = 0;
66 	vap->va_rdev = PUFFS_VNOVAL;
67 	vap->va_bytes = 0;
68 	vap->va_filerev = 1;
69 	vap->va_vaflags = 0;
70 
71 	vap->va_atime = vap->va_mtime = vap->va_ctime = vap->va_birthtime = ts;
72 }
73 
74 /*
75  * Well, as you can probably see, this interface has the slight problem
76  * of assuming file creation will always be succesful, or at least not
77  * giving a reason for the failure.  Be sure to do better when you
78  * implement your own fs.
79  */
80 struct puffs_node *
81 dtfs_genfile(struct puffs_node *dir, const struct puffs_cn *pcn,
82 	enum vtype type)
83 {
84 	struct dtfs_file *df_dir, *dff;
85 	struct dtfs_dirent *dfd;
86 	struct dtfs_mount *dtm;
87 	struct puffs_node *newpn;
88 	uid_t uid;
89 	int rv;
90 
91 	assert(dir->pn_va.va_type == VDIR);
92 	assert(dir->pn_mnt != NULL);
93 
94 	uid = 0;
95 	rv = puffs_cred_getuid(pcn->pcn_cred, &uid);
96 	assert(rv == 0);
97 
98 	if (type == VDIR) {
99 		dff = dtfs_newdir();
100 		dff->df_dotdot = dir;
101 	} else
102 		dff = dtfs_newfile();
103 
104 	dtm = puffs_pn_getmntspecific(dir);
105 	newpn = puffs_pn_new(dir->pn_mnt, dff);
106 	if (newpn == NULL)
107 		errx(1, "getnewpnode");
108 	dtfs_baseattrs(&newpn->pn_va, type, dtm->dtm_nextfileid++);
109 
110 	df_dir = dir->pn_data;
111 	dfd = emalloc(sizeof(struct dtfs_dirent));
112 	dfd->dfd_node = newpn;
113 	dfd->dfd_name = estrndup(pcn->pcn_name, pcn->pcn_namelen);
114 	dfd->dfd_namelen = strlen(dfd->dfd_name);
115 	dfd->dfd_parent = dir;
116 	dtfs_adddent(dir, dfd);
117 
118 	newpn->pn_va.va_uid = uid;
119 	newpn->pn_va.va_gid = dir->pn_va.va_gid;
120 
121 	return newpn;
122 }
123 
124 struct dtfs_file *
125 dtfs_newdir()
126 {
127 	struct dtfs_file *dff;
128 
129 	dff = emalloc(sizeof(struct dtfs_file));
130 	memset(dff, 0, sizeof(struct dtfs_file));
131 	LIST_INIT(&dff->df_dirents);
132 
133 	return dff;
134 }
135 
136 struct dtfs_file *
137 dtfs_newfile()
138 {
139 	struct dtfs_file *dff;
140 
141 	dff = emalloc(sizeof(struct dtfs_file));
142 	memset(dff, 0, sizeof(struct dtfs_file));
143 
144 	return dff;
145 }
146 
147 struct dtfs_dirent *
148 dtfs_dirgetnth(struct dtfs_file *searchdir, int n)
149 {
150 	struct dtfs_dirent *dirent;
151 	int i;
152 
153 	i = 0;
154 	LIST_FOREACH(dirent, &searchdir->df_dirents, dfd_entries) {
155 		if (i == n)
156 			return dirent;
157 		i++;
158 	}
159 
160 	return NULL;
161 }
162 
163 struct dtfs_dirent *
164 dtfs_dirgetbyname(struct dtfs_file *searchdir, const char *fname, size_t fnlen)
165 {
166 	struct dtfs_dirent *dirent;
167 
168 	LIST_FOREACH(dirent, &searchdir->df_dirents, dfd_entries)
169 		if (dirent->dfd_namelen == fnlen
170 		    && strncmp(dirent->dfd_name, fname, fnlen) == 0)
171 			return dirent;
172 
173 	return NULL;
174 }
175 
176 /*
177  * common nuke, kill dirent from parent node
178  */
179 void
180 dtfs_nukenode(struct puffs_node *nukeme, struct puffs_node *pn_parent,
181 	const char *fname, size_t fnlen)
182 {
183 	struct dtfs_dirent *dfd;
184 	struct dtfs_mount *dtm;
185 
186 	assert(pn_parent->pn_va.va_type == VDIR);
187 
188 	dfd = dtfs_dirgetbyname(DTFS_PTOF(pn_parent), fname, fnlen);
189 	assert(dfd);
190 
191 	dtm = puffs_pn_getmntspecific(nukeme);
192 	dtm->dtm_nfiles--;
193 	assert(dtm->dtm_nfiles >= 1);
194 
195 	dtfs_removedent(pn_parent, dfd);
196 	free(dfd);
197 }
198 
199 /* free lingering information */
200 void
201 dtfs_freenode(struct puffs_node *pn)
202 {
203 	struct dtfs_file *df = DTFS_PTOF(pn);
204 	struct dtfs_mount *dtm;
205 	int i;
206 
207 	assert(pn->pn_va.va_nlink == 0);
208 	dtm = puffs_pn_getmntspecific(pn);
209 
210 	switch (pn->pn_va.va_type) {
211 	case VREG:
212 		assert(dtm->dtm_fsizes >= pn->pn_va.va_size);
213 		dtm->dtm_fsizes -= pn->pn_va.va_size;
214 		for (i = 0; i < BLOCKNUM(df->df_datalen, DTFS_BLOCKSHIFT); i++)
215 			free(df->df_blocks[i]);
216 		if (df->df_datalen > i << DTFS_BLOCKSHIFT)
217 			free(df->df_blocks[i]);
218 		break;
219 	case VLNK:
220 		free(df->df_linktarget);
221 		break;
222 	case VCHR:
223 	case VBLK:
224 	case VDIR:
225 	case VSOCK:
226 	case VFIFO:
227 		break;
228 	default:
229 		assert(0);
230 		break;
231 	}
232 
233 	free(df);
234 	puffs_pn_put(pn);
235 }
236 
237 void
238 dtfs_setsize(struct puffs_node *pn, off_t newsize)
239 {
240 	struct dtfs_file *df = DTFS_PTOF(pn);
241 	struct dtfs_mount *dtm;
242 	size_t newblocks;
243 	int needalloc, shrinks;
244 	int i;
245 
246 	needalloc = newsize > ROUNDUP(df->df_datalen, DTFS_BLOCKSIZE);
247 	shrinks = newsize < pn->pn_va.va_size;
248 
249 	if (needalloc || shrinks) {
250 		newblocks = BLOCKNUM(newsize, DTFS_BLOCKSHIFT) + 1;
251 
252 		if (shrinks)
253 			for (i = newblocks; i < df->df_numblocks; i++)
254 				free(df->df_blocks[i]);
255 
256 		df->df_blocks = erealloc(df->df_blocks,
257 		    newblocks * sizeof(uint8_t *));
258 		/*
259 		 * if extended, set storage to zero
260 		 * to match correct behaviour
261 		 */
262 		if (!shrinks) {
263 			for (i = df->df_numblocks; i < newblocks; i++) {
264 				df->df_blocks[i] = emalloc(DTFS_BLOCKSIZE);
265 				memset(df->df_blocks[i], 0, DTFS_BLOCKSIZE);
266 			}
267 		}
268 
269 		df->df_datalen = newsize;
270 		df->df_numblocks = newblocks;
271 	}
272 
273 	dtm = puffs_pn_getmntspecific(pn);
274 	if (!shrinks) {
275 		dtm->dtm_fsizes += newsize - pn->pn_va.va_size;
276 	} else {
277 		dtm->dtm_fsizes -= pn->pn_va.va_size - newsize;
278 	}
279 
280 	pn->pn_va.va_size = newsize;
281 	pn->pn_va.va_bytes = BLOCKNUM(newsize,DTFS_BLOCKSHIFT)>>DTFS_BLOCKSHIFT;
282 }
283 
284 /* add & bump link count */
285 void
286 dtfs_adddent(struct puffs_node *pn_dir, struct dtfs_dirent *dent)
287 {
288 	struct dtfs_file *dir = DTFS_PTOF(pn_dir);
289 	struct puffs_node *pn_file = dent->dfd_node;
290 	struct dtfs_file *file = DTFS_PTOF(pn_file);
291 	struct dtfs_mount *dtm;
292 
293 	assert(pn_dir->pn_va.va_type == VDIR);
294 	LIST_INSERT_HEAD(&dir->df_dirents, dent, dfd_entries);
295 	pn_file->pn_va.va_nlink++;
296 
297 	dtm = puffs_pn_getmntspecific(pn_file);
298 	dtm->dtm_nfiles++;
299 
300 	dent->dfd_parent = pn_dir;
301 	if (dent->dfd_node->pn_va.va_type == VDIR) {
302 		file->df_dotdot = pn_dir;
303 		pn_dir->pn_va.va_nlink++;
304 	}
305 
306 	dtfs_updatetimes(pn_dir, 0, 1, 1);
307 }
308 
309 /* remove & lower link count */
310 void
311 dtfs_removedent(struct puffs_node *pn_dir, struct dtfs_dirent *dent)
312 {
313 	struct puffs_node *pn_file = dent->dfd_node;
314 
315 	assert(pn_dir->pn_va.va_type == VDIR);
316 	LIST_REMOVE(dent, dfd_entries);
317 	if (pn_file->pn_va.va_type == VDIR) {
318 		struct dtfs_file *df = DTFS_PTOF(pn_file);
319 
320 		pn_dir->pn_va.va_nlink--;
321 		df->df_dotdot = NULL;
322 	}
323 	pn_file->pn_va.va_nlink--;
324 	assert(pn_dir->pn_va.va_nlink >= 2);
325 
326 	dtfs_updatetimes(pn_dir, 0, 1, 1);
327 }
328 
329 void
330 dtfs_updatetimes(struct puffs_node *pn, int doatime, int doctime, int domtime)
331 {
332 	struct timeval tv;
333 	struct timespec ts;
334 
335 	gettimeofday(&tv, NULL);
336 	TIMEVAL_TO_TIMESPEC(&tv, &ts);
337 
338 	if (doatime)
339 		pn->pn_va.va_atime = ts;
340 	if (doctime)
341 		pn->pn_va.va_ctime = ts;
342 	if (domtime)
343 		pn->pn_va.va_mtime = ts;
344 }
345 
346 bool
347 dtfs_isunder(struct puffs_node *pn, struct puffs_node *pn_parent)
348 {
349 	struct dtfs_file *df;
350 
351 	while (pn) {
352 		if (pn == pn_parent)
353 			return true;
354 		df = DTFS_CTOF(pn);
355 		pn = df->df_dotdot;
356 	}
357 
358 	return false;
359 }
360