1/*	$NetBSD: dtfs_subr.c,v 1.4 2013/10/19 17:45:00 christos 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
42void
43dtfs_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 */
80struct puffs_node *
81dtfs_genfile(struct puffs_node *dir, const struct puffs_cn *pcn,
82	enum vtype type)
83{
84	struct dtfs_file *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	dfd = emalloc(sizeof(struct dtfs_dirent));
111	dfd->dfd_node = newpn;
112	dfd->dfd_name = estrndup(pcn->pcn_name, pcn->pcn_namelen);
113	dfd->dfd_namelen = strlen(dfd->dfd_name);
114	dfd->dfd_parent = dir;
115	dtfs_adddent(dir, dfd);
116
117	newpn->pn_va.va_uid = uid;
118	newpn->pn_va.va_gid = dir->pn_va.va_gid;
119
120	return newpn;
121}
122
123struct dtfs_file *
124dtfs_newdir()
125{
126	struct dtfs_file *dff;
127
128	dff = emalloc(sizeof(struct dtfs_file));
129	memset(dff, 0, sizeof(struct dtfs_file));
130	LIST_INIT(&dff->df_dirents);
131
132	return dff;
133}
134
135struct dtfs_file *
136dtfs_newfile()
137{
138	struct dtfs_file *dff;
139
140	dff = emalloc(sizeof(struct dtfs_file));
141	memset(dff, 0, sizeof(struct dtfs_file));
142
143	return dff;
144}
145
146struct dtfs_dirent *
147dtfs_dirgetnth(struct dtfs_file *searchdir, int n)
148{
149	struct dtfs_dirent *dirent;
150	int i;
151
152	i = 0;
153	LIST_FOREACH(dirent, &searchdir->df_dirents, dfd_entries) {
154		if (i == n)
155			return dirent;
156		i++;
157	}
158
159	return NULL;
160}
161
162struct dtfs_dirent *
163dtfs_dirgetbyname(struct dtfs_file *searchdir, const char *fname, size_t fnlen)
164{
165	struct dtfs_dirent *dirent;
166
167	LIST_FOREACH(dirent, &searchdir->df_dirents, dfd_entries)
168		if (dirent->dfd_namelen == fnlen
169		    && strncmp(dirent->dfd_name, fname, fnlen) == 0)
170			return dirent;
171
172	return NULL;
173}
174
175/*
176 * common nuke, kill dirent from parent node
177 */
178void
179dtfs_nukenode(struct puffs_node *nukeme, struct puffs_node *pn_parent,
180	const char *fname, size_t fnlen)
181{
182	struct dtfs_dirent *dfd;
183	struct dtfs_mount *dtm;
184
185	assert(pn_parent->pn_va.va_type == VDIR);
186
187	dfd = dtfs_dirgetbyname(DTFS_PTOF(pn_parent), fname, fnlen);
188	assert(dfd);
189
190	dtm = puffs_pn_getmntspecific(nukeme);
191	dtm->dtm_nfiles--;
192	assert(dtm->dtm_nfiles >= 1);
193
194	dtfs_removedent(pn_parent, dfd);
195	free(dfd);
196}
197
198/* free lingering information */
199void
200dtfs_freenode(struct puffs_node *pn)
201{
202	struct dtfs_file *df = DTFS_PTOF(pn);
203	struct dtfs_mount *dtm;
204	int i;
205
206	assert(pn->pn_va.va_nlink == 0);
207	dtm = puffs_pn_getmntspecific(pn);
208
209	switch (pn->pn_va.va_type) {
210	case VREG:
211		assert(dtm->dtm_fsizes >= pn->pn_va.va_size);
212		dtm->dtm_fsizes -= pn->pn_va.va_size;
213		for (i = 0; i < BLOCKNUM(df->df_datalen, DTFS_BLOCKSHIFT); i++)
214			free(df->df_blocks[i]);
215		if (df->df_datalen > i << DTFS_BLOCKSHIFT)
216			free(df->df_blocks[i]);
217		break;
218	case VLNK:
219		free(df->df_linktarget);
220		break;
221	case VCHR:
222	case VBLK:
223	case VDIR:
224	case VSOCK:
225	case VFIFO:
226		break;
227	default:
228		assert(0);
229		break;
230	}
231
232	free(df);
233	puffs_pn_put(pn);
234}
235
236void
237dtfs_setsize(struct puffs_node *pn, off_t newsize)
238{
239	struct dtfs_file *df = DTFS_PTOF(pn);
240	struct dtfs_mount *dtm;
241	size_t newblocks;
242	int needalloc, shrinks;
243	int i;
244
245	needalloc = newsize > ROUNDUP(df->df_datalen, DTFS_BLOCKSIZE);
246	shrinks = newsize < pn->pn_va.va_size;
247
248	if (needalloc || shrinks) {
249		newblocks = BLOCKNUM(newsize, DTFS_BLOCKSHIFT) + 1;
250
251		if (shrinks)
252			for (i = newblocks; i < df->df_numblocks; i++)
253				free(df->df_blocks[i]);
254
255		df->df_blocks = erealloc(df->df_blocks,
256		    newblocks * sizeof(uint8_t *));
257		/*
258		 * if extended, set storage to zero
259		 * to match correct behaviour
260		 */
261		if (!shrinks) {
262			for (i = df->df_numblocks; i < newblocks; i++) {
263				df->df_blocks[i] = emalloc(DTFS_BLOCKSIZE);
264				memset(df->df_blocks[i], 0, DTFS_BLOCKSIZE);
265			}
266		}
267
268		df->df_datalen = newsize;
269		df->df_numblocks = newblocks;
270	}
271
272	dtm = puffs_pn_getmntspecific(pn);
273	if (!shrinks) {
274		dtm->dtm_fsizes += newsize - pn->pn_va.va_size;
275	} else {
276		dtm->dtm_fsizes -= pn->pn_va.va_size - newsize;
277	}
278
279	pn->pn_va.va_size = newsize;
280	pn->pn_va.va_bytes = BLOCKNUM(newsize,DTFS_BLOCKSHIFT)>>DTFS_BLOCKSHIFT;
281}
282
283/* add & bump link count */
284void
285dtfs_adddent(struct puffs_node *pn_dir, struct dtfs_dirent *dent)
286{
287	struct dtfs_file *dir = DTFS_PTOF(pn_dir);
288	struct puffs_node *pn_file = dent->dfd_node;
289	struct dtfs_file *file = DTFS_PTOF(pn_file);
290	struct dtfs_mount *dtm;
291
292	assert(pn_dir->pn_va.va_type == VDIR);
293	LIST_INSERT_HEAD(&dir->df_dirents, dent, dfd_entries);
294	pn_file->pn_va.va_nlink++;
295
296	dtm = puffs_pn_getmntspecific(pn_file);
297	dtm->dtm_nfiles++;
298
299	dent->dfd_parent = pn_dir;
300	if (dent->dfd_node->pn_va.va_type == VDIR) {
301		file->df_dotdot = pn_dir;
302		pn_dir->pn_va.va_nlink++;
303	}
304
305	dtfs_updatetimes(pn_dir, 0, 1, 1);
306}
307
308/* remove & lower link count */
309void
310dtfs_removedent(struct puffs_node *pn_dir, struct dtfs_dirent *dent)
311{
312	struct puffs_node *pn_file = dent->dfd_node;
313
314	assert(pn_dir->pn_va.va_type == VDIR);
315	LIST_REMOVE(dent, dfd_entries);
316	if (pn_file->pn_va.va_type == VDIR) {
317		struct dtfs_file *df = DTFS_PTOF(pn_file);
318
319		pn_dir->pn_va.va_nlink--;
320		df->df_dotdot = NULL;
321	}
322	pn_file->pn_va.va_nlink--;
323	assert(pn_dir->pn_va.va_nlink >= 2);
324
325	dtfs_updatetimes(pn_dir, 0, 1, 1);
326}
327
328void
329dtfs_updatetimes(struct puffs_node *pn, int doatime, int doctime, int domtime)
330{
331	struct timeval tv;
332	struct timespec ts;
333
334	gettimeofday(&tv, NULL);
335	TIMEVAL_TO_TIMESPEC(&tv, &ts);
336
337	if (doatime)
338		pn->pn_va.va_atime = ts;
339	if (doctime)
340		pn->pn_va.va_ctime = ts;
341	if (domtime)
342		pn->pn_va.va_mtime = ts;
343}
344
345bool
346dtfs_isunder(struct puffs_node *pn, struct puffs_node *pn_parent)
347{
348	struct dtfs_file *df;
349
350	while (pn) {
351		if (pn == pn_parent)
352			return true;
353		df = DTFS_CTOF(pn);
354		pn = df->df_dotdot;
355	}
356
357	return false;
358}
359