1272343Sngie/*	$NetBSD: dtfs_vnops.c,v 1.10 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/poll.h>
30272343Sngie
31272343Sngie#include <assert.h>
32272343Sngie#include <errno.h>
33272343Sngie#include <puffs.h>
34272343Sngie#include <stdio.h>
35272343Sngie#include <stdlib.h>
36272343Sngie#include <string.h>
37272343Sngie#include <unistd.h>
38272343Sngie#include <util.h>
39272343Sngie
40272343Sngie#include "dtfs.h"
41272343Sngie
42272343Sngieint
43272343Sngiedtfs_node_lookup(struct puffs_usermount *pu, void *opc,
44272343Sngie	struct puffs_newinfo *pni, const struct puffs_cn *pcn)
45272343Sngie{
46272343Sngie	struct puffs_node *pn_dir = opc;
47272343Sngie	struct dtfs_file *df = DTFS_CTOF(opc);
48272343Sngie	struct dtfs_dirent *dfd;
49272343Sngie	extern int straightflush;
50272343Sngie	int rv;
51272343Sngie
52272343Sngie	/* parent dir? */
53272343Sngie	if (PCNISDOTDOT(pcn)) {
54272343Sngie		if (df->df_dotdot == NULL)
55272343Sngie			return ENOENT;
56272343Sngie
57272343Sngie		assert(df->df_dotdot->pn_va.va_type == VDIR);
58272343Sngie		puffs_newinfo_setcookie(pni, df->df_dotdot);
59272343Sngie		puffs_newinfo_setvtype(pni, df->df_dotdot->pn_va.va_type);
60272343Sngie
61272343Sngie		return 0;
62272343Sngie	}
63272343Sngie
64272343Sngie	dfd = dtfs_dirgetbyname(df, pcn->pcn_name, pcn->pcn_namelen);
65272343Sngie	if (dfd) {
66272343Sngie		if ((pcn->pcn_flags & NAMEI_ISLASTCN) &&
67272343Sngie		    (pcn->pcn_nameiop == NAMEI_DELETE)) {
68272343Sngie			rv = puffs_access(VDIR, pn_dir->pn_va.va_mode,
69272343Sngie			    pn_dir->pn_va.va_uid, pn_dir->pn_va.va_gid,
70272343Sngie			    PUFFS_VWRITE, pcn->pcn_cred);
71272343Sngie			if (rv)
72272343Sngie				return rv;
73272343Sngie		}
74272343Sngie		puffs_newinfo_setcookie(pni, dfd->dfd_node);
75272343Sngie		puffs_newinfo_setvtype(pni, dfd->dfd_node->pn_va.va_type);
76272343Sngie		puffs_newinfo_setsize(pni, dfd->dfd_node->pn_va.va_size);
77272343Sngie		puffs_newinfo_setrdev(pni, dfd->dfd_node->pn_va.va_rdev);
78272343Sngie
79272343Sngie		if (straightflush)
80272343Sngie			puffs_flush_pagecache_node(pu, dfd->dfd_node);
81272343Sngie
82272343Sngie		return 0;
83272343Sngie	}
84272343Sngie
85272343Sngie	if ((pcn->pcn_flags & NAMEI_ISLASTCN)
86272343Sngie	    && (pcn->pcn_nameiop == NAMEI_CREATE ||
87272343Sngie	        pcn->pcn_nameiop == NAMEI_RENAME)) {
88272343Sngie		rv = puffs_access(VDIR, pn_dir->pn_va.va_mode,
89272343Sngie		    pn_dir->pn_va.va_uid, pn_dir->pn_va.va_gid,
90272343Sngie		    PUFFS_VWRITE, pcn->pcn_cred);
91272343Sngie		if (rv)
92272343Sngie			return rv;
93272343Sngie	}
94272343Sngie
95272343Sngie	return ENOENT;
96272343Sngie}
97272343Sngie
98272343Sngieint
99272343Sngiedtfs_node_access(struct puffs_usermount *pu, void *opc, int acc_mode,
100272343Sngie	const struct puffs_cred *pcr)
101272343Sngie{
102272343Sngie	struct puffs_node *pn = opc;
103272343Sngie
104272343Sngie	return puffs_access(pn->pn_va.va_type, pn->pn_va.va_mode,
105272343Sngie	    pn->pn_va.va_uid, pn->pn_va.va_gid, acc_mode, pcr);
106272343Sngie}
107272343Sngie
108272343Sngieint
109272343Sngiedtfs_node_setattr(struct puffs_usermount *pu, void *opc,
110272343Sngie	const struct vattr *va, const struct puffs_cred *pcr)
111272343Sngie{
112272343Sngie	struct puffs_node *pn = opc;
113272343Sngie	int rv;
114272343Sngie
115272343Sngie	/* check permissions */
116272343Sngie	if (va->va_flags != PUFFS_VNOVAL)
117272343Sngie		return EOPNOTSUPP;
118272343Sngie
119272343Sngie	if (va->va_uid != PUFFS_VNOVAL || va->va_gid != PUFFS_VNOVAL) {
120272343Sngie		rv = puffs_access_chown(pn->pn_va.va_uid, pn->pn_va.va_gid,
121272343Sngie		    va->va_uid, va->va_gid, pcr);
122272343Sngie		if (rv)
123272343Sngie			return rv;
124272343Sngie	}
125272343Sngie
126272343Sngie	if (va->va_mode != PUFFS_VNOVAL) {
127272343Sngie		rv = puffs_access_chmod(pn->pn_va.va_uid, pn->pn_va.va_gid,
128272343Sngie		    pn->pn_va.va_type, va->va_mode, pcr);
129272343Sngie		if (rv)
130272343Sngie			return rv;
131272343Sngie	}
132272343Sngie
133272343Sngie	if ((va->va_atime.tv_sec != PUFFS_VNOVAL
134272343Sngie	      && va->va_atime.tv_nsec != PUFFS_VNOVAL)
135272343Sngie	    || (va->va_mtime.tv_sec != PUFFS_VNOVAL
136272343Sngie	      && va->va_mtime.tv_nsec != PUFFS_VNOVAL)) {
137272343Sngie		rv = puffs_access_times(pn->pn_va.va_uid, pn->pn_va.va_gid,
138272343Sngie		    pn->pn_va.va_mode, va->va_vaflags & VA_UTIMES_NULL, pcr);
139272343Sngie		if (rv)
140272343Sngie			return rv;
141272343Sngie	}
142272343Sngie
143272343Sngie	if (va->va_size != PUFFS_VNOVAL) {
144272343Sngie		switch (pn->pn_va.va_type) {
145272343Sngie		case VREG:
146272343Sngie			dtfs_setsize(pn, va->va_size);
147272343Sngie			pn->pn_va.va_bytes = va->va_size;
148272343Sngie			break;
149272343Sngie		case VBLK:
150272343Sngie		case VCHR:
151272343Sngie		case VFIFO:
152272343Sngie			break;
153272343Sngie		case VDIR:
154272343Sngie			return EISDIR;
155272343Sngie		default:
156272343Sngie			return EOPNOTSUPP;
157272343Sngie		}
158272343Sngie	}
159272343Sngie
160272343Sngie	puffs_setvattr(&pn->pn_va, va);
161272343Sngie
162272343Sngie	return 0;
163272343Sngie}
164272343Sngie
165272343Sngie/* create a new node in the parent directory specified by opc */
166272343Sngieint
167272343Sngiedtfs_node_create(struct puffs_usermount *pu, void *opc,
168272343Sngie	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
169272343Sngie	const struct vattr *va)
170272343Sngie{
171272343Sngie	struct puffs_node *pn_parent = opc;
172272343Sngie	struct puffs_node *pn_new;
173272343Sngie
174272343Sngie	if (!(va->va_type == VREG || va->va_type == VSOCK))
175272343Sngie		return ENODEV;
176272343Sngie
177272343Sngie	pn_new = dtfs_genfile(pn_parent, pcn, va->va_type);
178272343Sngie	puffs_setvattr(&pn_new->pn_va, va);
179272343Sngie
180272343Sngie	puffs_newinfo_setcookie(pni, pn_new);
181272343Sngie
182272343Sngie	return 0;
183272343Sngie}
184272343Sngie
185272343Sngieint
186272343Sngiedtfs_node_remove(struct puffs_usermount *pu, void *opc, void *targ,
187272343Sngie	const struct puffs_cn *pcn)
188272343Sngie{
189272343Sngie	struct puffs_node *pn_parent = opc;
190272343Sngie	struct puffs_node *pn = targ;
191272343Sngie
192272343Sngie	if (pn->pn_va.va_type == VDIR)
193272343Sngie		return EPERM;
194272343Sngie
195272343Sngie	dtfs_nukenode(targ, pn_parent, pcn->pcn_name, pcn->pcn_namelen);
196272343Sngie
197272343Sngie	if (pn->pn_va.va_nlink == 0)
198272343Sngie		puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2);
199272343Sngie
200272343Sngie	return 0;
201272343Sngie}
202272343Sngie
203272343Sngieint
204272343Sngiedtfs_node_mkdir(struct puffs_usermount *pu, void *opc,
205272343Sngie	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
206272343Sngie	const struct vattr *va)
207272343Sngie{
208272343Sngie	struct puffs_node *pn_parent = opc;
209272343Sngie	struct puffs_node *pn_new;
210272343Sngie
211272343Sngie	pn_new = dtfs_genfile(pn_parent, pcn, VDIR);
212272343Sngie	puffs_setvattr(&pn_new->pn_va, va);
213272343Sngie
214272343Sngie	puffs_newinfo_setcookie(pni, pn_new);
215272343Sngie
216272343Sngie	return 0;
217272343Sngie}
218272343Sngie
219272343Sngieint
220272343Sngiedtfs_node_rmdir(struct puffs_usermount *pu, void *opc, void *targ,
221272343Sngie	const struct puffs_cn *pcn)
222272343Sngie{
223272343Sngie	struct puffs_node *pn_parent = opc;
224272343Sngie	struct dtfs_file *df = DTFS_CTOF(targ);
225272343Sngie
226272343Sngie	if (!LIST_EMPTY(&df->df_dirents))
227272343Sngie		return ENOTEMPTY;
228272343Sngie
229272343Sngie	dtfs_nukenode(targ, pn_parent, pcn->pcn_name, pcn->pcn_namelen);
230272343Sngie	puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2);
231272343Sngie
232272343Sngie	return 0;
233272343Sngie}
234272343Sngie
235272343Sngieint
236272343Sngiedtfs_node_readdir(struct puffs_usermount *pu, void *opc,
237272343Sngie	struct dirent *dent, off_t *readoff, size_t *reslen,
238272343Sngie	const struct puffs_cred *pcr,
239272343Sngie	int *eofflag, off_t *cookies, size_t *ncookies)
240272343Sngie{
241272343Sngie	struct puffs_node *pn = opc;
242272343Sngie	struct puffs_node *pn_nth;
243272343Sngie	struct dtfs_dirent *dfd_nth;
244272343Sngie
245272343Sngie	if (pn->pn_va.va_type != VDIR)
246272343Sngie		return ENOTDIR;
247272343Sngie
248272343Sngie	dtfs_updatetimes(pn, 1, 0, 0);
249272343Sngie
250272343Sngie	*ncookies = 0;
251272343Sngie again:
252272343Sngie	if (*readoff == DENT_DOT || *readoff == DENT_DOTDOT) {
253272343Sngie		puffs_gendotdent(&dent, pn->pn_va.va_fileid, *readoff, reslen);
254272343Sngie		(*readoff)++;
255272343Sngie		PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff);
256272343Sngie		goto again;
257272343Sngie	}
258272343Sngie
259272343Sngie	for (;;) {
260272343Sngie		dfd_nth = dtfs_dirgetnth(pn->pn_data, DENT_ADJ(*readoff));
261272343Sngie		if (!dfd_nth) {
262272343Sngie			*eofflag = 1;
263272343Sngie			break;
264272343Sngie		}
265272343Sngie		pn_nth = dfd_nth->dfd_node;
266272343Sngie
267272343Sngie		if (!puffs_nextdent(&dent, dfd_nth->dfd_name,
268272343Sngie		    pn_nth->pn_va.va_fileid,
269272343Sngie		    puffs_vtype2dt(pn_nth->pn_va.va_type),
270272343Sngie		    reslen))
271272343Sngie			break;
272272343Sngie
273272343Sngie		(*readoff)++;
274272343Sngie		PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff);
275272343Sngie	}
276272343Sngie
277272343Sngie	return 0;
278272343Sngie}
279272343Sngie
280272343Sngieint
281272343Sngiedtfs_node_poll(struct puffs_usermount *pu, void *opc, int *events)
282272343Sngie{
283272343Sngie	struct dtfs_mount *dtm = puffs_getspecific(pu);
284272343Sngie	struct dtfs_poll dp;
285272343Sngie	struct itimerval it;
286272343Sngie
287272343Sngie	memset(&it, 0, sizeof(struct itimerval));
288272343Sngie	it.it_value.tv_sec = 4;
289272343Sngie	if (setitimer(ITIMER_REAL, &it, NULL) == -1)
290272343Sngie		return errno;
291272343Sngie
292272343Sngie	dp.dp_pcc = puffs_cc_getcc(pu);
293272343Sngie	LIST_INSERT_HEAD(&dtm->dtm_pollent, &dp, dp_entries);
294272343Sngie	puffs_cc_yield(dp.dp_pcc);
295272343Sngie
296272343Sngie	*events = *events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM);
297272343Sngie	return 0;
298272343Sngie}
299272343Sngie
300272343Sngieint
301272343Sngiedtfs_node_mmap(struct puffs_usermount *pu, void *opc, vm_prot_t prot,
302272343Sngie	const struct puffs_cred *pcr)
303272343Sngie{
304272343Sngie	struct dtfs_mount *dtm = puffs_getspecific(pu);
305272343Sngie
306272343Sngie	if ((dtm->dtm_allowprot & prot) != prot)
307272343Sngie		return EACCES;
308272343Sngie
309272343Sngie	return 0;
310272343Sngie}
311272343Sngie
312272343Sngieint
313272343Sngiedtfs_node_rename(struct puffs_usermount *pu, void *opc, void *src,
314272343Sngie	const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
315272343Sngie	const struct puffs_cn *pcn_targ)
316272343Sngie{
317272343Sngie	struct dtfs_dirent *dfd_src;
318272343Sngie	struct dtfs_file *df_targ;
319272343Sngie	struct puffs_node *pn_sdir = opc;
320272343Sngie	struct puffs_node *pn_sfile = src;
321272343Sngie	struct puffs_node *pn_tdir = targ_dir;
322272343Sngie	struct puffs_node *pn_tfile = targ;
323272343Sngie
324272343Sngie	/* check that we don't do the old amigados trick */
325272343Sngie	if (pn_sfile->pn_va.va_type == VDIR) {
326272343Sngie		if (dtfs_isunder(pn_tdir, pn_sfile))
327272343Sngie			return EINVAL;
328272343Sngie
329272343Sngie		if ((pcn_src->pcn_namelen == 1 && pcn_src->pcn_name[0]=='.') ||
330272343Sngie		    opc == src ||
331272343Sngie		    PCNISDOTDOT(pcn_src) ||
332272343Sngie		    PCNISDOTDOT(pcn_targ)) {
333272343Sngie			return EINVAL;
334272343Sngie		}
335272343Sngie	}
336272343Sngie
337272343Sngie	dfd_src = dtfs_dirgetbyname(DTFS_PTOF(pn_sdir),
338272343Sngie	    pcn_src->pcn_name, pcn_src->pcn_namelen);
339272343Sngie
340272343Sngie	/* does it still exist, or did someone race us here? */
341272343Sngie	if (dfd_src == NULL) {
342272343Sngie		return ENOENT;
343272343Sngie	}
344272343Sngie
345272343Sngie	/* if there's a target file, nuke it for atomic replacement */
346272343Sngie	if (pn_tfile) {
347272343Sngie		if (pn_tfile->pn_va.va_type == VDIR) {
348272343Sngie			df_targ = DTFS_CTOF(pn_tfile);
349272343Sngie			if (!LIST_EMPTY(&df_targ->df_dirents))
350272343Sngie				return ENOTEMPTY;
351272343Sngie		}
352272343Sngie		dtfs_nukenode(pn_tfile, pn_tdir,
353272343Sngie		    pcn_targ->pcn_name, pcn_targ->pcn_namelen);
354272343Sngie	}
355272343Sngie
356272343Sngie	/* out with the old */
357272343Sngie	dtfs_removedent(pn_sdir, dfd_src);
358272343Sngie	/* and in with the new */
359272343Sngie	dtfs_adddent(pn_tdir, dfd_src);
360272343Sngie
361272343Sngie	/* update name */
362272343Sngie	free(dfd_src->dfd_name);
363272343Sngie	dfd_src->dfd_name = estrndup(pcn_targ->pcn_name,pcn_targ->pcn_namelen);
364272343Sngie	dfd_src->dfd_namelen = strlen(dfd_src->dfd_name);
365272343Sngie
366272343Sngie	dtfs_updatetimes(src, 0, 1, 0);
367272343Sngie
368272343Sngie	return 0;
369272343Sngie}
370272343Sngie
371272343Sngieint
372272343Sngiedtfs_node_link(struct puffs_usermount *pu, void *opc, void *targ,
373272343Sngie	const struct puffs_cn *pcn)
374272343Sngie{
375272343Sngie	struct puffs_node *pn_dir = opc;
376272343Sngie	struct dtfs_dirent *dfd;
377272343Sngie
378272343Sngie	dfd = emalloc(sizeof(struct dtfs_dirent));
379272343Sngie	dfd->dfd_node = targ;
380272343Sngie	dfd->dfd_name = estrndup(pcn->pcn_name, pcn->pcn_namelen);
381272343Sngie	dfd->dfd_namelen = strlen(dfd->dfd_name);
382272343Sngie	dtfs_adddent(pn_dir, dfd);
383272343Sngie
384272343Sngie	dtfs_updatetimes(targ, 0, 1, 0);
385272343Sngie
386272343Sngie	return 0;
387272343Sngie}
388272343Sngie
389272343Sngieint
390272343Sngiedtfs_node_symlink(struct puffs_usermount *pu, void *opc,
391272343Sngie	struct puffs_newinfo *pni, const struct puffs_cn *pcn_src,
392272343Sngie	const struct vattr *va, const char *link_target)
393272343Sngie{
394272343Sngie	struct puffs_node *pn_parent = opc;
395272343Sngie	struct puffs_node *pn_new;
396272343Sngie	struct dtfs_file *df_new;
397272343Sngie
398272343Sngie	if (va->va_type != VLNK)
399272343Sngie		return ENODEV;
400272343Sngie
401272343Sngie	pn_new = dtfs_genfile(pn_parent, pcn_src, VLNK);
402272343Sngie	puffs_setvattr(&pn_new->pn_va, va);
403272343Sngie	df_new = DTFS_PTOF(pn_new);
404272343Sngie	df_new->df_linktarget = estrdup(link_target);
405272343Sngie	pn_new->pn_va.va_size = strlen(df_new->df_linktarget);
406272343Sngie
407272343Sngie	puffs_newinfo_setcookie(pni, pn_new);
408272343Sngie
409272343Sngie	return 0;
410272343Sngie}
411272343Sngie
412272343Sngieint
413272343Sngiedtfs_node_readlink(struct puffs_usermount *pu, void *opc,
414272343Sngie	const struct puffs_cred *cred, char *linkname, size_t *linklen)
415272343Sngie{
416272343Sngie	struct dtfs_file *df = DTFS_CTOF(opc);
417272343Sngie	struct puffs_node *pn = opc;
418272343Sngie
419272343Sngie	assert(pn->pn_va.va_type == VLNK);
420272343Sngie	strlcpy(linkname, df->df_linktarget, *linklen);
421272343Sngie	*linklen = strlen(linkname);
422272343Sngie
423272343Sngie	return 0;
424272343Sngie}
425272343Sngie
426272343Sngieint
427272343Sngiedtfs_node_mknod(struct puffs_usermount *pu, void *opc,
428272343Sngie	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
429272343Sngie	const struct vattr *va)
430272343Sngie{
431272343Sngie	struct puffs_node *pn_parent = opc;
432272343Sngie	struct puffs_node *pn_new;
433272343Sngie
434272343Sngie	if (!(va->va_type == VBLK || va->va_type == VCHR
435272343Sngie	    || va->va_type == VFIFO))
436272343Sngie		return EINVAL;
437272343Sngie
438272343Sngie	pn_new = dtfs_genfile(pn_parent, pcn, va->va_type);
439272343Sngie	puffs_setvattr(&pn_new->pn_va, va);
440272343Sngie
441272343Sngie	puffs_newinfo_setcookie(pni, pn_new);
442272343Sngie
443272343Sngie	return 0;
444272343Sngie}
445272343Sngie
446272343Sngie#define BLOCKOFF(a,b) ((a) & ((b)-1))
447272343Sngie#define BLOCKLEFT(a,b) ((b) - BLOCKOFF(a,b))
448272343Sngie
449272343Sngie/*
450272343Sngie * Read operation, used both for VOP_READ and VOP_GETPAGES
451272343Sngie */
452272343Sngieint
453272343Sngiedtfs_node_read(struct puffs_usermount *pu, void *opc, uint8_t *buf,
454272343Sngie	off_t offset, size_t *resid, const struct puffs_cred *pcr, int ioflag)
455272343Sngie{
456272343Sngie	struct puffs_node *pn = opc;
457272343Sngie	struct dtfs_file *df = DTFS_CTOF(opc);
458272343Sngie	quad_t xfer, origxfer;
459272343Sngie	uint8_t *src, *dest;
460272343Sngie	size_t copylen;
461272343Sngie
462272343Sngie	if (pn->pn_va.va_type != VREG)
463272343Sngie		return EISDIR;
464272343Sngie
465272343Sngie	xfer = MIN(*resid, df->df_datalen - offset);
466272343Sngie	if (xfer < 0)
467272343Sngie		return EINVAL;
468272343Sngie
469272343Sngie	dest = buf;
470272343Sngie	origxfer = xfer;
471272343Sngie	while (xfer > 0) {
472272343Sngie		copylen = MIN(xfer, BLOCKLEFT(offset, DTFS_BLOCKSIZE));
473272343Sngie		src = df->df_blocks[BLOCKNUM(offset, DTFS_BLOCKSHIFT)]
474272343Sngie		    + BLOCKOFF(offset, DTFS_BLOCKSIZE);
475272343Sngie		memcpy(dest, src, copylen);
476272343Sngie		offset += copylen;
477272343Sngie		dest += copylen;
478272343Sngie		xfer -= copylen;
479272343Sngie	}
480272343Sngie	*resid -= origxfer;
481272343Sngie
482272343Sngie	dtfs_updatetimes(pn, 1, 0, 0);
483272343Sngie
484272343Sngie	return 0;
485272343Sngie}
486272343Sngie
487272343Sngie/*
488272343Sngie * write operation on the wing
489272343Sngie */
490272343Sngieint
491272343Sngiedtfs_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf,
492272343Sngie	off_t offset, size_t *resid, const struct puffs_cred *pcr, int ioflag)
493272343Sngie{
494272343Sngie	struct puffs_node *pn = opc;
495272343Sngie	struct dtfs_file *df = DTFS_CTOF(opc);
496272343Sngie	uint8_t *src, *dest;
497272343Sngie	size_t copylen;
498272343Sngie
499272343Sngie	if (pn->pn_va.va_type != VREG)
500272343Sngie		return EISDIR;
501272343Sngie
502272343Sngie	if (ioflag & PUFFS_IO_APPEND)
503272343Sngie		offset = pn->pn_va.va_size;
504272343Sngie
505272343Sngie	if (*resid + offset > pn->pn_va.va_size)
506272343Sngie		dtfs_setsize(pn, *resid + offset);
507272343Sngie
508272343Sngie	src = buf;
509272343Sngie	while (*resid > 0) {
510272343Sngie		int i;
511272343Sngie		copylen = MIN(*resid, BLOCKLEFT(offset, DTFS_BLOCKSIZE));
512272343Sngie		i = BLOCKNUM(offset, DTFS_BLOCKSHIFT);
513272343Sngie		dest = df->df_blocks[i]
514272343Sngie		    + BLOCKOFF(offset, DTFS_BLOCKSIZE);
515272343Sngie		memcpy(dest, src, copylen);
516272343Sngie		offset += copylen;
517272343Sngie		dest += copylen;
518272343Sngie		*resid -= copylen;
519272343Sngie	}
520272343Sngie
521272343Sngie	dtfs_updatetimes(pn, 0, 1, 1);
522272343Sngie
523272343Sngie	return 0;
524272343Sngie}
525272343Sngie
526272343Sngieint
527272343Sngiedtfs_node_pathconf(struct puffs_usermount *pu, puffs_cookie_t opc,
528272343Sngie	int name, register_t *retval)
529272343Sngie{
530272343Sngie
531272343Sngie	switch (name) {
532272343Sngie	case _PC_LINK_MAX:
533272343Sngie		*retval = LINK_MAX;
534272343Sngie		return 0;
535272343Sngie	case _PC_NAME_MAX:
536272343Sngie		*retval = NAME_MAX;
537272343Sngie		return 0;
538272343Sngie	case _PC_PATH_MAX:
539272343Sngie		*retval = PATH_MAX;
540272343Sngie		return 0;
541272343Sngie	case _PC_PIPE_BUF:
542272343Sngie		*retval = PIPE_BUF;
543272343Sngie		return 0;
544272343Sngie	case _PC_CHOWN_RESTRICTED:
545272343Sngie		*retval = 1;
546272343Sngie		return 0;
547272343Sngie	case _PC_NO_TRUNC:
548272343Sngie		*retval = 1;
549272343Sngie		return 0;
550272343Sngie	case _PC_SYNC_IO:
551272343Sngie		*retval = 1;
552272343Sngie		return 0;
553272343Sngie	case _PC_FILESIZEBITS:
554272343Sngie		*retval = 43; /* this one goes to 11 */
555272343Sngie		return 0;
556272343Sngie	case _PC_SYMLINK_MAX:
557272343Sngie		*retval = MAXPATHLEN;
558272343Sngie		return 0;
559272343Sngie	case _PC_2_SYMLINKS:
560272343Sngie		*retval = 1;
561272343Sngie		return 0;
562272343Sngie	default:
563272343Sngie		return EINVAL;
564272343Sngie	}
565272343Sngie}
566272343Sngie
567272343Sngieint
568272343Sngiedtfs_node_inactive(struct puffs_usermount *pu, puffs_cookie_t opc)
569272343Sngie{
570272343Sngie	struct puffs_node *pn = opc;
571272343Sngie
572272343Sngie	if (pn->pn_va.va_nlink == 0)
573272343Sngie		puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N1);
574272343Sngie	return 0;
575272343Sngie}
576272343Sngie
577272343Sngieint
578272343Sngiedtfs_node_reclaim(struct puffs_usermount *pu, void *opc)
579272343Sngie{
580272343Sngie	struct puffs_node *pn = opc;
581272343Sngie
582272343Sngie	if (pn->pn_va.va_nlink == 0)
583272343Sngie		dtfs_freenode(pn);
584272343Sngie
585272343Sngie	return 0;
586272343Sngie}
587