diff --git a/lib/libperfuse/ops.c b/lib/libperfuse/ops.c index 655400c9281e..46f8a8e927b1 100644 --- a/lib/libperfuse/ops.c +++ b/lib/libperfuse/ops.c @@ -1,4 +1,4 @@ -/* $NetBSD: ops.c,v 1.6 2010/09/02 08:58:06 manu Exp $ */ +/* $NetBSD: ops.c,v 1.7 2010/09/03 07:15:18 manu Exp $ */ /*- * Copyright (c) 2010 Emmanuel Dreyfus. All rights reserved. @@ -41,6 +41,7 @@ #include "perfuse_priv.h" #include "fuse.h" +static int node_close_common(struct puffs_usermount *, puffs_cookie_t, int); static int no_access(puffs_cookie_t, const struct puffs_cred *, mode_t); static void fuse_attr_to_vap(struct perfuse_state *, struct vattr *, struct fuse_attr *); @@ -74,6 +75,7 @@ static void dequeue_requests(struct perfuse_state *, */ #define F_WAIT 0x010 #define F_FLOCK 0x020 +#define OFLAGS(fflags) ((fflags) - 1) /* * Borrowed from src/sys/kern/vfs_subr.c and src/sys/sys/vnode.h @@ -90,6 +92,74 @@ const int vttoif_tab[9] = { #define IFTOVT(mode) (iftovt_tab[((mode) & S_IFMT) >> 12]) #define VTTOIF(indx) (vttoif_tab[(int)(indx)]) +static int +node_close_common(pu, opc, mode) + struct puffs_usermount *pu; + puffs_cookie_t opc; + int mode; +{ + struct perfuse_state *ps; + perfuse_msg_t *pm; + int op; + uint64_t fh; + struct fuse_release_in *fri; + struct perfuse_node_data *pnd; + struct puffs_node *pn; + int error; + + ps = puffs_getspecific(pu); + pn = (struct puffs_node *)opc; + pnd = PERFUSE_NODE_DATA(pn); + + if (puffs_pn_getvap(pn)->va_type == VDIR) { + op = FUSE_RELEASEDIR; + mode = FREAD; + } else { + op = FUSE_RELEASE; + } + + /* + * Destroy the filehandle before sending the + * request to the FUSE filesystem, otherwise + * we may get a second close() while we wait + * for the reply, and we would end up closing + * the same fh twice instead of closng both. + */ + fh = perfuse_get_fh(opc, mode); + perfuse_destroy_fh(pn, fh); + + /* + * release_flags may be set to FUSE_RELEASE_FLUSH + * to flush locks. lock_owner must be set in that case + */ + pm = ps->ps_new_msg(pu, opc, op, sizeof(*fri), NULL); + fri = GET_INPAYLOAD(ps, pm, fuse_release_in); + fri->fh = fh; + fri->flags = 0; + fri->release_flags = 0; + fri->lock_owner = pnd->pnd_lock_owner; + fri->flags = (fri->lock_owner != 0) ? FUSE_RELEASE_FLUSH : 0; + +#ifdef PERFUSE_DEBUG + if (perfuse_diagflags & PDF_FH) + DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n", + __func__, (void *)opc, pnd->pnd_ino, fri->fh); +#endif + + if ((error = XCHG_MSG(ps, pu, pm, NO_PAYLOAD_REPLY_LEN)) != 0) + goto out; + + ps->ps_destroy_msg(pm); + + error = 0; + +out: + if (error != 0) + DERRX(EX_SOFTWARE, "%s: freed fh = 0x%"PRIx64" but filesystem " + "returned error = %d", __func__, fh, error); + + return error; +} static int no_access(opc, pcr, mode) @@ -801,6 +871,19 @@ perfuse_node_lookup(pu, opc, pni, pcn) struct puffs_node *pn; int error; + /* + * Special case for .. + */ + if (PCNISDOTDOT(pcn)) { + pn = PERFUSE_NODE_DATA(opc)->pnd_parent; + PERFUSE_NODE_DATA(pn)->pnd_flags &= ~PND_RECLAIMED; + + puffs_newinfo_setcookie(pni, pn); + puffs_newinfo_setvtype(pni, VDIR); + + return 0; + } + /* * XXX This is borrowed from librefuse, * and __UNCONST is said to be fixed. @@ -866,6 +949,12 @@ perfuse_node_create(pu, opc, pni, pcn, vap) if (error != 0) return error; + error = node_lookup_common(pu, opc, (char*)PCNPATH(pcn), &pn); + if (error != 0) + return error; + + opc = (puffs_cookie_t)pn; + error = perfuse_node_open(pu, opc, FREAD|FWRITE, pcn->pcn_cred); if (error != 0) return error; @@ -898,12 +987,26 @@ perfuse_node_create(pu, opc, pni, pcn, vap) * so that we can reuse it later */ pn = perfuse_new_pn(pu, opc); - perfuse_new_fh((puffs_cookie_t)pn, foo->fh); + perfuse_new_fh((puffs_cookie_t)pn, foo->fh, FWRITE); PERFUSE_NODE_DATA(pn)->pnd_ino = feo->nodeid; +#ifdef PERFUSE_DEBUG + if (perfuse_diagflags & PDF_FH) + DPRINTF("%s: opc = %p, file = \"%s\", " + "ino = %"PRId64", rfh = 0x%"PRIx64"\n", + __func__, (void *)pn, (char *)PCNPATH(pcn), + feo->nodeid, foo->fh); +#endif + fuse_attr_to_vap(ps, &pn->pn_va, &feo->attr); puffs_newinfo_setcookie(pni, pn); + /* + * It seems we need to do this so that glusterfs gets fully + * aware that the file was created. If we do not do it, we + * get "SETATTR (null) (fuse_loc_fill() failed)" + */ + (void)node_lookup_common(pu, opc, (char*)PCNPATH(pcn), NULL); out: ps->ps_destroy_msg(pm); @@ -979,8 +1082,10 @@ perfuse_node_open(pu, opc, mode, pcr) const struct puffs_cred *pcr; { struct perfuse_state *ps; + struct perfuse_node_data *pnd; perfuse_msg_t *pm; mode_t pmode; + mode_t fmode; int op; struct fuse_open_in *foi; struct fuse_open_out *foo; @@ -988,6 +1093,7 @@ perfuse_node_open(pu, opc, mode, pcr) int error; ps = puffs_getspecific(pu); + pnd = PERFUSE_NODE_DATA(opc); pn = (struct puffs_node *)opc; if (puffs_pn_getvap(pn)->va_type == VDIR) { @@ -995,8 +1101,8 @@ perfuse_node_open(pu, opc, mode, pcr) pmode = PUFFS_VREAD|PUFFS_VEXEC; } else { op = FUSE_OPEN; - if (mode & (O_RDWR|O_WRONLY)) - pmode = PUFFS_VWRITE; + if (mode & FWRITE) + pmode = PUFFS_VWRITE|PUFFS_VREAD; else pmode = PUFFS_VREAD; } @@ -1006,8 +1112,7 @@ perfuse_node_open(pu, opc, mode, pcr) * Opening a file requires R-- for reading, -W- for writing * In both cases, --X is required on the parent. */ - if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent, - pcr, PUFFS_VEXEC)) + if (no_access((puffs_cookie_t)pnd->pnd_parent, pcr, PUFFS_VEXEC)) return EACCES; if (no_access(opc, pcr, pmode)) @@ -1017,23 +1122,30 @@ perfuse_node_open(pu, opc, mode, pcr) * libfuse docs say O_CREAT should not be set. */ mode &= ~O_CREAT; - + + /* + * Do not open twice, and do not reopen for reading + * if we already have write handle. + * Directories are always open with read access only, + * whatever flags we get. + */ + if (op == FUSE_OPENDIR) + mode = (mode & ~(FREAD|FWRITE)) | FREAD; + if ((mode & FREAD) && (pnd->pnd_flags & PND_RFH)) + return 0; + if ((mode & FWRITE) && (pnd->pnd_flags & PND_WFH)) + return 0; + + /* + * Convert PUFFS mode to FUSE mode: convert FREAD/FWRITE + * to O_RDONLY/O_WRONLY while perserving the other options. + */ + fmode = mode & ~(FREAD|FWRITE); + fmode |= (mode & FWRITE) ? O_RDWR : O_RDONLY; + pm = ps->ps_new_msg(pu, opc, op, sizeof(*foi), pcr); foi = GET_INPAYLOAD(ps, pm, fuse_open_in); - foi->flags = mode & ~O_ACCMODE; - switch (mode & (FREAD|FWRITE)) { - case FREAD|FWRITE: - foi->flags |= O_RDWR; - break; - case FREAD: - foi->flags |= O_RDONLY; - break; - case FWRITE: - foi->flags |= O_WRONLY; - break; - default: - break; - } + foi->flags = fmode; foi->unused = 0; if ((error = XCHG_MSG(ps, pu, pm, sizeof(*foo))) != 0) @@ -1045,15 +1157,16 @@ perfuse_node_open(pu, opc, mode, pcr) * Save the file handle in node private data * so that we can reuse it later */ - perfuse_new_fh((puffs_cookie_t)pn, foo->fh); + perfuse_new_fh((puffs_cookie_t)pn, foo->fh, mode); #ifdef PERFUSE_DEBUG if (perfuse_diagflags & PDF_FH) DPRINTF("%s: opc = %p, file = \"%s\", " - "ino = %"PRId64", fh = 0x%"PRIx64"\n", + "ino = %"PRId64", %s%sfh = 0x%"PRIx64"\n", __func__, (void *)opc, (char *)PNPATH((struct puffs_node *)opc), - PERFUSE_NODE_DATA(opc)->pnd_ino, foo->fh); + pnd->pnd_ino, mode & FREAD ? "r" : "", + mode & FWRITE ? "w" : "", foo->fh); #endif out: ps->ps_destroy_msg(pm); @@ -1061,7 +1174,7 @@ out: return error; } -/* ARGSUSED2 */ +/* ARGSUSED0 */ int perfuse_node_close(pu, opc, flags, pcr) struct puffs_usermount *pu; @@ -1069,95 +1182,34 @@ perfuse_node_close(pu, opc, flags, pcr) int flags; const struct puffs_cred *pcr; { - struct perfuse_state *ps; - perfuse_msg_t *pm; - int op; - uint64_t fh; - struct perfuse_node_data *pnd; - struct fuse_release_in *fri; struct puffs_node *pn; - int error; - - ps = puffs_getspecific(pu); - pn = (struct puffs_node *)opc; - pnd = PERFUSE_NODE_DATA(pn); + struct perfuse_node_data *pnd; - if (puffs_pn_getvap(pn)->va_type == VDIR) - op = FUSE_RELEASEDIR; - else - op = FUSE_RELEASE; + pn = (struct puffs_node *)opc; + pnd = PERFUSE_NODE_DATA(opc); if (!(pnd->pnd_flags & PND_OPEN)) return EBADF; - fh = perfuse_get_fh(opc); - /* - * Sync before close for files + * Make sure all operation are finished + * There can be an ongoing write, or queued operations + * XXX perhaps deadlock. Use requeue_request */ - if ((op == FUSE_RELEASE) && (pnd->pnd_flags & PND_DIRTY)) { -#ifdef PERFUSE_DEBUG - if (perfuse_diagflags & PDF_SYNC) - DPRINTF("%s: SYNC opc = %p, file = \"%s\"\n", - __func__, (void*)opc, (char *)PNPATH(pn)); -#endif - if ((error = perfuse_node_fsync(pu, opc, pcr, 0, 0, 0)) != 0) - return error; + while ((pnd->pnd_flags & PND_BUSY) || + !TAILQ_EMPTY(&pnd->pnd_pcq)) + puffs_cc_yield(puffs_cc_getcc(pu)); - pnd->pnd_flags &= ~PND_DIRTY; - -#ifdef PERFUSE_DEBUG - if (perfuse_diagflags & PDF_SYNC) - DPRINTF("%s: CLEAR opc = %p, file = \"%s\"\n", - __func__, (void*)opc, (char *)PNPATH((pn))); -#endif - } - - /* - * Destroy the filehandle before sending the - * request to the FUSE filesystem, otherwise - * we may get a second close() while we wait - * for the reply, and we would end up closing - * the same fh twice instead of closng both. + /* + * The NetBSD kernel will send sync and setattr(mtime, ctime) + * afer a close on a regular file. Some FUSE filesystem will + * assume theses operations are performed on open files. We + * therefore postpone the close operation at reclaim time. */ - perfuse_destroy_fh(pn, fh); + if (puffs_pn_getvap(pn)->va_type != VREG) + return node_close_common(pu, opc, flags); -#ifdef PERFUSE_DEBUG - if (perfuse_diagflags & PDF_FH) - DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n", - __func__, (void *)opc, pnd->pnd_ino, fh); -#endif - - /* - * release_flags may be set to FUSE_RELEASE_FLUSH - * to flush locks. lock_owner must be set in that case - */ - pm = ps->ps_new_msg(pu, opc, op, sizeof(*fri), NULL); - fri = GET_INPAYLOAD(ps, pm, fuse_release_in); - fri->fh = fh; - fri->flags = 0; - fri->release_flags = 0; - fri->lock_owner = PERFUSE_NODE_DATA(pn)->pnd_lock_owner; - fri->flags = (fri->lock_owner != 0) ? FUSE_RELEASE_FLUSH : 0; - -#ifdef PERFUSE_DEBUG - if (perfuse_diagflags & PDF_FH) - DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n", - __func__, (void *)opc, pnd->pnd_ino, fri->fh); -#endif - - if ((error = XCHG_MSG(ps, pu, pm, NO_PAYLOAD_REPLY_LEN)) != 0) - goto out; - -out: - if (error != 0) - DWARNX("%s: freed fh = 0x%"PRIx64" but filesystem " - "returned error = %d", - __func__, fh, error); - - ps->ps_destroy_msg(pm); - - return error; + return 0; } int @@ -1200,7 +1252,7 @@ perfuse_node_access(pu, opc, mode, pcr) fgi = GET_INPAYLOAD(ps, pm, fuse_getattr_in); fgi->getattr_flags = 0; fgi->dummy = 0; - fgi->fh = perfuse_get_fh(opc); + fgi->fh = perfuse_get_fh(opc, FREAD); #ifdef PERFUSE_DEBUG if (perfuse_diagflags & PDF_FH) @@ -1288,15 +1340,20 @@ perfuse_node_setattr(pu, opc, vap, pcr) perfuse_msg_t *pm; uint64_t fh; struct perfuse_state *ps; + struct perfuse_node_data *pnd; struct fuse_setattr_in *fsi; int error; + int open_self; struct vattr *old_va; + open_self = 0; + ps = puffs_getspecific(pu); + pnd = PERFUSE_NODE_DATA(opc); + /* * setattr requires --X on the parent directory */ - if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent, - pcr, PUFFS_VEXEC)) + if (no_access((puffs_cookie_t)pnd->pnd_parent, pcr, PUFFS_VEXEC)) return EACCES; old_va = puffs_pn_getvap((struct puffs_node *)opc); @@ -1331,21 +1388,38 @@ perfuse_node_setattr(pu, opc, vap, pcr) */ if ((vap->va_mode != (mode_t)PUFFS_VNOVAL) && (puffs_access_chmod(old_va->va_uid, old_va->va_gid, - old_va->va_type, vap->va_mode, pcr)) != 0) + old_va->va_type, vap->va_mode, pcr)) != 0) return EACCES; + /* + * setattr(mtime, ctime) require an open file, + * at least for glusterfs. + */ + if (((vap->va_atime.tv_sec != (time_t)PUFFS_VNOVAL) || + (vap->va_mtime.tv_sec != (time_t)PUFFS_VNOVAL)) && + !(pnd->pnd_flags & PND_WFH)) { + if ((error = perfuse_node_open(pu, opc, FWRITE, pcr)) != 0) + return error; + open_self = 1; + } + /* + * It seems troublesome to resize a file while + * a write is just beeing done. Wait for + * it to finish. + */ + if (vap->va_size != (u_quad_t)PUFFS_VNOVAL) + while (pnd->pnd_flags & PND_INWRITE) + requeue_request(pu, opc, PCQ_AFTERWRITE); - ps = puffs_getspecific(pu); pm = ps->ps_new_msg(pu, opc, FUSE_SETATTR, sizeof(*fsi), pcr); fsi = GET_INPAYLOAD(ps, pm, fuse_setattr_in); fsi->valid = 0; - if (PERFUSE_NODE_DATA(opc)->pnd_flags & PND_OPEN) { - fh = perfuse_get_fh(opc); + if (pnd->pnd_flags & PND_WFH) { + fh = perfuse_get_fh(opc, FWRITE); fsi->fh = fh; - if (fh != FUSE_UNKNOWN_FH) - fsi->valid |= FUSE_FATTR_FH; + fsi->valid |= FUSE_FATTR_FH; } if (vap->va_size != (u_quad_t)PUFFS_VNOVAL) { @@ -1380,8 +1454,8 @@ perfuse_node_setattr(pu, opc, vap, pcr) fsi->valid |= FUSE_FATTR_GID; } - if (PERFUSE_NODE_DATA(opc)->pnd_lock_owner != 0) { - fsi->lock_owner = PERFUSE_NODE_DATA(opc)->pnd_lock_owner; + if (pnd->pnd_lock_owner != 0) { + fsi->lock_owner = pnd->pnd_lock_owner; fsi->valid |= FUSE_FATTR_LOCKOWNER; } @@ -1392,6 +1466,9 @@ perfuse_node_setattr(pu, opc, vap, pcr) ps->ps_destroy_msg(pm); + if (open_self) + (void)perfuse_node_close(pu, opc, FWRITE, pcr); + return error; } @@ -1413,7 +1490,7 @@ perfuse_node_poll(pu, opc, events) */ pm = ps->ps_new_msg(pu, opc, FUSE_POLL, sizeof(*fpi), NULL); fpi = GET_INPAYLOAD(ps, pm, fuse_poll_in); - fpi->fh = perfuse_get_fh(opc); + fpi->fh = perfuse_get_fh(opc, FREAD); fpi->kh = 0; fpi->flags = 0; @@ -1479,7 +1556,7 @@ perfuse_node_fsync(pu, opc, pcr, flags, offlo, offhi) /* * Do not sync if there are no change to sync - * XXX remove that testif we implement mmap + * XXX remove that test if we implement mmap */ pnd = PERFUSE_NODE_DATA(opc); #ifdef PERFUSE_DEBUG @@ -1498,12 +1575,12 @@ perfuse_node_fsync(pu, opc, pcr, flags, offlo, offhi) * "FSYNC() ERR => -1 (Invalid argument)" */ if (!(pnd->pnd_flags & PND_OPEN)) { - if ((error = perfuse_node_open(pu, opc, FREAD, pcr)) != 0) + if ((error = perfuse_node_open(pu, opc, FWRITE, pcr)) != 0) goto out; open_self = 1; } - fh = perfuse_get_fh(opc); + fh = perfuse_get_fh(opc, FWRITE); /* * If fsync_flags is set, meta data should not be flushed. @@ -1543,8 +1620,8 @@ out: if (pm != NULL) ps->ps_destroy_msg(pm); - if (open_self) - (void)perfuse_node_close(pu, opc, 0, pcr); + if (open_self) + (void)node_close_common(pu, opc, FWRITE); return error; } @@ -1576,26 +1653,28 @@ perfuse_node_remove(pu, opc, targ, pcn) const struct puffs_cn *pcn; { struct perfuse_state *ps; - perfuse_msg_t *pm; struct puffs_node *pn; + struct perfuse_node_data *pnd; + perfuse_msg_t *pm; char *path; const char *name; size_t len; int error; + pnd = PERFUSE_NODE_DATA(opc); + /* * remove requires -WX on the parent directory * no right required on the object. */ - if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent, + if (no_access((puffs_cookie_t)pnd->pnd_parent, pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC)) return EACCES; - ps = puffs_getspecific(pu); - if (targ == NULL) DERRX(EX_SOFTWARE, "%s: targ is NULL", __func__); + ps = puffs_getspecific(pu); pn = (struct puffs_node *)targ; name = basename_r((char *)PNPATH(pn)); len = strlen(name) + 1; @@ -1610,9 +1689,6 @@ perfuse_node_remove(pu, opc, targ, pcn) if (puffs_inval_namecache_dir(pu, opc) != 0) DERR(EX_OSERR, "puffs_inval_namecache_dir failed"); - if (puffs_inval_pagecache_node(pu, (puffs_cookie_t)pn) != 0) - DERR(EX_OSERR, "puffs_inval_namecache_node failed"); - puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2); /* @@ -1767,6 +1843,7 @@ perfuse_node_rmdir(pu, opc, targ, pcn) const struct puffs_cn *pcn; { struct perfuse_state *ps; + struct perfuse_node_data *pnd; perfuse_msg_t *pm; struct puffs_node *pn; char *path; @@ -1774,11 +1851,13 @@ perfuse_node_rmdir(pu, opc, targ, pcn) size_t len; int error; + pnd = PERFUSE_NODE_DATA(opc); + /* * remove requires -WX on the parent directory * no right required on the object. */ - if (no_access((puffs_cookie_t)PERFUSE_NODE_DATA(opc)->pnd_parent, + if (no_access((puffs_cookie_t)pnd->pnd_parent, pcn->pcn_cred, PUFFS_VWRITE|PUFFS_VEXEC)) return EACCES; @@ -1797,9 +1876,6 @@ perfuse_node_rmdir(pu, opc, targ, pcn) if (puffs_inval_namecache_dir(pu, opc) != 0) DERR(EX_OSERR, "puffs_inval_namecache_dir failed"); - if (puffs_inval_pagecache_node(pu, (puffs_cookie_t)pn) != 0) - DERR(EX_OSERR, "puffs_inval_namecache_node failed"); - puffs_setback(puffs_cc_getcc(pu), PUFFS_SETBACK_NOREF_N2); out: @@ -1905,17 +1981,17 @@ perfuse_node_readdir(pu, opc, dent, readoff, * It seems NetBSD can call readdir without open first * libfuse will crash if it is done that way, hence open first. */ - if (!(PERFUSE_NODE_DATA(opc)->pnd_flags & PND_OPEN)) { + if (!(pnd->pnd_flags & PND_OPEN)) { if ((error = perfuse_node_open(pu, opc, FREAD, pcr)) != 0) goto out; open_self = 1; } - fh = perfuse_get_fh(opc); + fh = perfuse_get_fh(opc, FREAD); #ifdef PERFUSE_DEBUG if (perfuse_diagflags & PDF_FH) - DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n", + DPRINTF("%s: opc = %p, ino = %"PRId64", rfh = 0x%"PRIx64"\n", __func__, (void *)opc, PERFUSE_NODE_DATA(opc)->pnd_ino, fh); #endif @@ -2013,7 +2089,7 @@ out: * errors are ignored. */ if (open_self) - (void)perfuse_node_close(pu, opc, 0, pcr); + (void)perfuse_node_close(pu, opc, FWRITE, pcr); if (error == 0) error = readdir_buffered(ps, opc, dent, readoff, @@ -2091,12 +2167,6 @@ perfuse_node_reclaim(pu, opc) ps = puffs_getspecific(pu); pnd = PERFUSE_NODE_DATA(opc); - /* - * Make sure open files are properly closed when reclaimed. - */ - while (pnd->pnd_flags & PND_OPEN) - (void)perfuse_node_close(pu, opc, 0, NULL); - /* * Never forget the root. */ @@ -2121,15 +2191,17 @@ perfuse_node_reclaim(pu, opc) #ifdef PERFUSE_DEBUG if (perfuse_diagflags & PDF_RECLAIM) DPRINTF("%s (nodeid %"PRId64") is %sreclaimed, " - "has childcount %d, %sopen\n", + "has childcount %d %s%s%s, pending ops:%s%s%s%s\n", (char *)PNPATH(pn), pnd->pnd_ino, pnd->pnd_flags & PND_RECLAIMED ? "" : "not ", pnd->pnd_childcount, - pnd->pnd_flags & PND_OPEN ? "" : "not "); - - if (pnd->pnd_flags & PND_OPEN) - DWARNX("%s: (nodeid %"PRId64") %s is still open", - __func__, pnd->pnd_ino, (char *)PNPATH(pn)); + pnd->pnd_flags & PND_OPEN ? "open " : "not open", + pnd->pnd_flags & PND_RFH ? "r" : "", + pnd->pnd_flags & PND_WFH ? "w" : "", + pnd->pnd_flags & PND_INREADDIR ? " readdir" : "", + pnd->pnd_flags & PND_INREAD ? " read" : "", + pnd->pnd_flags & PND_INWRITE ? " write" : "", + pnd->pnd_flags & PND_BUSY ? "" : " none"); #endif if (!(pnd->pnd_flags & PND_RECLAIMED) || @@ -2137,12 +2209,38 @@ perfuse_node_reclaim(pu, opc) return 0; /* - * If the file is still open, close all file handles - * XXX no pcr arguement to send. + * Make sure all operation are finished + * There can be an ongoing write, or queued operations */ - while(pnd->pnd_flags & PND_OPEN) - (void)perfuse_node_close(pu, opc, 0, NULL); + while (pnd->pnd_flags & PND_INWRITE) { + requeue_request(pu, opc, PCQ_AFTERWRITE); + /* + * It may have been cancelled in the meantime + */ + if (!(pnd->pnd_flags & PND_RECLAIMED)) + return 0; + } + +#ifdef PERFUSE_DEBUG + if ((pnd->pnd_flags & PND_BUSY) || + !TAILQ_EMPTY(&pnd->pnd_pcq)) + DERRX(EX_SOFTWARE, "%s: opc = %p: ongoing operations", + __func__, (void *)opc); +#endif + + /* + * Close open files + */ + if (pnd->pnd_flags & PND_WFH) + (void)node_close_common(pu, opc, FREAD); + + if (pnd->pnd_flags & PND_RFH) + (void)node_close_common(pu, opc, FWRITE); + + /* + * And send the FORGET message + */ pm = ps->ps_new_msg(pu, (puffs_cookie_t)pn, FUSE_FORGET, sizeof(*ffi), NULL); ffi = GET_INPAYLOAD(ps, pm, fuse_forget_in); @@ -2223,7 +2321,7 @@ perfuse_node_advlock(pu, opc, id, op, fl, flags) pm = ps->ps_new_msg(pu, opc, fop, sizeof(*fli), NULL); fli = GET_INPAYLOAD(ps, pm, fuse_lk_in); - fli->fh = perfuse_get_fh(opc); + fli->fh = perfuse_get_fh(opc, FWRITE); fli->owner = fl->l_pid; fli->lk.start = fl->l_start; fli->lk.end = fl->l_start + fl->l_len; @@ -2279,6 +2377,7 @@ perfuse_node_read(pu, opc, buf, offset, resid, pcr, ioflag) int ioflag; { struct perfuse_state *ps; + struct perfuse_node_data *pnd; perfuse_msg_t *pm; struct fuse_read_in *fri; struct fuse_out_header *foh; @@ -2287,8 +2386,14 @@ perfuse_node_read(pu, opc, buf, offset, resid, pcr, ioflag) int error; ps = puffs_getspecific(pu); + pnd = PERFUSE_NODE_DATA(opc); pm = NULL; + if (puffs_pn_getvap((struct puffs_node *)opc)->va_type == VDIR) + return EBADF; + + pnd->pnd_flags |= PND_INREAD; + requested = *resid; if ((ps->ps_readahead + requested) > ps->ps_max_readahead) { if (perfuse_diagflags & PDF_REQUEUE) @@ -2307,19 +2412,18 @@ perfuse_node_read(pu, opc, buf, offset, resid, pcr, ioflag) */ pm = ps->ps_new_msg(pu, opc, FUSE_READ, sizeof(*fri), pcr); fri = GET_INPAYLOAD(ps, pm, fuse_read_in); - fri->fh = perfuse_get_fh(opc); + fri->fh = perfuse_get_fh(opc, FREAD); fri->offset = offset; fri->size = (uint32_t)MIN(*resid, PAGE_SIZE - sizeof(*foh)); fri->read_flags = 0; /* XXX Unused by libfuse? */ - fri->lock_owner = PERFUSE_NODE_DATA(opc)->pnd_lock_owner; + fri->lock_owner = pnd->pnd_lock_owner; fri->flags = 0; fri->flags |= (fri->lock_owner != 0) ? FUSE_READ_LOCKOWNER : 0; #ifdef PERFUSE_DEBUG if (perfuse_diagflags & PDF_FH) DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n", - __func__, (void *)opc, - PERFUSE_NODE_DATA(opc)->pnd_ino, fri->fh); + __func__, (void *)opc, pnd->pnd_ino, fri->fh); #endif error = XCHG_MSG(ps, pu, pm, UNSPEC_REPLY_LEN); @@ -2351,6 +2455,8 @@ out: ps->ps_readahead -= requested; dequeue_requests(ps, opc, PCQ_READ, 1); + pnd->pnd_flags &= ~PND_INREAD; + return error; } @@ -2365,6 +2471,7 @@ perfuse_node_write(pu, opc, buf, offset, resid, pcr, ioflag) int ioflag; { struct perfuse_state *ps; + struct perfuse_node_data *pnd; perfuse_msg_t *pm; struct fuse_write_in *fwi; struct fuse_write_out *fwo; @@ -2375,9 +2482,16 @@ perfuse_node_write(pu, opc, buf, offset, resid, pcr, ioflag) int error; ps = puffs_getspecific(pu); + pnd = PERFUSE_NODE_DATA(opc); pm = NULL; written = 0; + if (puffs_pn_getvap((struct puffs_node *)opc)->va_type == VDIR) + return EBADF; + +DPRINTF("%s ENTER\n", __func__); + pnd->pnd_flags |= PND_INWRITE; + requested = *resid; if ((ps->ps_write + requested) > ps->ps_max_write) { if (perfuse_diagflags & PDF_REQUEUE) @@ -2403,11 +2517,11 @@ perfuse_node_write(pu, opc, buf, offset, resid, pcr, ioflag) */ pm = ps->ps_new_msg(pu, opc, FUSE_WRITE, payload_len, pcr); fwi = GET_INPAYLOAD(ps, pm, fuse_write_in); - fwi->fh = perfuse_get_fh(opc); + fwi->fh = perfuse_get_fh(opc, FWRITE); fwi->offset = offset; fwi->size = (uint32_t)data_len; fwi->write_flags = (fwi->size % PAGE_SIZE) ? 0 : 1; - fwi->lock_owner = PERFUSE_NODE_DATA(opc)->pnd_lock_owner; + fwi->lock_owner = pnd->pnd_lock_owner; fwi->flags = 0; fwi->flags |= (fwi->lock_owner != 0) ? FUSE_WRITE_LOCKOWNER : 0; fwi->flags |= (ioflag & IO_DIRECT) ? 0 : FUSE_WRITE_CACHE; @@ -2416,8 +2530,7 @@ perfuse_node_write(pu, opc, buf, offset, resid, pcr, ioflag) #ifdef PERFUSE_DEBUG if (perfuse_diagflags & PDF_FH) DPRINTF("%s: opc = %p, ino = %"PRId64", fh = 0x%"PRIx64"\n", - __func__, (void *)opc, - PERFUSE_NODE_DATA(opc)->pnd_ino, fwi->fh); + __func__, (void *)opc, pnd->pnd_ino, fwi->fh); #endif if ((error = XCHG_MSG(ps, pu, pm, sizeof(*fwo))) != 0) goto out; @@ -2447,7 +2560,7 @@ perfuse_node_write(pu, opc, buf, offset, resid, pcr, ioflag) /* * Remember to sync the file */ - PERFUSE_NODE_DATA(opc)->pnd_flags |= PND_DIRTY; + pnd->pnd_flags |= PND_DIRTY; #ifdef PERFUSE_DEBUG if (perfuse_diagflags & PDF_SYNC) @@ -2462,6 +2575,13 @@ out: ps->ps_write -= requested; dequeue_requests(ps, opc, PCQ_WRITE, 1); + pnd->pnd_flags &= ~PND_INWRITE; + + /* + * Dequeue operation that were waiting for write to complete + */ + dequeue_requests(ps, opc, PCQ_AFTERWRITE, DEQUEUE_ALL); + return error; } diff --git a/lib/libperfuse/perfuse_priv.h b/lib/libperfuse/perfuse_priv.h index ea43eb5c7da7..d2d9530a8bf4 100644 --- a/lib/libperfuse/perfuse_priv.h +++ b/lib/libperfuse/perfuse_priv.h @@ -1,4 +1,4 @@ -/* $NetBSD: perfuse_priv.h,v 1.4 2010/09/01 14:57:24 manu Exp $ */ +/* $NetBSD: perfuse_priv.h,v 1.5 2010/09/03 07:15:18 manu Exp $ */ /*- * Copyright (c) 2010 Emmanuel Dreyfus. All rights reserved. @@ -71,13 +71,7 @@ struct perfuse_state { }; -struct perfuse_file_handle { - uint64_t pfh_fh; - TAILQ_ENTRY(perfuse_file_handle) pfh_entries; -}; - - -enum perfuse_qtype { PCQ_READDIR, PCQ_READ, PCQ_WRITE }; +enum perfuse_qtype { PCQ_READDIR, PCQ_READ, PCQ_WRITE, PCQ_AFTERWRITE }; struct perfuse_cc_queue { enum perfuse_qtype pcq_type; @@ -87,7 +81,8 @@ struct perfuse_cc_queue { struct perfuse_node_data { - TAILQ_HEAD(,perfuse_file_handle) pnd_fh; + uint64_t pnd_rfh; + uint64_t pnd_wfh; uint64_t pnd_ino; /* inode */ uint64_t pnd_nlookup; /* vnode refcount */ uint64_t pnd_offset; /* seek state */ @@ -98,10 +93,16 @@ struct perfuse_node_data { size_t pnd_all_fd_len; TAILQ_HEAD(,perfuse_cc_queue) pnd_pcq; /* queued requests */ int pnd_flags; -#define PND_RECLAIMED 0x1 /* reclaim pending */ -#define PND_INREADDIR 0x2 /* readdir in progress */ -#define PND_OPEN 0x4 /* At least one fh is allocated */ -#define PND_DIRTY 0x8 /* There is some data to sync */ +#define PND_RECLAIMED 0x01 /* reclaim pending */ +#define PND_INREADDIR 0x02 /* readdir in progress */ +#define PND_DIRTY 0x04 /* There is some data to sync */ +#define PND_RFH 0x08 /* Read FH allocated */ +#define PND_WFH 0x10 /* Write FH allocated */ +#define PND_INREAD 0x20 /* read in progress */ +#define PND_INWRITE 0x40 /* write in progress */ + +#define PND_OPEN (PND_RFH|PND_WFH) /* File is open */ +#define PND_BUSY (PND_INREADDIR|PND_INREAD|PND_INWRITE) puffs_cookie_t pnd_parent; int pnd_childcount; }; @@ -131,9 +132,9 @@ __BEGIN_DECLS struct puffs_node *perfuse_new_pn(struct puffs_usermount *, struct puffs_node *); void perfuse_destroy_pn(struct puffs_node *); -void perfuse_new_fh(puffs_cookie_t, uint64_t); +void perfuse_new_fh(puffs_cookie_t, uint64_t, int); void perfuse_destroy_fh(puffs_cookie_t, uint64_t); -uint64_t perfuse_get_fh(puffs_cookie_t); +uint64_t perfuse_get_fh(puffs_cookie_t, int); uint64_t perfuse_next_unique(struct puffs_usermount *); char *perfuse_fs_mount(int, ssize_t); diff --git a/lib/libperfuse/subr.c b/lib/libperfuse/subr.c index 48082f330e77..6638a681cf4a 100644 --- a/lib/libperfuse/subr.c +++ b/lib/libperfuse/subr.c @@ -1,4 +1,4 @@ -/* $NetBSD: subr.c,v 1.3 2010/09/01 14:57:24 manu Exp $ */ +/* $NetBSD: subr.c,v 1.4 2010/09/03 07:15:18 manu Exp $ */ /*- * Copyright (c) 2010 Emmanuel Dreyfus. All rights reserved. @@ -51,7 +51,8 @@ perfuse_new_pn(pu, parent) DERR(EX_SOFTWARE, "puffs_pn_new failed"); (void)memset(pnd, 0, sizeof(*pnd)); - TAILQ_INIT(&pnd->pnd_fh); + pnd->pnd_rfh = FUSE_UNKNOWN_FH; + pnd->pnd_wfh = FUSE_UNKNOWN_FH; pnd->pnd_ino = PERFUSE_UNKNOWN_INO; pnd->pnd_nlookup = 1; pnd->pnd_parent = parent; @@ -79,8 +80,8 @@ perfuse_destroy_pn(pn) if (pnd->pnd_all_fd != NULL) free(pnd->pnd_all_fd); #ifdef PERFUSE_DEBUG - if (!TAILQ_EMPTY(&pnd->pnd_fh)) - DERRX(EX_SOFTWARE, "%s: non empty pnd_fh", __func__); + if (pnd->pnd_flags & PND_OPEN) + DERRX(EX_SOFTWARE, "%s: file open", __func__); if (!TAILQ_EMPTY(&pnd->pnd_pcq)) DERRX(EX_SOFTWARE, "%s: non empty pnd_pcq", __func__); @@ -96,25 +97,30 @@ perfuse_destroy_pn(pn) void -perfuse_new_fh(opc, fh) +perfuse_new_fh(opc, fh, mode) puffs_cookie_t opc; uint64_t fh; + int mode; { struct perfuse_node_data *pnd; - struct perfuse_file_handle *pfh; - - if (fh == FUSE_UNKNOWN_FH) - return; pnd = PERFUSE_NODE_DATA(opc); - pnd->pnd_flags |= PND_OPEN; - if ((pfh = malloc(sizeof(*pfh))) == NULL) - DERR(EX_OSERR, "malloc failed"); + if (mode & FWRITE) { + if (pnd->pnd_flags & PND_WFH) + DERRX(EX_SOFTWARE, "%s: opc = %p, write fh already set", + __func__, (void *)opc); + pnd->pnd_wfh = fh; + pnd->pnd_flags |= PND_WFH; + } - pfh->pfh_fh = fh; - - TAILQ_INSERT_TAIL(&pnd->pnd_fh, pfh, pfh_entries); + if (mode & FREAD) { + if (pnd->pnd_flags & PND_RFH) + DERRX(EX_SOFTWARE, "%s: opc = %p, read fh already set", + __func__, (void *)opc); + pnd->pnd_rfh = fh; + pnd->pnd_flags |= PND_RFH; + } return; } @@ -125,42 +131,51 @@ perfuse_destroy_fh(opc, fh) uint64_t fh; { struct perfuse_node_data *pnd; - struct perfuse_file_handle *pfh; pnd = PERFUSE_NODE_DATA(opc); - TAILQ_FOREACH(pfh, &pnd->pnd_fh, pfh_entries) { - if (pfh->pfh_fh == fh) { - TAILQ_REMOVE(&pnd->pnd_fh, pfh, pfh_entries); - free(pfh); - break; - } + if (fh == pnd->pnd_rfh) { + if (!(pnd->pnd_flags & PND_RFH) && (fh != FUSE_UNKNOWN_FH)) + DERRX(EX_SOFTWARE, + "%s: opc = %p, unset rfh = %"PRIx64"", + __func__, (void *)opc, fh); + pnd->pnd_rfh = FUSE_UNKNOWN_FH; + pnd->pnd_flags &= ~PND_RFH; } - if (TAILQ_EMPTY(&pnd->pnd_fh)) - pnd->pnd_flags &= ~PND_OPEN; + if (fh == pnd->pnd_wfh) { + if (!(pnd->pnd_flags & PND_WFH) && (fh != FUSE_UNKNOWN_FH)) + DERRX(EX_SOFTWARE, + "%s: opc = %p, unset wfh = %"PRIx64"", + __func__, (void *)opc, fh); + pnd->pnd_wfh = FUSE_UNKNOWN_FH; + pnd->pnd_flags &= ~PND_WFH; + } - if (pfh == NULL) - DERRX(EX_SOFTWARE, - "%s: unexistant fh = %"PRId64" (double close?)", - __func__, fh); - return; } uint64_t -perfuse_get_fh(opc) +perfuse_get_fh(opc, mode) puffs_cookie_t opc; + int mode; { struct perfuse_node_data *pnd; - struct perfuse_file_handle *pfh; - uint64_t fh = FUSE_UNKNOWN_FH; pnd = PERFUSE_NODE_DATA(opc); - if ((pfh = TAILQ_FIRST(&pnd->pnd_fh)) != NULL) - fh = pfh->pfh_fh;; + if (mode & FWRITE) + if (pnd->pnd_flags & PND_WFH) + return pnd->pnd_wfh; - return fh; + if (mode & FREAD) { + if (pnd->pnd_flags & PND_RFH) + return pnd->pnd_rfh; + + if (pnd->pnd_flags & PND_WFH) + return pnd->pnd_wfh; + } + + return FUSE_UNKNOWN_FH; }