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
42 int
dtfs_node_lookup(struct puffs_usermount * pu,void * opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn)43 dtfs_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
98 int
dtfs_node_access(struct puffs_usermount * pu,void * opc,int acc_mode,const struct puffs_cred * pcr)99 dtfs_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
108 int
dtfs_node_setattr(struct puffs_usermount * pu,void * opc,const struct vattr * va,const struct puffs_cred * pcr)109 dtfs_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 */
166 int
dtfs_node_create(struct puffs_usermount * pu,void * opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn,const struct vattr * va)167 dtfs_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
185 int
dtfs_node_remove(struct puffs_usermount * pu,void * opc,void * targ,const struct puffs_cn * pcn)186 dtfs_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
203 int
dtfs_node_mkdir(struct puffs_usermount * pu,void * opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn,const struct vattr * va)204 dtfs_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
219 int
dtfs_node_rmdir(struct puffs_usermount * pu,void * opc,void * targ,const struct puffs_cn * pcn)220 dtfs_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
235 int
dtfs_node_readdir(struct puffs_usermount * pu,void * opc,struct dirent * dent,off_t * readoff,size_t * reslen,const struct puffs_cred * pcr,int * eofflag,off_t * cookies,size_t * ncookies)236 dtfs_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
280 int
dtfs_node_poll(struct puffs_usermount * pu,void * opc,int * events)281 dtfs_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
300 int
dtfs_node_mmap(struct puffs_usermount * pu,void * opc,vm_prot_t prot,const struct puffs_cred * pcr)301 dtfs_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
312 int
dtfs_node_rename(struct puffs_usermount * pu,void * opc,void * src,const struct puffs_cn * pcn_src,void * targ_dir,void * targ,const struct puffs_cn * pcn_targ)313 dtfs_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
371 int
dtfs_node_link(struct puffs_usermount * pu,void * opc,void * targ,const struct puffs_cn * pcn)372 dtfs_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
389 int
dtfs_node_symlink(struct puffs_usermount * pu,void * opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn_src,const struct vattr * va,const char * link_target)390 dtfs_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
412 int
dtfs_node_readlink(struct puffs_usermount * pu,void * opc,const struct puffs_cred * cred,char * linkname,size_t * linklen)413 dtfs_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
426 int
dtfs_node_mknod(struct puffs_usermount * pu,void * opc,struct puffs_newinfo * pni,const struct puffs_cn * pcn,const struct vattr * va)427 dtfs_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 */
452 int
dtfs_node_read(struct puffs_usermount * pu,void * opc,uint8_t * buf,off_t offset,size_t * resid,const struct puffs_cred * pcr,int ioflag)453 dtfs_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 */
490 int
dtfs_node_write(struct puffs_usermount * pu,void * opc,uint8_t * buf,off_t offset,size_t * resid,const struct puffs_cred * pcr,int ioflag)491 dtfs_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
526 int
dtfs_node_pathconf(struct puffs_usermount * pu,puffs_cookie_t opc,int name,register_t * retval)527 dtfs_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
567 int
dtfs_node_inactive(struct puffs_usermount * pu,puffs_cookie_t opc)568 dtfs_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
577 int
dtfs_node_reclaim(struct puffs_usermount * pu,void * opc)578 dtfs_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