1/*	$NetBSD: dtfs_vnops.c,v 1.10 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/poll.h>
30
31#include <assert.h>
32#include <errno.h>
33#include <puffs.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <unistd.h>
38#include <util.h>
39
40#include "dtfs.h"
41
42int
43dtfs_node_lookup(struct puffs_usermount *pu, void *opc,
44	struct puffs_newinfo *pni, const struct puffs_cn *pcn)
45{
46	struct puffs_node *pn_dir = opc;
47	struct dtfs_file *df = DTFS_CTOF(opc);
48	struct dtfs_dirent *dfd;
49	extern int straightflush;
50	int rv;
51
52	/* parent dir? */
53	if (PCNISDOTDOT(pcn)) {
54		if (df->df_dotdot == NULL)
55			return ENOENT;
56
57		assert(df->df_dotdot->pn_va.va_type == VDIR);
58		puffs_newinfo_setcookie(pni, df->df_dotdot);
59		puffs_newinfo_setvtype(pni, df->df_dotdot->pn_va.va_type);
60
61		return 0;
62	}
63
64	dfd = dtfs_dirgetbyname(df, pcn->pcn_name, pcn->pcn_namelen);
65	if (dfd) {
66		if ((pcn->pcn_flags & NAMEI_ISLASTCN) &&
67		    (pcn->pcn_nameiop == NAMEI_DELETE)) {
68			rv = puffs_access(VDIR, pn_dir->pn_va.va_mode,
69			    pn_dir->pn_va.va_uid, pn_dir->pn_va.va_gid,
70			    PUFFS_VWRITE, pcn->pcn_cred);
71			if (rv)
72				return rv;
73		}
74		puffs_newinfo_setcookie(pni, dfd->dfd_node);
75		puffs_newinfo_setvtype(pni, dfd->dfd_node->pn_va.va_type);
76		puffs_newinfo_setsize(pni, dfd->dfd_node->pn_va.va_size);
77		puffs_newinfo_setrdev(pni, dfd->dfd_node->pn_va.va_rdev);
78
79		if (straightflush)
80			puffs_flush_pagecache_node(pu, dfd->dfd_node);
81
82		return 0;
83	}
84
85	if ((pcn->pcn_flags & NAMEI_ISLASTCN)
86	    && (pcn->pcn_nameiop == NAMEI_CREATE ||
87	        pcn->pcn_nameiop == NAMEI_RENAME)) {
88		rv = puffs_access(VDIR, pn_dir->pn_va.va_mode,
89		    pn_dir->pn_va.va_uid, pn_dir->pn_va.va_gid,
90		    PUFFS_VWRITE, pcn->pcn_cred);
91		if (rv)
92			return rv;
93	}
94
95	return ENOENT;
96}
97
98int
99dtfs_node_access(struct puffs_usermount *pu, void *opc, int acc_mode,
100	const struct puffs_cred *pcr)
101{
102	struct puffs_node *pn = opc;
103
104	return puffs_access(pn->pn_va.va_type, pn->pn_va.va_mode,
105	    pn->pn_va.va_uid, pn->pn_va.va_gid, acc_mode, pcr);
106}
107
108int
109dtfs_node_setattr(struct puffs_usermount *pu, void *opc,
110	const struct vattr *va, const struct puffs_cred *pcr)
111{
112	struct puffs_node *pn = opc;
113	int rv;
114
115	/* check permissions */
116	if (va->va_flags != PUFFS_VNOVAL)
117		return EOPNOTSUPP;
118
119	if (va->va_uid != PUFFS_VNOVAL || va->va_gid != PUFFS_VNOVAL) {
120		rv = puffs_access_chown(pn->pn_va.va_uid, pn->pn_va.va_gid,
121		    va->va_uid, va->va_gid, pcr);
122		if (rv)
123			return rv;
124	}
125
126	if (va->va_mode != PUFFS_VNOVAL) {
127		rv = puffs_access_chmod(pn->pn_va.va_uid, pn->pn_va.va_gid,
128		    pn->pn_va.va_type, va->va_mode, pcr);
129		if (rv)
130			return rv;
131	}
132
133	if ((va->va_atime.tv_sec != PUFFS_VNOVAL
134	      && va->va_atime.tv_nsec != PUFFS_VNOVAL)
135	    || (va->va_mtime.tv_sec != PUFFS_VNOVAL
136	      && va->va_mtime.tv_nsec != PUFFS_VNOVAL)) {
137		rv = puffs_access_times(pn->pn_va.va_uid, pn->pn_va.va_gid,
138		    pn->pn_va.va_mode, va->va_vaflags & VA_UTIMES_NULL, pcr);
139		if (rv)
140			return rv;
141	}
142
143	if (va->va_size != PUFFS_VNOVAL) {
144		switch (pn->pn_va.va_type) {
145		case VREG:
146			dtfs_setsize(pn, va->va_size);
147			pn->pn_va.va_bytes = va->va_size;
148			break;
149		case VBLK:
150		case VCHR:
151		case VFIFO:
152			break;
153		case VDIR:
154			return EISDIR;
155		default:
156			return EOPNOTSUPP;
157		}
158	}
159
160	puffs_setvattr(&pn->pn_va, va);
161
162	return 0;
163}
164
165/* create a new node in the parent directory specified by opc */
166int
167dtfs_node_create(struct puffs_usermount *pu, void *opc,
168	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
169	const struct vattr *va)
170{
171	struct puffs_node *pn_parent = opc;
172	struct puffs_node *pn_new;
173
174	if (!(va->va_type == VREG || va->va_type == VSOCK))
175		return ENODEV;
176
177	pn_new = dtfs_genfile(pn_parent, pcn, va->va_type);
178	puffs_setvattr(&pn_new->pn_va, va);
179
180	puffs_newinfo_setcookie(pni, pn_new);
181
182	return 0;
183}
184
185int
186dtfs_node_remove(struct puffs_usermount *pu, void *opc, void *targ,
187	const struct puffs_cn *pcn)
188{
189	struct puffs_node *pn_parent = opc;
190	struct puffs_node *pn = targ;
191
192	if (pn->pn_va.va_type == VDIR)
193		return EPERM;
194
195	dtfs_nukenode(targ, pn_parent, pcn->pcn_name, pcn->pcn_namelen);
196
197	if (pn->pn_va.va_nlink == 0)
198		puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2);
199
200	return 0;
201}
202
203int
204dtfs_node_mkdir(struct puffs_usermount *pu, void *opc,
205	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
206	const struct vattr *va)
207{
208	struct puffs_node *pn_parent = opc;
209	struct puffs_node *pn_new;
210
211	pn_new = dtfs_genfile(pn_parent, pcn, VDIR);
212	puffs_setvattr(&pn_new->pn_va, va);
213
214	puffs_newinfo_setcookie(pni, pn_new);
215
216	return 0;
217}
218
219int
220dtfs_node_rmdir(struct puffs_usermount *pu, void *opc, void *targ,
221	const struct puffs_cn *pcn)
222{
223	struct puffs_node *pn_parent = opc;
224	struct dtfs_file *df = DTFS_CTOF(targ);
225
226	if (!LIST_EMPTY(&df->df_dirents))
227		return ENOTEMPTY;
228
229	dtfs_nukenode(targ, pn_parent, pcn->pcn_name, pcn->pcn_namelen);
230	puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2);
231
232	return 0;
233}
234
235int
236dtfs_node_readdir(struct puffs_usermount *pu, void *opc,
237	struct dirent *dent, off_t *readoff, size_t *reslen,
238	const struct puffs_cred *pcr,
239	int *eofflag, off_t *cookies, size_t *ncookies)
240{
241	struct puffs_node *pn = opc;
242	struct puffs_node *pn_nth;
243	struct dtfs_dirent *dfd_nth;
244
245	if (pn->pn_va.va_type != VDIR)
246		return ENOTDIR;
247
248	dtfs_updatetimes(pn, 1, 0, 0);
249
250	*ncookies = 0;
251 again:
252	if (*readoff == DENT_DOT || *readoff == DENT_DOTDOT) {
253		puffs_gendotdent(&dent, pn->pn_va.va_fileid, *readoff, reslen);
254		(*readoff)++;
255		PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff);
256		goto again;
257	}
258
259	for (;;) {
260		dfd_nth = dtfs_dirgetnth(pn->pn_data, DENT_ADJ(*readoff));
261		if (!dfd_nth) {
262			*eofflag = 1;
263			break;
264		}
265		pn_nth = dfd_nth->dfd_node;
266
267		if (!puffs_nextdent(&dent, dfd_nth->dfd_name,
268		    pn_nth->pn_va.va_fileid,
269		    puffs_vtype2dt(pn_nth->pn_va.va_type),
270		    reslen))
271			break;
272
273		(*readoff)++;
274		PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff);
275	}
276
277	return 0;
278}
279
280int
281dtfs_node_poll(struct puffs_usermount *pu, void *opc, int *events)
282{
283	struct dtfs_mount *dtm = puffs_getspecific(pu);
284	struct dtfs_poll dp;
285	struct itimerval it;
286
287	memset(&it, 0, sizeof(struct itimerval));
288	it.it_value.tv_sec = 4;
289	if (setitimer(ITIMER_REAL, &it, NULL) == -1)
290		return errno;
291
292	dp.dp_pcc = puffs_cc_getcc(pu);
293	LIST_INSERT_HEAD(&dtm->dtm_pollent, &dp, dp_entries);
294	puffs_cc_yield(dp.dp_pcc);
295
296	*events = *events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM);
297	return 0;
298}
299
300int
301dtfs_node_mmap(struct puffs_usermount *pu, void *opc, vm_prot_t prot,
302	const struct puffs_cred *pcr)
303{
304	struct dtfs_mount *dtm = puffs_getspecific(pu);
305
306	if ((dtm->dtm_allowprot & prot) != prot)
307		return EACCES;
308
309	return 0;
310}
311
312int
313dtfs_node_rename(struct puffs_usermount *pu, void *opc, void *src,
314	const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
315	const struct puffs_cn *pcn_targ)
316{
317	struct dtfs_dirent *dfd_src;
318	struct dtfs_file *df_targ;
319	struct puffs_node *pn_sdir = opc;
320	struct puffs_node *pn_sfile = src;
321	struct puffs_node *pn_tdir = targ_dir;
322	struct puffs_node *pn_tfile = targ;
323
324	/* check that we don't do the old amigados trick */
325	if (pn_sfile->pn_va.va_type == VDIR) {
326		if (dtfs_isunder(pn_tdir, pn_sfile))
327			return EINVAL;
328
329		if ((pcn_src->pcn_namelen == 1 && pcn_src->pcn_name[0]=='.') ||
330		    opc == src ||
331		    PCNISDOTDOT(pcn_src) ||
332		    PCNISDOTDOT(pcn_targ)) {
333			return EINVAL;
334		}
335	}
336
337	dfd_src = dtfs_dirgetbyname(DTFS_PTOF(pn_sdir),
338	    pcn_src->pcn_name, pcn_src->pcn_namelen);
339
340	/* does it still exist, or did someone race us here? */
341	if (dfd_src == NULL) {
342		return ENOENT;
343	}
344
345	/* if there's a target file, nuke it for atomic replacement */
346	if (pn_tfile) {
347		if (pn_tfile->pn_va.va_type == VDIR) {
348			df_targ = DTFS_CTOF(pn_tfile);
349			if (!LIST_EMPTY(&df_targ->df_dirents))
350				return ENOTEMPTY;
351		}
352		dtfs_nukenode(pn_tfile, pn_tdir,
353		    pcn_targ->pcn_name, pcn_targ->pcn_namelen);
354	}
355
356	/* out with the old */
357	dtfs_removedent(pn_sdir, dfd_src);
358	/* and in with the new */
359	dtfs_adddent(pn_tdir, dfd_src);
360
361	/* update name */
362	free(dfd_src->dfd_name);
363	dfd_src->dfd_name = estrndup(pcn_targ->pcn_name,pcn_targ->pcn_namelen);
364	dfd_src->dfd_namelen = strlen(dfd_src->dfd_name);
365
366	dtfs_updatetimes(src, 0, 1, 0);
367
368	return 0;
369}
370
371int
372dtfs_node_link(struct puffs_usermount *pu, void *opc, void *targ,
373	const struct puffs_cn *pcn)
374{
375	struct puffs_node *pn_dir = opc;
376	struct dtfs_dirent *dfd;
377
378	dfd = emalloc(sizeof(struct dtfs_dirent));
379	dfd->dfd_node = targ;
380	dfd->dfd_name = estrndup(pcn->pcn_name, pcn->pcn_namelen);
381	dfd->dfd_namelen = strlen(dfd->dfd_name);
382	dtfs_adddent(pn_dir, dfd);
383
384	dtfs_updatetimes(targ, 0, 1, 0);
385
386	return 0;
387}
388
389int
390dtfs_node_symlink(struct puffs_usermount *pu, void *opc,
391	struct puffs_newinfo *pni, const struct puffs_cn *pcn_src,
392	const struct vattr *va, const char *link_target)
393{
394	struct puffs_node *pn_parent = opc;
395	struct puffs_node *pn_new;
396	struct dtfs_file *df_new;
397
398	if (va->va_type != VLNK)
399		return ENODEV;
400
401	pn_new = dtfs_genfile(pn_parent, pcn_src, VLNK);
402	puffs_setvattr(&pn_new->pn_va, va);
403	df_new = DTFS_PTOF(pn_new);
404	df_new->df_linktarget = estrdup(link_target);
405	pn_new->pn_va.va_size = strlen(df_new->df_linktarget);
406
407	puffs_newinfo_setcookie(pni, pn_new);
408
409	return 0;
410}
411
412int
413dtfs_node_readlink(struct puffs_usermount *pu, void *opc,
414	const struct puffs_cred *cred, char *linkname, size_t *linklen)
415{
416	struct dtfs_file *df = DTFS_CTOF(opc);
417	struct puffs_node *pn = opc;
418
419	assert(pn->pn_va.va_type == VLNK);
420	strlcpy(linkname, df->df_linktarget, *linklen);
421	*linklen = strlen(linkname);
422
423	return 0;
424}
425
426int
427dtfs_node_mknod(struct puffs_usermount *pu, void *opc,
428	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
429	const struct vattr *va)
430{
431	struct puffs_node *pn_parent = opc;
432	struct puffs_node *pn_new;
433
434	if (!(va->va_type == VBLK || va->va_type == VCHR
435	    || va->va_type == VFIFO))
436		return EINVAL;
437
438	pn_new = dtfs_genfile(pn_parent, pcn, va->va_type);
439	puffs_setvattr(&pn_new->pn_va, va);
440
441	puffs_newinfo_setcookie(pni, pn_new);
442
443	return 0;
444}
445
446#define BLOCKOFF(a,b) ((a) & ((b)-1))
447#define BLOCKLEFT(a,b) ((b) - BLOCKOFF(a,b))
448
449/*
450 * Read operation, used both for VOP_READ and VOP_GETPAGES
451 */
452int
453dtfs_node_read(struct puffs_usermount *pu, void *opc, uint8_t *buf,
454	off_t offset, size_t *resid, const struct puffs_cred *pcr, int ioflag)
455{
456	struct puffs_node *pn = opc;
457	struct dtfs_file *df = DTFS_CTOF(opc);
458	quad_t xfer, origxfer;
459	uint8_t *src, *dest;
460	size_t copylen;
461
462	if (pn->pn_va.va_type != VREG)
463		return EISDIR;
464
465	xfer = MIN(*resid, df->df_datalen - offset);
466	if (xfer < 0)
467		return EINVAL;
468
469	dest = buf;
470	origxfer = xfer;
471	while (xfer > 0) {
472		copylen = MIN(xfer, BLOCKLEFT(offset, DTFS_BLOCKSIZE));
473		src = df->df_blocks[BLOCKNUM(offset, DTFS_BLOCKSHIFT)]
474		    + BLOCKOFF(offset, DTFS_BLOCKSIZE);
475		memcpy(dest, src, copylen);
476		offset += copylen;
477		dest += copylen;
478		xfer -= copylen;
479	}
480	*resid -= origxfer;
481
482	dtfs_updatetimes(pn, 1, 0, 0);
483
484	return 0;
485}
486
487/*
488 * write operation on the wing
489 */
490int
491dtfs_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf,
492	off_t offset, size_t *resid, const struct puffs_cred *pcr, int ioflag)
493{
494	struct puffs_node *pn = opc;
495	struct dtfs_file *df = DTFS_CTOF(opc);
496	uint8_t *src, *dest;
497	size_t copylen;
498
499	if (pn->pn_va.va_type != VREG)
500		return EISDIR;
501
502	if (ioflag & PUFFS_IO_APPEND)
503		offset = pn->pn_va.va_size;
504
505	if (*resid + offset > pn->pn_va.va_size)
506		dtfs_setsize(pn, *resid + offset);
507
508	src = buf;
509	while (*resid > 0) {
510		int i;
511		copylen = MIN(*resid, BLOCKLEFT(offset, DTFS_BLOCKSIZE));
512		i = BLOCKNUM(offset, DTFS_BLOCKSHIFT);
513		dest = df->df_blocks[i]
514		    + BLOCKOFF(offset, DTFS_BLOCKSIZE);
515		memcpy(dest, src, copylen);
516		offset += copylen;
517		dest += copylen;
518		*resid -= copylen;
519	}
520
521	dtfs_updatetimes(pn, 0, 1, 1);
522
523	return 0;
524}
525
526int
527dtfs_node_pathconf(struct puffs_usermount *pu, puffs_cookie_t opc,
528	int name, register_t *retval)
529{
530
531	switch (name) {
532	case _PC_LINK_MAX:
533		*retval = LINK_MAX;
534		return 0;
535	case _PC_NAME_MAX:
536		*retval = NAME_MAX;
537		return 0;
538	case _PC_PATH_MAX:
539		*retval = PATH_MAX;
540		return 0;
541	case _PC_PIPE_BUF:
542		*retval = PIPE_BUF;
543		return 0;
544	case _PC_CHOWN_RESTRICTED:
545		*retval = 1;
546		return 0;
547	case _PC_NO_TRUNC:
548		*retval = 1;
549		return 0;
550	case _PC_SYNC_IO:
551		*retval = 1;
552		return 0;
553	case _PC_FILESIZEBITS:
554		*retval = 43; /* this one goes to 11 */
555		return 0;
556	case _PC_SYMLINK_MAX:
557		*retval = MAXPATHLEN;
558		return 0;
559	case _PC_2_SYMLINKS:
560		*retval = 1;
561		return 0;
562	default:
563		return EINVAL;
564	}
565}
566
567int
568dtfs_node_inactive(struct puffs_usermount *pu, puffs_cookie_t opc)
569{
570	struct puffs_node *pn = opc;
571
572	if (pn->pn_va.va_nlink == 0)
573		puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N1);
574	return 0;
575}
576
577int
578dtfs_node_reclaim(struct puffs_usermount *pu, void *opc)
579{
580	struct puffs_node *pn = opc;
581
582	if (pn->pn_va.va_nlink == 0)
583		dtfs_freenode(pn);
584
585	return 0;
586}
587