dtfs_vnops.c revision 1.1 1 /* $NetBSD: dtfs_vnops.c,v 1.1 2010/07/06 14:16:44 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 puffs_node *pn_sdir = opc;
308 struct puffs_node *pn_tdir = targ_dir;
309 struct puffs_node *pn_tfile = targ;
310
311 dfd_src = dtfs_dirgetbyname(DTFS_PTOF(pn_sdir),
312 pcn_src->pcn_name, pcn_src->pcn_namelen);
313
314 /* asked for "." or ".." XXX: make sure? */
315 if (!dfd_src)
316 return EINVAL;
317
318 /* if there's a target file, nuke it for atomic replacement */
319 if (pn_tfile) {
320 if (pn_tfile->pn_va.va_type == VDIR) {
321 assert(/*CONSTCOND*/0); /* XXX FIXME */
322 }
323 dtfs_nukenode(pn_tfile, pn_sdir,
324 pcn_targ->pcn_name, pcn_targ->pcn_namelen);
325 }
326
327 /* out with the old */
328 dtfs_removedent(pn_sdir, dfd_src);
329 /* and in with the new */
330 dtfs_adddent(pn_tdir, dfd_src);
331
332 /* update name */
333 free(dfd_src->dfd_name);
334 dfd_src->dfd_name = estrndup(pcn_targ->pcn_name,pcn_targ->pcn_namelen);
335 dfd_src->dfd_namelen = strlen(dfd_src->dfd_name);
336
337 dtfs_updatetimes(src, 0, 1, 0);
338
339 return 0;
340 }
341
342 int
343 dtfs_node_link(struct puffs_usermount *pu, void *opc, void *targ,
344 const struct puffs_cn *pcn)
345 {
346 struct puffs_node *pn_dir = opc;
347 struct dtfs_dirent *dfd;
348
349 dfd = emalloc(sizeof(struct dtfs_dirent));
350 dfd->dfd_node = targ;
351 dfd->dfd_name = estrndup(pcn->pcn_name, pcn->pcn_namelen);
352 dfd->dfd_namelen = strlen(dfd->dfd_name);
353 dtfs_adddent(pn_dir, dfd);
354
355 dtfs_updatetimes(targ, 0, 1, 0);
356
357 return 0;
358 }
359
360 int
361 dtfs_node_symlink(struct puffs_usermount *pu, void *opc,
362 struct puffs_newinfo *pni, const struct puffs_cn *pcn_src,
363 const struct vattr *va, const char *link_target)
364 {
365 struct puffs_node *pn_parent = opc;
366 struct puffs_node *pn_new;
367 struct dtfs_file *df_new;
368
369 if (va->va_type != VLNK)
370 return ENODEV;
371
372 pn_new = dtfs_genfile(pn_parent, pcn_src, VLNK);
373 puffs_setvattr(&pn_new->pn_va, va);
374 df_new = DTFS_PTOF(pn_new);
375 df_new->df_linktarget = estrdup(link_target);
376 pn_new->pn_va.va_size = strlen(df_new->df_linktarget);
377
378 puffs_newinfo_setcookie(pni, pn_new);
379
380 return 0;
381 }
382
383 int
384 dtfs_node_readlink(struct puffs_usermount *pu, void *opc,
385 const struct puffs_cred *cred, char *linkname, size_t *linklen)
386 {
387 struct dtfs_file *df = DTFS_CTOF(opc);
388 struct puffs_node *pn = opc;
389
390 assert(pn->pn_va.va_type == VLNK);
391 strlcpy(linkname, df->df_linktarget, *linklen);
392 *linklen = strlen(linkname);
393
394 return 0;
395 }
396
397 int
398 dtfs_node_mknod(struct puffs_usermount *pu, void *opc,
399 struct puffs_newinfo *pni, const struct puffs_cn *pcn,
400 const struct vattr *va)
401 {
402 struct puffs_node *pn_parent = opc;
403 struct puffs_node *pn_new;
404 struct dtfs_file *df;
405
406 if (!(va->va_type == VBLK || va->va_type == VCHR
407 || va->va_type == VFIFO))
408 return EINVAL;
409
410 pn_new = dtfs_genfile(pn_parent, pcn, va->va_type);
411 puffs_setvattr(&pn_new->pn_va, va);
412
413 df = DTFS_PTOF(pn_new);
414 puffs_newinfo_setcookie(pni, pn_new);
415
416 return 0;
417 }
418
419 #define BLOCKOFF(a,b) ((a) & ((b)-1))
420 #define BLOCKLEFT(a,b) ((b) - BLOCKOFF(a,b))
421
422 /*
423 * Read operation, used both for VOP_READ and VOP_GETPAGES
424 */
425 int
426 dtfs_node_read(struct puffs_usermount *pu, void *opc, uint8_t *buf,
427 off_t offset, size_t *resid, const struct puffs_cred *pcr, int ioflag)
428 {
429 struct puffs_node *pn = opc;
430 struct dtfs_file *df = DTFS_CTOF(opc);
431 quad_t xfer, origxfer;
432 uint8_t *src, *dest;
433 size_t copylen;
434
435 if (pn->pn_va.va_type != VREG)
436 return EISDIR;
437
438 xfer = MIN(*resid, df->df_datalen - offset);
439 if (xfer < 0)
440 return EINVAL;
441
442 dest = buf;
443 origxfer = xfer;
444 while (xfer > 0) {
445 copylen = MIN(xfer, BLOCKLEFT(offset, DTFS_BLOCKSIZE));
446 src = df->df_blocks[BLOCKNUM(offset, DTFS_BLOCKSHIFT)]
447 + BLOCKOFF(offset, DTFS_BLOCKSIZE);
448 memcpy(dest, src, copylen);
449 offset += copylen;
450 dest += copylen;
451 xfer -= copylen;
452 }
453 *resid -= origxfer;
454
455 dtfs_updatetimes(pn, 1, 0, 0);
456
457 return 0;
458 }
459
460 /*
461 * write operation on the wing
462 */
463 int
464 dtfs_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf,
465 off_t offset, size_t *resid, const struct puffs_cred *pcr, int ioflag)
466 {
467 struct puffs_node *pn = opc;
468 struct dtfs_file *df = DTFS_CTOF(opc);
469 uint8_t *src, *dest;
470 size_t copylen;
471
472 if (pn->pn_va.va_type != VREG)
473 return EISDIR;
474
475 if (ioflag & PUFFS_IO_APPEND)
476 offset = pn->pn_va.va_size;
477
478 if (*resid + offset > pn->pn_va.va_size)
479 dtfs_setsize(pn, *resid + offset);
480
481 src = buf;
482 while (*resid > 0) {
483 int i;
484 copylen = MIN(*resid, BLOCKLEFT(offset, DTFS_BLOCKSIZE));
485 i = BLOCKNUM(offset, DTFS_BLOCKSHIFT);
486 dest = df->df_blocks[i]
487 + BLOCKOFF(offset, DTFS_BLOCKSIZE);
488 memcpy(dest, src, copylen);
489 offset += copylen;
490 dest += copylen;
491 *resid -= copylen;
492 }
493
494 dtfs_updatetimes(pn, 0, 1, 1);
495
496 return 0;
497 }
498
499 int
500 dtfs_node_inactive(struct puffs_usermount *pu, puffs_cookie_t opc)
501 {
502 struct puffs_node *pn = opc;
503
504 if (pn->pn_va.va_nlink == 0)
505 puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N1);
506 return 0;
507 }
508
509 int
510 dtfs_node_reclaim(struct puffs_usermount *pu, void *opc)
511 {
512 struct puffs_node *pn = opc;
513
514 if (pn->pn_va.va_nlink == 0)
515 dtfs_freenode(pn);
516
517 return 0;
518 }
519