/*ARGSUSED*/ int puffs_null_node_write(struct puffs_usermount *pu, puffs_cookie_t opc, uint8_t *buf, off_t offset, size_t *buflen, const struct puffs_cred *pcred, int ioflag) { struct puffs_node *pn = opc; ssize_t n; off_t off; int fd, rv; rv = 0; fd = writeableopen(PNPATH(pn)); if (fd == -1) return errno; off = lseek(fd, offset, SEEK_SET); if (off == -1) { rv = errno; goto out; } n = write(fd, buf, *buflen); if (n == -1) rv = errno; else *buflen -= n; out: close(fd); return rv; }
/*ARGSUSED*/ int puffs_null_fs_statvfs(struct puffs_usermount *pu, struct statfs *svfsb) { if (statfs(PNPATH(puffs_getroot(pu)), svfsb) == -1) return errno; return 0; }
/*ARGSUSED*/ int puffs_null_node_readdir(struct puffs_usermount *pu, puffs_cookie_t opc, struct dirent *de, off_t *off, size_t *reslen, const struct puffs_cred *pcred, int *eofflag, off_t *cookies, size_t *ncookies) { struct puffs_node *pn = opc; struct dirent entry, *result; DIR *dp; off_t i; int rv; *ncookies = 0; dp = opendir(PNPATH(pn)); if (dp == NULL) return errno; rv = 0; i = *off; /* * XXX: need to do trickery here, telldir/seekdir would be nice, but * then we'd need to keep state, which I'm too lazy to keep */ while (i--) { rv = readdir_r(dp, &entry, &result); if (rv || !result) goto out; } for (;;) { rv = readdir_r(dp, &entry, &result); if (rv != 0) goto out; if (!result) { *eofflag = 1; goto out; } if (_DIRENT_SIZE(result) > *reslen) goto out; *de = *result; *reslen -= _DIRENT_SIZE(result); de = _DIRENT_NEXT(de); (*off)++; PUFFS_STORE_DCOOKIE(cookies, ncookies, *off); } out: closedir(dp); return 0; }
/*ARGSUSED*/ int puffs_null_node_link(struct puffs_usermount *pu, puffs_cookie_t opc, puffs_cookie_t targ, const struct puffs_cn *pcn) { struct puffs_node *pn_targ = targ; if (link(PNPATH(pn_targ), PCNPATH(pcn)) == -1) return errno; return 0; }
/*ARGSUSED*/ int puffs_null_node_rmdir(struct puffs_usermount *pu, puffs_cookie_t opc, puffs_cookie_t targ, const struct puffs_cn *pcn) { struct puffs_node *pn_targ = targ; if (rmdir(PNPATH(pn_targ)) == -1) return errno; puffs_pn_remove(pn_targ); return 0; }
int sysctlfs_fs_nodetofh(struct puffs_usermount *pu, void *cookie, void *fid, size_t *fidsize) { struct puffs_node *pn = cookie; struct sfsfid *sfid; sfid = fid; sfid->len = PNPLEN(pn); memcpy(&sfid->path, PNPATH(pn), sfid->len * sizeof(int)); return 0; }
/*ARGSUSED*/ int puffs_null_node_getattr(struct puffs_usermount *pu, puffs_cookie_t opc, struct vattr *va, const struct puffs_cred *pcred) { struct puffs_node *pn = opc; struct stat sb; if (lstat(PNPATH(pn), &sb) == -1) return errno; puffs_stat2vattr(va, &sb); return 0; }
/*ARGSUSED*/ int puffs_null_node_readlink(struct puffs_usermount *pu, puffs_cookie_t opc, const struct puffs_cred *pcred, char *linkname, size_t *linklen) { struct puffs_node *pn = opc; ssize_t rv; rv = readlink(PNPATH(pn), linkname, *linklen); if (rv == -1) return errno; *linklen = rv; return 0; }
/*ARGSUSED*/ int puffs_null_node_setattr(struct puffs_usermount *pu, puffs_cookie_t opc, const struct vattr *va, const struct puffs_cred *pcred) { struct puffs_node *pn = opc; int rv; rv = processvattr(PNPATH(pn), va, pn->pn_va.va_type == VREG); if (rv) return rv; puffs_setvattr(&pn->pn_va, va); return 0; }
int getnodeattr(struct puffs_usermount *pu, struct puffs_node *pn, const char *path) { struct psshfs_ctx *pctx = puffs_getspecific(pu); struct psshfs_node *psn = pn->pn_data; struct vattr va; int rv; if (!psn->attrread || REFRESHTIMEOUT(pctx, time(NULL)-psn->attrread)) { rv = getpathattr(pu, path ? path : PNPATH(pn), &va); if (rv) return rv; setpnva(pu, pn, &va); } return 0; }
/*ARGSUSED*/ int puffs_null_node_fsync(struct puffs_usermount *pu, puffs_cookie_t opc, const struct puffs_cred *pcred, int how, off_t offlo, off_t offhi) { struct puffs_node *pn = opc; int fd, rv; int fflags; rv = 0; fd = writeableopen(PNPATH(pn)); if (fd == -1) return errno; if (fsync(fd) == -1) rv = errno; close(fd); return rv; }
/* * called from nodewalk, checks for exact match */ void * puffs_path_walkcmp(struct puffs_usermount *pu, struct puffs_node *pn, void *arg) { struct puffs_pathobj *po = arg; struct puffs_pathobj po2; if (po->po_len != PNPLEN(pn)) return NULL; /* * If hashing and the hash doesn't match, we know this is * definitely not a match. Otherwise check for collisions. */ if (pu->pu_flags & PUFFS_FLAG_HASHPATH) if (pn->pn_po.po_hash != po->po_hash) return NULL; po2.po_path = PNPATH(pn); po2.po_len = PNPLEN(pn); if (pu->pu_pathcmp(pu, po, &po2, PNPLEN(pn), 0) == 0) return pn; return NULL; }
/*ARGSUSED*/ int puffs_null_fs_nodetofh(struct puffs_usermount *pu, puffs_cookie_t opc, void *fid, size_t *fidsize) { struct puffs_node *pn = opc; fhandle_t fh; int rv; if (*fidsize != sizeof(struct fid)) return EINVAL; rv = 0; if (getfh(PNPATH(pn), &fh) == -1) rv = errno; if (rv == 0) { *(struct fid *)fid = fh.fh_fid; pn->pn_data = malloc(*fidsize); if (pn->pn_data == NULL) abort(); /* lazy */ memcpy(pn->pn_data, fid, *fidsize); } return rv; }
int sysctlfs_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf, off_t offset, size_t *resid, const struct puffs_cred *cred, int ioflag) { struct puffs_node *pn = opc; struct sfsnode *sfs = pn->pn_data; long long ll; int i, rv; bool b; /* * I picked the wrong day to ... um, the wrong place to return errors */ /* easy to support, but just unavailable now */ if (rflag) return EOPNOTSUPP; if (puffs_cred_isjuggernaut(cred) == 0) return EACCES; if (ISADIR(sfs)) return EISDIR; if (offset != 0) return EINVAL; if (ioflag & PUFFS_IO_APPEND) return EINVAL; switch (SYSCTL_TYPE(sfs->sysctl_flags)) { case CTLTYPE_BOOL: if (strcasestr((const char *)buf, "true")) b = true; else if (strcasestr((const char *)buf, "false")) b = false; else return EINVAL; rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL, &b, sizeof(b)); break; case CTLTYPE_INT: if (sscanf((const char *)buf, "%d", &i) != 1) return EINVAL; rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL, &i, sizeof(int)); break; case CTLTYPE_QUAD: if (sscanf((const char *)buf, "%lld", &ll) != 1) return EINVAL; rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL, &ll, sizeof(long long)); break; case CTLTYPE_STRING: rv = sysctl(PNPATH(pn), PNPLEN(pn), NULL, NULL, buf, *resid); break; default: rv = EINVAL; break; } if (rv) return rv; *resid = 0; return 0; }
int sysctlfs_node_readdir(struct puffs_usermount *pu, void *opc, struct dirent *dent, off_t *readoff, size_t *reslen, const struct puffs_cred *pcr, int *eofflag, off_t *cookies, size_t *ncookies) { struct sysctlnode sn[SFS_NODEPERDIR]; struct sysctlnode qnode; struct puffs_node *pn_dir = opc; struct puffs_node *pn_res; struct puffs_pathobj po; struct sfsnode *sfs_dir = pn_dir->pn_data, *sfs_ent; SfsName *sname; size_t sl, i; enum vtype vt; ino_t id; *ncookies = 0; again: if (*readoff == DENT_DOT || *readoff == DENT_DOTDOT) { puffs_gendotdent(&dent, sfs_dir->myid, *readoff, reslen); (*readoff)++; PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff); goto again; } memset(&qnode, 0, sizeof(qnode)); sl = SFS_NODEPERDIR * sizeof(struct sysctlnode); qnode.sysctl_flags = SYSCTL_VERSION; sname = PNPATH(pn_dir); (*sname)[PNPLEN(pn_dir)] = CTL_QUERY; if (sysctl(*sname, PNPLEN(pn_dir) + 1, sn, &sl, &qnode, sizeof(qnode)) == -1) return ENOENT; po.po_path = sname; po.po_len = PNPLEN(pn_dir)+1; for (i = DENT_ADJ(*readoff); i < sl / sizeof(struct sysctlnode); i++) { if (SYSCTL_TYPE(sn[i].sysctl_flags) == CTLTYPE_NODE) vt = VDIR; else vt = VREG; /* * check if the node exists. if so, give it the real * inode number. otherwise just fake it. */ (*sname)[PNPLEN(pn_dir)] = sn[i].sysctl_num; pn_res = puffs_pn_nodewalk(pu, puffs_path_walkcmp, &po); if (pn_res) { sfs_ent = pn_res->pn_data; id = sfs_ent->myid; } else { id = nextid++; } if (!puffs_nextdent(&dent, sn[i].sysctl_name, id, puffs_vtype2dt(vt), reslen)) return 0; (*readoff)++; PUFFS_STORE_DCOOKIE(cookies, ncookies, *readoff); } *eofflag = 1; return 0; }
int sftp_readdir(struct puffs_usermount *pu, struct psshfs_ctx *pctx, struct puffs_node *pn) { struct puffs_cc *pcc = puffs_cc_getcc(pu); struct psshfs_node *psn = pn->pn_data; struct psshfs_dir *olddir, *testd; struct puffs_framebuf *pb; uint32_t reqid = NEXTREQ(pctx); uint32_t count, dhandlen; int tmpval; char *dhand = NULL; size_t nent; char *longname = NULL; size_t idx; int rv; assert(pn->pn_va.va_type == VDIR); idx = 0; olddir = psn->dir; nent = psn->dentnext; if (psn->dir && psn->dentread && !REFRESHTIMEOUT(pctx, time(NULL) - psn->dentread)) return 0; if (psn->dentread) { if ((rv = puffs_inval_namecache_dir(pu, pn))) warn("readdir: dcache inval fail %p", pn); } pb = psbuf_makeout(); psbuf_req_str(pb, SSH_FXP_OPENDIR, reqid, PNPATH(pn)); if (puffs_framev_enqueue_cc(pcc, pctx->sshfd, pb, 0) == -1) { rv = errno; goto wayout; } rv = psbuf_expect_handle(pb, &dhand, &dhandlen); if (rv) goto wayout; /* * Well, the following is O(n^2), so feel free to improve if it * gets too taxing on your system. */ /* * note: for the "getattr in batch" to work, this must be before * the attribute-getting. Otherwise times for first entries in * large directories might expire before the directory itself and * result in one-by-one attribute fetching. */ psn->dentread = time(NULL); psn->dentnext = 0; psn->denttot = 0; psn->dir = NULL; for (;;) { reqid = NEXTREQ(pctx); psbuf_recycleout(pb); psbuf_req_data(pb, SSH_FXP_READDIR, reqid, dhand, dhandlen); GETRESPONSE(pb, pctx->sshfd); /* check for EOF */ if (psbuf_get_type(pb) == SSH_FXP_STATUS) { rv = psbuf_expect_status(pb); goto out; } rv = psbuf_expect_name(pb, &count); if (rv) goto out; for (; count--; idx++) { if (idx == psn->denttot) allocdirs(psn); if ((rv = psbuf_get_str(pb, &psn->dir[idx].entryname, NULL))) goto out; if ((rv = psbuf_get_str(pb, &longname, NULL)) != 0) goto out; if ((rv = psbuf_get_vattr(pb, &psn->dir[idx].va)) != 0) goto out; if (sscanf(longname, "%*s%d", &tmpval) != 1) { rv = EPROTO; goto out; } psn->dir[idx].va.va_nlink = tmpval; free(longname); longname = NULL; /* * In case of DOT, copy the attributes (mostly * because we want the link count for the root dir). */ if (strcmp(psn->dir[idx].entryname, ".") == 0) { setpnva(pu, pn, &psn->dir[idx].va); } /* * Check if we already have a psshfs_dir for the * name we are processing. If so, use the old one. * If not, create a new one */ testd = lookup(olddir, nent, psn->dir[idx].entryname); if (testd) { psn->dir[idx].entry = testd->entry; /* * Has entry. Update attributes to what * we just got from the server. */ if (testd->entry) { setpnva(pu, testd->entry, &psn->dir[idx].va); psn->dir[idx].va.va_fileid = testd->entry->pn_va.va_fileid; /* * No entry. This can happen in two cases: * 1) the file was created "behind our back" * on the server * 2) we do two readdirs before we instantiate * the node (or run with -t 0). * * Cache attributes from the server in * case we want to instantiate this node * soon. Also preserve the old inode number * which was given when the dirent was created. */ } else { psn->dir[idx].va.va_fileid = testd->va.va_fileid; testd->va = psn->dir[idx].va; } /* No previous entry? Initialize this one. */ } else { psn->dir[idx].entry = NULL; psn->dir[idx].va.va_fileid = pctx->nextino++; } psn->dir[idx].attrread = psn->dentread; psn->dir[idx].valid = 1; } } out: /* XXX: rv */ psn->dentnext = idx; freedircache(olddir, nent); reqid = NEXTREQ(pctx); psbuf_recycleout(pb); psbuf_req_data(pb, SSH_FXP_CLOSE, reqid, dhand, dhandlen); puffs_framev_enqueue_justsend(pu, pctx->sshfd, pb, 1, 0); free(dhand); free(longname); return rv; wayout: free(dhand); PSSHFSRETURN(rv); }