1272343Sngie/*	$NetBSD: dtfs_subr.c,v 1.4 2013/10/19 17:45:00 christos Exp $	*/
2272343Sngie
3272343Sngie/*
4272343Sngie * Copyright (c) 2006  Antti Kantee.  All Rights Reserved.
5272343Sngie *
6272343Sngie * Redistribution and use in source and binary forms, with or without
7272343Sngie * modification, are permitted provided that the following conditions
8272343Sngie * are met:
9272343Sngie * 1. Redistributions of source code must retain the above copyright
10272343Sngie *    notice, this list of conditions and the following disclaimer.
11272343Sngie * 2. Redistributions in binary form must reproduce the above copyright
12272343Sngie *    notice, this list of conditions and the following disclaimer in the
13272343Sngie *    documentation and/or other materials provided with the distribution.
14272343Sngie *
15272343Sngie * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16272343Sngie * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17272343Sngie * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18272343Sngie * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19272343Sngie * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20272343Sngie * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21272343Sngie * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22272343Sngie * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23272343Sngie * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24272343Sngie * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25272343Sngie * SUCH DAMAGE.
26272343Sngie */
27272343Sngie
28272343Sngie#include <sys/types.h>
29272343Sngie#include <sys/time.h>
30272343Sngie
31272343Sngie#include <assert.h>
32272343Sngie#include <err.h>
33272343Sngie#include <errno.h>
34272343Sngie#include <puffs.h>
35272343Sngie#include <stdlib.h>
36272343Sngie#include <string.h>
37272343Sngie#include <unistd.h>
38272343Sngie#include <util.h>
39272343Sngie
40272343Sngie#include "dtfs.h"
41272343Sngie
42272343Sngievoid
43272343Sngiedtfs_baseattrs(struct vattr *vap, enum vtype type, ino_t id)
44272343Sngie{
45272343Sngie	struct timeval tv;
46272343Sngie	struct timespec ts;
47272343Sngie
48272343Sngie	gettimeofday(&tv, NULL);
49272343Sngie	TIMEVAL_TO_TIMESPEC(&tv, &ts);
50272343Sngie
51272343Sngie	vap->va_type = type;
52272343Sngie	if (type == VDIR) {
53272343Sngie		vap->va_mode = 0777;
54272343Sngie		vap->va_nlink = 1;	/* n + 1 after adding dent */
55272343Sngie	} else {
56272343Sngie		vap->va_mode = 0666;
57272343Sngie		vap->va_nlink = 0;	/* n + 1 */
58272343Sngie	}
59272343Sngie	vap->va_uid = 0;
60272343Sngie	vap->va_gid = 0;
61272343Sngie	vap->va_fileid = id;
62272343Sngie	vap->va_size = 0;
63272343Sngie	vap->va_blocksize = getpagesize();
64272343Sngie	vap->va_gen = random();
65272343Sngie	vap->va_flags = 0;
66272343Sngie	vap->va_rdev = PUFFS_VNOVAL;
67272343Sngie	vap->va_bytes = 0;
68272343Sngie	vap->va_filerev = 1;
69272343Sngie	vap->va_vaflags = 0;
70272343Sngie
71272343Sngie	vap->va_atime = vap->va_mtime = vap->va_ctime = vap->va_birthtime = ts;
72272343Sngie}
73272343Sngie
74272343Sngie/*
75272343Sngie * Well, as you can probably see, this interface has the slight problem
76272343Sngie * of assuming file creation will always be succesful, or at least not
77272343Sngie * giving a reason for the failure.  Be sure to do better when you
78272343Sngie * implement your own fs.
79272343Sngie */
80272343Sngiestruct puffs_node *
81272343Sngiedtfs_genfile(struct puffs_node *dir, const struct puffs_cn *pcn,
82272343Sngie	enum vtype type)
83272343Sngie{
84272343Sngie	struct dtfs_file *dff;
85272343Sngie	struct dtfs_dirent *dfd;
86272343Sngie	struct dtfs_mount *dtm;
87272343Sngie	struct puffs_node *newpn;
88272343Sngie	uid_t uid;
89272343Sngie	int rv;
90272343Sngie
91272343Sngie	assert(dir->pn_va.va_type == VDIR);
92272343Sngie	assert(dir->pn_mnt != NULL);
93272343Sngie
94272343Sngie	uid = 0;
95272343Sngie	rv = puffs_cred_getuid(pcn->pcn_cred, &uid);
96272343Sngie	assert(rv == 0);
97272343Sngie
98272343Sngie	if (type == VDIR) {
99272343Sngie		dff = dtfs_newdir();
100272343Sngie		dff->df_dotdot = dir;
101272343Sngie	} else
102272343Sngie		dff = dtfs_newfile();
103272343Sngie
104272343Sngie	dtm = puffs_pn_getmntspecific(dir);
105272343Sngie	newpn = puffs_pn_new(dir->pn_mnt, dff);
106272343Sngie	if (newpn == NULL)
107272343Sngie		errx(1, "getnewpnode");
108272343Sngie	dtfs_baseattrs(&newpn->pn_va, type, dtm->dtm_nextfileid++);
109272343Sngie
110272343Sngie	dfd = emalloc(sizeof(struct dtfs_dirent));
111272343Sngie	dfd->dfd_node = newpn;
112272343Sngie	dfd->dfd_name = estrndup(pcn->pcn_name, pcn->pcn_namelen);
113272343Sngie	dfd->dfd_namelen = strlen(dfd->dfd_name);
114272343Sngie	dfd->dfd_parent = dir;
115272343Sngie	dtfs_adddent(dir, dfd);
116272343Sngie
117272343Sngie	newpn->pn_va.va_uid = uid;
118272343Sngie	newpn->pn_va.va_gid = dir->pn_va.va_gid;
119272343Sngie
120272343Sngie	return newpn;
121272343Sngie}
122272343Sngie
123272343Sngiestruct dtfs_file *
124272343Sngiedtfs_newdir()
125272343Sngie{
126272343Sngie	struct dtfs_file *dff;
127272343Sngie
128272343Sngie	dff = emalloc(sizeof(struct dtfs_file));
129272343Sngie	memset(dff, 0, sizeof(struct dtfs_file));
130272343Sngie	LIST_INIT(&dff->df_dirents);
131272343Sngie
132272343Sngie	return dff;
133272343Sngie}
134272343Sngie
135272343Sngiestruct dtfs_file *
136272343Sngiedtfs_newfile()
137272343Sngie{
138272343Sngie	struct dtfs_file *dff;
139272343Sngie
140272343Sngie	dff = emalloc(sizeof(struct dtfs_file));
141272343Sngie	memset(dff, 0, sizeof(struct dtfs_file));
142272343Sngie
143272343Sngie	return dff;
144272343Sngie}
145272343Sngie
146272343Sngiestruct dtfs_dirent *
147272343Sngiedtfs_dirgetnth(struct dtfs_file *searchdir, int n)
148272343Sngie{
149272343Sngie	struct dtfs_dirent *dirent;
150272343Sngie	int i;
151272343Sngie
152272343Sngie	i = 0;
153272343Sngie	LIST_FOREACH(dirent, &searchdir->df_dirents, dfd_entries) {
154272343Sngie		if (i == n)
155272343Sngie			return dirent;
156272343Sngie		i++;
157272343Sngie	}
158272343Sngie
159272343Sngie	return NULL;
160272343Sngie}
161272343Sngie
162272343Sngiestruct dtfs_dirent *
163272343Sngiedtfs_dirgetbyname(struct dtfs_file *searchdir, const char *fname, size_t fnlen)
164272343Sngie{
165272343Sngie	struct dtfs_dirent *dirent;
166272343Sngie
167272343Sngie	LIST_FOREACH(dirent, &searchdir->df_dirents, dfd_entries)
168272343Sngie		if (dirent->dfd_namelen == fnlen
169272343Sngie		    && strncmp(dirent->dfd_name, fname, fnlen) == 0)
170272343Sngie			return dirent;
171272343Sngie
172272343Sngie	return NULL;
173272343Sngie}
174272343Sngie
175272343Sngie/*
176272343Sngie * common nuke, kill dirent from parent node
177272343Sngie */
178272343Sngievoid
179272343Sngiedtfs_nukenode(struct puffs_node *nukeme, struct puffs_node *pn_parent,
180272343Sngie	const char *fname, size_t fnlen)
181272343Sngie{
182272343Sngie	struct dtfs_dirent *dfd;
183272343Sngie	struct dtfs_mount *dtm;
184272343Sngie
185272343Sngie	assert(pn_parent->pn_va.va_type == VDIR);
186272343Sngie
187272343Sngie	dfd = dtfs_dirgetbyname(DTFS_PTOF(pn_parent), fname, fnlen);
188272343Sngie	assert(dfd);
189272343Sngie
190272343Sngie	dtm = puffs_pn_getmntspecific(nukeme);
191272343Sngie	dtm->dtm_nfiles--;
192272343Sngie	assert(dtm->dtm_nfiles >= 1);
193272343Sngie
194272343Sngie	dtfs_removedent(pn_parent, dfd);
195272343Sngie	free(dfd);
196272343Sngie}
197272343Sngie
198272343Sngie/* free lingering information */
199272343Sngievoid
200272343Sngiedtfs_freenode(struct puffs_node *pn)
201272343Sngie{
202272343Sngie	struct dtfs_file *df = DTFS_PTOF(pn);
203272343Sngie	struct dtfs_mount *dtm;
204272343Sngie	int i;
205272343Sngie
206272343Sngie	assert(pn->pn_va.va_nlink == 0);
207272343Sngie	dtm = puffs_pn_getmntspecific(pn);
208272343Sngie
209272343Sngie	switch (pn->pn_va.va_type) {
210272343Sngie	case VREG:
211272343Sngie		assert(dtm->dtm_fsizes >= pn->pn_va.va_size);
212272343Sngie		dtm->dtm_fsizes -= pn->pn_va.va_size;
213272343Sngie		for (i = 0; i < BLOCKNUM(df->df_datalen, DTFS_BLOCKSHIFT); i++)
214272343Sngie			free(df->df_blocks[i]);
215272343Sngie		if (df->df_datalen > i << DTFS_BLOCKSHIFT)
216272343Sngie			free(df->df_blocks[i]);
217272343Sngie		break;
218272343Sngie	case VLNK:
219272343Sngie		free(df->df_linktarget);
220272343Sngie		break;
221272343Sngie	case VCHR:
222272343Sngie	case VBLK:
223272343Sngie	case VDIR:
224272343Sngie	case VSOCK:
225272343Sngie	case VFIFO:
226272343Sngie		break;
227272343Sngie	default:
228272343Sngie		assert(0);
229272343Sngie		break;
230272343Sngie	}
231272343Sngie
232272343Sngie	free(df);
233272343Sngie	puffs_pn_put(pn);
234272343Sngie}
235272343Sngie
236272343Sngievoid
237272343Sngiedtfs_setsize(struct puffs_node *pn, off_t newsize)
238272343Sngie{
239272343Sngie	struct dtfs_file *df = DTFS_PTOF(pn);
240272343Sngie	struct dtfs_mount *dtm;
241272343Sngie	size_t newblocks;
242272343Sngie	int needalloc, shrinks;
243272343Sngie	int i;
244272343Sngie
245272343Sngie	needalloc = newsize > ROUNDUP(df->df_datalen, DTFS_BLOCKSIZE);
246272343Sngie	shrinks = newsize < pn->pn_va.va_size;
247272343Sngie
248272343Sngie	if (needalloc || shrinks) {
249272343Sngie		newblocks = BLOCKNUM(newsize, DTFS_BLOCKSHIFT) + 1;
250272343Sngie
251272343Sngie		if (shrinks)
252272343Sngie			for (i = newblocks; i < df->df_numblocks; i++)
253272343Sngie				free(df->df_blocks[i]);
254272343Sngie
255272343Sngie		df->df_blocks = erealloc(df->df_blocks,
256272343Sngie		    newblocks * sizeof(uint8_t *));
257272343Sngie		/*
258272343Sngie		 * if extended, set storage to zero
259272343Sngie		 * to match correct behaviour
260272343Sngie		 */
261272343Sngie		if (!shrinks) {
262272343Sngie			for (i = df->df_numblocks; i < newblocks; i++) {
263272343Sngie				df->df_blocks[i] = emalloc(DTFS_BLOCKSIZE);
264272343Sngie				memset(df->df_blocks[i], 0, DTFS_BLOCKSIZE);
265272343Sngie			}
266272343Sngie		}
267272343Sngie
268272343Sngie		df->df_datalen = newsize;
269272343Sngie		df->df_numblocks = newblocks;
270272343Sngie	}
271272343Sngie
272272343Sngie	dtm = puffs_pn_getmntspecific(pn);
273272343Sngie	if (!shrinks) {
274272343Sngie		dtm->dtm_fsizes += newsize - pn->pn_va.va_size;
275272343Sngie	} else {
276272343Sngie		dtm->dtm_fsizes -= pn->pn_va.va_size - newsize;
277272343Sngie	}
278272343Sngie
279272343Sngie	pn->pn_va.va_size = newsize;
280272343Sngie	pn->pn_va.va_bytes = BLOCKNUM(newsize,DTFS_BLOCKSHIFT)>>DTFS_BLOCKSHIFT;
281272343Sngie}
282272343Sngie
283272343Sngie/* add & bump link count */
284272343Sngievoid
285272343Sngiedtfs_adddent(struct puffs_node *pn_dir, struct dtfs_dirent *dent)
286272343Sngie{
287272343Sngie	struct dtfs_file *dir = DTFS_PTOF(pn_dir);
288272343Sngie	struct puffs_node *pn_file = dent->dfd_node;
289272343Sngie	struct dtfs_file *file = DTFS_PTOF(pn_file);
290272343Sngie	struct dtfs_mount *dtm;
291272343Sngie
292272343Sngie	assert(pn_dir->pn_va.va_type == VDIR);
293272343Sngie	LIST_INSERT_HEAD(&dir->df_dirents, dent, dfd_entries);
294272343Sngie	pn_file->pn_va.va_nlink++;
295272343Sngie
296272343Sngie	dtm = puffs_pn_getmntspecific(pn_file);
297272343Sngie	dtm->dtm_nfiles++;
298272343Sngie
299272343Sngie	dent->dfd_parent = pn_dir;
300272343Sngie	if (dent->dfd_node->pn_va.va_type == VDIR) {
301272343Sngie		file->df_dotdot = pn_dir;
302272343Sngie		pn_dir->pn_va.va_nlink++;
303272343Sngie	}
304272343Sngie
305272343Sngie	dtfs_updatetimes(pn_dir, 0, 1, 1);
306272343Sngie}
307272343Sngie
308272343Sngie/* remove & lower link count */
309272343Sngievoid
310272343Sngiedtfs_removedent(struct puffs_node *pn_dir, struct dtfs_dirent *dent)
311272343Sngie{
312272343Sngie	struct puffs_node *pn_file = dent->dfd_node;
313272343Sngie
314272343Sngie	assert(pn_dir->pn_va.va_type == VDIR);
315272343Sngie	LIST_REMOVE(dent, dfd_entries);
316272343Sngie	if (pn_file->pn_va.va_type == VDIR) {
317272343Sngie		struct dtfs_file *df = DTFS_PTOF(pn_file);
318272343Sngie
319272343Sngie		pn_dir->pn_va.va_nlink--;
320272343Sngie		df->df_dotdot = NULL;
321272343Sngie	}
322272343Sngie	pn_file->pn_va.va_nlink--;
323272343Sngie	assert(pn_dir->pn_va.va_nlink >= 2);
324272343Sngie
325272343Sngie	dtfs_updatetimes(pn_dir, 0, 1, 1);
326272343Sngie}
327272343Sngie
328272343Sngievoid
329272343Sngiedtfs_updatetimes(struct puffs_node *pn, int doatime, int doctime, int domtime)
330272343Sngie{
331272343Sngie	struct timeval tv;
332272343Sngie	struct timespec ts;
333272343Sngie
334272343Sngie	gettimeofday(&tv, NULL);
335272343Sngie	TIMEVAL_TO_TIMESPEC(&tv, &ts);
336272343Sngie
337272343Sngie	if (doatime)
338272343Sngie		pn->pn_va.va_atime = ts;
339272343Sngie	if (doctime)
340272343Sngie		pn->pn_va.va_ctime = ts;
341272343Sngie	if (domtime)
342272343Sngie		pn->pn_va.va_mtime = ts;
343272343Sngie}
344272343Sngie
345272343Sngiebool
346272343Sngiedtfs_isunder(struct puffs_node *pn, struct puffs_node *pn_parent)
347272343Sngie{
348272343Sngie	struct dtfs_file *df;
349272343Sngie
350272343Sngie	while (pn) {
351272343Sngie		if (pn == pn_parent)
352272343Sngie			return true;
353272343Sngie		df = DTFS_CTOF(pn);
354272343Sngie		pn = df->df_dotdot;
355272343Sngie	}
356272343Sngie
357272343Sngie	return false;
358272343Sngie}
359