]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/netbsd-tests/fs/puffs/h_dtfs/dtfs_vnops.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / netbsd-tests / fs / puffs / h_dtfs / dtfs_vnops.c
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }