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