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