/*===========================================================================* * fs_rename * *===========================================================================*/ PUBLIC int fs_rename() { /* Perform the rename(name1, name2) system call. */ struct puffs_node *old_dirp, *old_ip; /* ptrs to old dir, file pnodes */ struct puffs_node *new_dirp, *new_ip; /* ptrs to new dir, file pnodes */ struct puffs_kcn pkcnp_src; PUFFS_MAKECRED(pcr_src, &global_kcred); struct puffs_cn pcn_src = {&pkcnp_src, (struct puffs_cred *) __UNCONST(pcr_src), {0,0,0}}; struct puffs_kcn pkcnp_dest; PUFFS_MAKECRED(pcr_dest, &global_kcred); struct puffs_cn pcn_targ = {&pkcnp_dest, (struct puffs_cred *) __UNCONST(pcr_dest), {0,0,0}}; int r = OK; /* error flag; initially no error */ int odir, ndir; /* TRUE iff {old|new} file is dir */ int same_pdir; /* TRUE iff parent dirs are the same */ phys_bytes len; time_t cur_time; if (global_pu->pu_ops.puffs_node_rename == NULL) return(EINVAL); /* Copy the last component of the old name */ len = fs_m_in.REQ_REN_LEN_OLD; /* including trailing '\0' */ if (len > NAME_MAX + 1) return(ENAMETOOLONG); r = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_REN_GRANT_OLD, (vir_bytes) 0, (vir_bytes) pcn_src.pcn_name, (size_t) len, D); if (r != OK) return(r); NUL(pcn_src.pcn_name, len, sizeof(pcn_src.pcn_name)); pcn_src.pcn_namelen = len - 1; /* Copy the last component of the new name */ len = fs_m_in.REQ_REN_LEN_NEW; /* including trailing '\0' */ if (len > NAME_MAX + 1) return(ENAMETOOLONG); r = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) fs_m_in.REQ_REN_GRANT_NEW, (vir_bytes) 0, (vir_bytes) pcn_targ.pcn_name, (size_t) len, D); if (r != OK) return(r); NUL(pcn_targ.pcn_name, len, sizeof(pcn_targ.pcn_name)); pcn_targ.pcn_namelen = len - 1; /* Get old dir pnode */ if ((old_dirp = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_REN_OLD_DIR)) == NULL) return(ENOENT); old_ip = advance(old_dirp, pcn_src.pcn_name, IGN_PERM); if (!old_ip) { return(ENOENT); } r = err_code; if (r == EENTERMOUNT || r == ELEAVEMOUNT) { old_ip = NULL; if (r == EENTERMOUNT) r = EXDEV; /* should this fail at all? */ else if (r == ELEAVEMOUNT) r = EINVAL; /* rename on dot-dot */ } /* Get new dir pnode */ if ((new_dirp = puffs_pn_nodewalk(global_pu, 0, &fs_m_in.REQ_REN_NEW_DIR)) == NULL) { r = ENOENT; } else { if (new_dirp->pn_va.va_nlink == NO_LINK) { /* Dir does not actually exist */ return(ENOENT); } } /* not required to exist */ new_ip = advance(new_dirp, pcn_targ.pcn_name, IGN_PERM); /* However, if the check failed because the file does exist, don't continue. * Note that ELEAVEMOUNT is covered by the dot-dot check later. */ if (err_code == EENTERMOUNT) { new_ip = NULL; r = EBUSY; } if (old_ip != NULL) { /* TRUE iff dir */ odir = ((old_ip->pn_va.va_mode & I_TYPE) == I_DIRECTORY); } else { odir = FALSE; } if (r != OK) return(r); /* Check for a variety of possible errors. */ same_pdir = (old_dirp == new_dirp); /* The old or new name must not be . or .. */ if (strcmp(pcn_src.pcn_name, ".") == 0 || strcmp(pcn_src.pcn_name, "..") == 0 || strcmp(pcn_targ.pcn_name, ".") == 0 || strcmp(pcn_targ.pcn_name, "..") == 0) { r = EINVAL; } /* Some tests apply only if the new path exists. */ if (new_ip == NULL) { if (odir && (new_dirp->pn_va.va_nlink >= SHRT_MAX || new_dirp->pn_va.va_nlink >= LINK_MAX) && !same_pdir && r == OK) { r = EMLINK; } } else { if (old_ip == new_ip) r = SAME; /* old=new */ /* dir ? */ ndir = ((new_ip->pn_va.va_mode & I_TYPE) == I_DIRECTORY); if (odir == TRUE && ndir == FALSE) r = ENOTDIR; if (odir == FALSE && ndir == TRUE) r = EISDIR; } if (r == SAME) { r = OK; goto rename_out; } if (r != OK) return(r); /* If a process has another root directory than the system root, we might * "accidently" be moving it's working directory to a place where it's * root directory isn't a super directory of it anymore. This can make * the function chroot useless. If chroot will be used often we should * probably check for it here. */ /* The rename will probably work. Only two things can go wrong now: * 1. being unable to remove the new file. (when new file already exists) * 2. being unable to make the new directory entry. (new file doesn't exists) * [directory has to grow by one block and cannot because the disk * is completely full]. * 3. Something (doubtfully) else depending on the FS. */ if (buildpath) { pcn_src.pcn_po_full = old_ip->pn_po; if (puffs_path_pcnbuild(global_pu, &pcn_targ, new_dirp) != 0) return(EINVAL); } r = global_pu->pu_ops.puffs_node_rename(global_pu, old_dirp, old_ip, &pcn_src, new_dirp, new_ip, &pcn_targ); if (r > 0) r = -r; if (buildpath) { if (r) { global_pu->pu_pathfree(global_pu, &pcn_targ.pcn_po_full); } else { struct puffs_pathinfo pi; struct puffs_pathobj po_old; /* handle this node */ po_old = old_ip->pn_po; old_ip->pn_po = pcn_targ.pcn_po_full; if (old_ip->pn_va.va_type != VDIR) { global_pu->pu_pathfree(global_pu, &po_old); return(OK); } /* handle all child nodes for DIRs */ pi.pi_old = &pcn_src.pcn_po_full; pi.pi_new = &pcn_targ.pcn_po_full; PU_LOCK(); if (puffs_pn_nodewalk(global_pu, puffs_path_prefixadj, &pi) != NULL) { /* Actually nomem */ return(EINVAL); } PU_UNLOCK(); global_pu->pu_pathfree(global_pu, &po_old); } } rename_out: cur_time = clock_time(); update_times(old_dirp, MTIME | CTIME, cur_time); update_times(new_dirp, MTIME | CTIME, cur_time); /* XXX see release_node comment in fs_unlink */ if (new_ip && new_ip->pn_count == 0) { release_node(global_pu, new_ip); } return(r); }
static void dispatch(struct puffs_cc *pcc) { struct puffs_usermount *pu = pcc->pcc_pu; struct puffs_ops *pops = &pu->pu_ops; struct puffs_req *preq = puffs__framebuf_getdataptr(pcc->pcc_pb); void *auxbuf; /* help with typecasting */ puffs_cookie_t opcookie; int error = 0, buildpath, pncookie; /* XXX: smaller hammer, please */ if ((PUFFSOP_OPCLASS(preq->preq_opclass == PUFFSOP_VFS && preq->preq_optype == PUFFS_VFS_VPTOFH)) || (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN && (preq->preq_optype == PUFFS_VN_READDIR || preq->preq_optype == PUFFS_VN_READ))) { if (puffs_framebuf_reserve_space(pcc->pcc_pb, PUFFS_MSG_MAXSIZE) == -1) error = errno; preq = puffs__framebuf_getdataptr(pcc->pcc_pb); } auxbuf = preq; opcookie = preq->preq_cookie; assert((pcc->pcc_flags & PCC_DONE) == 0); buildpath = pu->pu_flags & PUFFS_FLAG_BUILDPATH; pncookie = pu->pu_flags & PUFFS_FLAG_PNCOOKIE; assert(!buildpath || pncookie); preq->preq_setbacks = 0; if (pu->pu_flags & PUFFS_FLAG_OPDUMP) puffsdump_req(preq); puffs__cc_setcaller(pcc, preq->preq_pid, preq->preq_lid); /* pre-operation */ if (pu->pu_oppre) pu->pu_oppre(pu); if (error) goto out; /* Execute actual operation */ if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VFS) { switch (preq->preq_optype) { case PUFFS_VFS_UNMOUNT: { struct puffs_vfsmsg_unmount *auxt = auxbuf; PU_SETSTATE(pu, PUFFS_STATE_UNMOUNTING); error = pops->puffs_fs_unmount(pu, auxt->pvfsr_flags); if (!error) PU_SETSTATE(pu, PUFFS_STATE_UNMOUNTED); else PU_SETSTATE(pu, PUFFS_STATE_RUNNING); break; } case PUFFS_VFS_STATVFS: { struct puffs_vfsmsg_statvfs *auxt = auxbuf; error = pops->puffs_fs_statvfs(pu, &auxt->pvfsr_sb); break; } case PUFFS_VFS_SYNC: { struct puffs_vfsmsg_sync *auxt = auxbuf; PUFFS_MAKECRED(pcr, &auxt->pvfsr_cred); error = pops->puffs_fs_sync(pu, auxt->pvfsr_waitfor, pcr); break; } case PUFFS_VFS_FHTOVP: { struct puffs_vfsmsg_fhtonode *auxt = auxbuf; struct puffs_newinfo pni; pni.pni_cookie = &auxt->pvfsr_fhcookie; pni.pni_vtype = &auxt->pvfsr_vtype; pni.pni_size = &auxt->pvfsr_size; pni.pni_rdev = &auxt->pvfsr_rdev; pni.pni_va = NULL; pni.pni_va_ttl = NULL; pni.pni_cn_ttl = NULL; error = pops->puffs_fs_fhtonode(pu, auxt->pvfsr_data, auxt->pvfsr_dsize, &pni); break; } case PUFFS_VFS_VPTOFH: { struct puffs_vfsmsg_nodetofh *auxt = auxbuf; error = pops->puffs_fs_nodetofh(pu, auxt->pvfsr_fhcookie, auxt->pvfsr_data, &auxt->pvfsr_dsize); break; } case PUFFS_VFS_EXTATTRCTL: { struct puffs_vfsmsg_extattrctl *auxt = auxbuf; const char *attrname; int flags; if (pops->puffs_fs_extattrctl == NULL) { error = EOPNOTSUPP; break; } if (auxt->pvfsr_flags & PUFFS_EXTATTRCTL_HASATTRNAME) attrname = auxt->pvfsr_attrname; else attrname = NULL; flags = auxt->pvfsr_flags & PUFFS_EXTATTRCTL_HASNODE; error = pops->puffs_fs_extattrctl(pu, auxt->pvfsr_cmd, opcookie, flags, auxt->pvfsr_attrnamespace, attrname); break; } default: /* * I guess the kernel sees this one coming */ error = EINVAL; break; } /* XXX: audit return values */ /* XXX: sync with kernel */ } else if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN) { switch (preq->preq_optype) { case PUFFS_VN_LOOKUP: { struct puffs_vnmsg_lookup *auxt = auxbuf; struct puffs_newinfo pni; struct puffs_cn pcn; struct puffs_node *pn = NULL; pcn.pcn_pkcnp = &auxt->pvnr_cn; PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred); pni.pni_cookie = &auxt->pvnr_newnode; pni.pni_vtype = &auxt->pvnr_vtype; pni.pni_size = &auxt->pvnr_size; pni.pni_rdev = &auxt->pvnr_rdev; pni.pni_va = &auxt->pvnr_va; pni.pni_va_ttl = &auxt->pvnr_va_ttl; pni.pni_cn_ttl = &auxt->pvnr_cn_ttl; if (buildpath) { error = puffs_path_pcnbuild(pu, &pcn, opcookie); if (error) break; } /* lookup *must* be present */ error = pops->puffs_node_lookup(pu, opcookie, &pni, &pcn); if (buildpath) { if (error) { pu->pu_pathfree(pu, &pcn.pcn_po_full); } else { /* * did we get a new node or a * recycled node? */ pn = PU_CMAP(pu, auxt->pvnr_newnode); if (pn->pn_po.po_path == NULL) pn->pn_po = pcn.pcn_po_full; else pu->pu_pathfree(pu, &pcn.pcn_po_full); } } if (pncookie && !error) { if (pn == NULL) pn = PU_CMAP(pu, auxt->pvnr_newnode); pn->pn_nlookup++; } break; } case PUFFS_VN_CREATE: { struct puffs_vnmsg_create *auxt = auxbuf; struct puffs_newinfo pni; struct puffs_cn pcn; struct puffs_node *pn = NULL; if (pops->puffs_node_create == NULL) { error = 0; break; } pcn.pcn_pkcnp = &auxt->pvnr_cn; PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred); memset(&pni, 0, sizeof(pni)); pni.pni_cookie = &auxt->pvnr_newnode; pni.pni_va = &auxt->pvnr_va; pni.pni_va_ttl = &auxt->pvnr_va_ttl; pni.pni_cn_ttl = &auxt->pvnr_cn_ttl; if (buildpath) { error = puffs_path_pcnbuild(pu, &pcn, opcookie); if (error) break; } error = pops->puffs_node_create(pu, opcookie, &pni, &pcn, &auxt->pvnr_va); if (buildpath) { if (error) { pu->pu_pathfree(pu, &pcn.pcn_po_full); } else { pn = PU_CMAP(pu, auxt->pvnr_newnode); pn->pn_po = pcn.pcn_po_full; } } if (pncookie && !error) { if (pn == NULL) pn = PU_CMAP(pu, auxt->pvnr_newnode); pn->pn_nlookup++; } break; } case PUFFS_VN_MKNOD: { struct puffs_vnmsg_mknod *auxt = auxbuf; struct puffs_newinfo pni; struct puffs_cn pcn; struct puffs_node *pn = NULL; if (pops->puffs_node_mknod == NULL) { error = 0; break; } pcn.pcn_pkcnp = &auxt->pvnr_cn; PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred); memset(&pni, 0, sizeof(pni)); pni.pni_cookie = &auxt->pvnr_newnode; pni.pni_va = &auxt->pvnr_va; pni.pni_va_ttl = &auxt->pvnr_va_ttl; pni.pni_cn_ttl = &auxt->pvnr_cn_ttl; if (buildpath) { error = puffs_path_pcnbuild(pu, &pcn, opcookie); if (error) break; } error = pops->puffs_node_mknod(pu, opcookie, &pni, &pcn, &auxt->pvnr_va); if (buildpath) { if (error) { pu->pu_pathfree(pu, &pcn.pcn_po_full); } else { pn = PU_CMAP(pu, auxt->pvnr_newnode); pn->pn_po = pcn.pcn_po_full; } } if (pncookie && !error) { if (pn == NULL) pn = PU_CMAP(pu, auxt->pvnr_newnode); pn->pn_nlookup++; } break; } case PUFFS_VN_OPEN: { struct puffs_vnmsg_open *auxt = auxbuf; PUFFS_MAKECRED(pcr, &auxt->pvnr_cred); if (pops->puffs_node_open2 != NULL) { error = pops->puffs_node_open2(pu, opcookie, auxt->pvnr_mode, pcr, &auxt->pvnr_oflags); break; } if (pops->puffs_node_open == NULL) { error = 0; break; } error = pops->puffs_node_open(pu, opcookie, auxt->pvnr_mode, pcr); break; } case PUFFS_VN_CLOSE: { struct puffs_vnmsg_close *auxt = auxbuf; PUFFS_MAKECRED(pcr, &auxt->pvnr_cred); if (pops->puffs_node_close == NULL) { error = 0; break; } error = pops->puffs_node_close(pu, opcookie, auxt->pvnr_fflag, pcr); break; } case PUFFS_VN_ACCESS: { struct puffs_vnmsg_access *auxt = auxbuf; PUFFS_MAKECRED(pcr, &auxt->pvnr_cred); if (pops->puffs_node_access == NULL) { error = 0; break; } error = pops->puffs_node_access(pu, opcookie, auxt->pvnr_mode, pcr); break; } case PUFFS_VN_GETATTR: { struct puffs_vnmsg_getattr *auxt = auxbuf; PUFFS_MAKECRED(pcr, &auxt->pvnr_cred); if (PUFFS_USE_FS_TTL(pu)) { if (pops->puffs_node_getattr_ttl == NULL) { error = EOPNOTSUPP; break; } error = pops->puffs_node_getattr_ttl(pu, opcookie, &auxt->pvnr_va, pcr, &auxt->pvnr_va_ttl); } else { if (pops->puffs_node_getattr == NULL) { error = EOPNOTSUPP; break; } error = pops->puffs_node_getattr(pu, opcookie, &auxt->pvnr_va, pcr); } break; } case PUFFS_VN_SETATTR: { struct puffs_vnmsg_setattr *auxt = auxbuf; PUFFS_MAKECRED(pcr, &auxt->pvnr_cred); if (PUFFS_USE_FS_TTL(pu)) { int xflag = 0; if (pops->puffs_node_setattr_ttl == NULL) { error = EOPNOTSUPP; break; } if (!PUFFSOP_WANTREPLY(preq->preq_opclass)) xflag |= PUFFS_SETATTR_FAF; error = pops->puffs_node_setattr_ttl(pu, opcookie, &auxt->pvnr_va, pcr, &auxt->pvnr_va_ttl, xflag); } else { if (pops->puffs_node_setattr == NULL) { error = EOPNOTSUPP; break; } error = pops->puffs_node_setattr(pu, opcookie, &auxt->pvnr_va, pcr); } break; } case PUFFS_VN_MMAP: { struct puffs_vnmsg_mmap *auxt = auxbuf; PUFFS_MAKECRED(pcr, &auxt->pvnr_cred); if (pops->puffs_node_mmap == NULL) { error = 0; break; } error = pops->puffs_node_mmap(pu, opcookie, auxt->pvnr_prot, pcr); break; } case PUFFS_VN_FSYNC: { struct puffs_vnmsg_fsync *auxt = auxbuf; PUFFS_MAKECRED(pcr, &auxt->pvnr_cred); if (pops->puffs_node_fsync == NULL) { error = 0; break; } error = pops->puffs_node_fsync(pu, opcookie, pcr, auxt->pvnr_flags, auxt->pvnr_offlo, auxt->pvnr_offhi); break; } case PUFFS_VN_SEEK: { struct puffs_vnmsg_seek *auxt = auxbuf; PUFFS_MAKECRED(pcr, &auxt->pvnr_cred); if (pops->puffs_node_seek == NULL) { error = 0; break; } error = pops->puffs_node_seek(pu, opcookie, auxt->pvnr_oldoff, auxt->pvnr_newoff, pcr); break; } case PUFFS_VN_REMOVE: { struct puffs_vnmsg_remove *auxt = auxbuf; struct puffs_cn pcn; if (pops->puffs_node_remove == NULL) { error = 0; break; } pcn.pcn_pkcnp = &auxt->pvnr_cn; PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred); error = pops->puffs_node_remove(pu, opcookie, auxt->pvnr_cookie_targ, &pcn); break; } case PUFFS_VN_LINK: { struct puffs_vnmsg_link *auxt = auxbuf; struct puffs_cn pcn; if (pops->puffs_node_link == NULL) { error = 0; break; } pcn.pcn_pkcnp = &auxt->pvnr_cn; PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred); if (buildpath) { error = puffs_path_pcnbuild(pu, &pcn, opcookie); if (error) break; } error = pops->puffs_node_link(pu, opcookie, auxt->pvnr_cookie_targ, &pcn); if (buildpath) pu->pu_pathfree(pu, &pcn.pcn_po_full); break; } case PUFFS_VN_RENAME: { struct puffs_vnmsg_rename *auxt = auxbuf; struct puffs_cn pcn_src, pcn_targ; struct puffs_node *pn_src; if (pops->puffs_node_rename == NULL) { error = 0; break; } pcn_src.pcn_pkcnp = &auxt->pvnr_cn_src; PUFFS_KCREDTOCRED(pcn_src.pcn_cred, &auxt->pvnr_cn_src_cred); pcn_targ.pcn_pkcnp = &auxt->pvnr_cn_targ; PUFFS_KCREDTOCRED(pcn_targ.pcn_cred, &auxt->pvnr_cn_targ_cred); if (buildpath) { pn_src = auxt->pvnr_cookie_src; pcn_src.pcn_po_full = pn_src->pn_po; error = puffs_path_pcnbuild(pu, &pcn_targ, auxt->pvnr_cookie_targdir); if (error) break; } error = pops->puffs_node_rename(pu, opcookie, auxt->pvnr_cookie_src, &pcn_src, auxt->pvnr_cookie_targdir, auxt->pvnr_cookie_targ, &pcn_targ); if (buildpath) { if (error) { pu->pu_pathfree(pu, &pcn_targ.pcn_po_full); } else { struct puffs_pathinfo pi; struct puffs_pathobj po_old; /* handle this node */ po_old = pn_src->pn_po; pn_src->pn_po = pcn_targ.pcn_po_full; if (pn_src->pn_va.va_type != VDIR) { pu->pu_pathfree(pu, &po_old); break; } /* handle all child nodes for DIRs */ pi.pi_old = &pcn_src.pcn_po_full; pi.pi_new = &pcn_targ.pcn_po_full; PU_LOCK(); if (puffs_pn_nodewalk(pu, puffs_path_prefixadj, &pi) != NULL) error = ENOMEM; PU_UNLOCK(); pu->pu_pathfree(pu, &po_old); } } break; } case PUFFS_VN_MKDIR: { struct puffs_vnmsg_mkdir *auxt = auxbuf; struct puffs_newinfo pni; struct puffs_cn pcn; struct puffs_node *pn = NULL; if (pops->puffs_node_mkdir == NULL) { error = 0; break; } pcn.pcn_pkcnp = &auxt->pvnr_cn; PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred); memset(&pni, 0, sizeof(pni)); pni.pni_cookie = &auxt->pvnr_newnode; pni.pni_va = &auxt->pvnr_va; pni.pni_va_ttl = &auxt->pvnr_va_ttl; pni.pni_cn_ttl = &auxt->pvnr_cn_ttl; if (buildpath) { error = puffs_path_pcnbuild(pu, &pcn, opcookie); if (error) break; } error = pops->puffs_node_mkdir(pu, opcookie, &pni, &pcn, &auxt->pvnr_va); if (buildpath) { if (error) { pu->pu_pathfree(pu, &pcn.pcn_po_full); } else { pn = PU_CMAP(pu, auxt->pvnr_newnode); pn->pn_po = pcn.pcn_po_full; } } if (pncookie && !error) { if (pn == NULL) pn = PU_CMAP(pu, auxt->pvnr_newnode); pn->pn_nlookup++; } break; } case PUFFS_VN_RMDIR: { struct puffs_vnmsg_rmdir *auxt = auxbuf; struct puffs_cn pcn; if (pops->puffs_node_rmdir == NULL) { error = 0; break; } pcn.pcn_pkcnp = &auxt->pvnr_cn; PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred); error = pops->puffs_node_rmdir(pu, opcookie, auxt->pvnr_cookie_targ, &pcn); break; } case PUFFS_VN_SYMLINK: { struct puffs_vnmsg_symlink *auxt = auxbuf; struct puffs_newinfo pni; struct puffs_cn pcn; struct puffs_node *pn = NULL; if (pops->puffs_node_symlink == NULL) { error = 0; break; } pcn.pcn_pkcnp = &auxt->pvnr_cn; PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred); memset(&pni, 0, sizeof(pni)); pni.pni_cookie = &auxt->pvnr_newnode; pni.pni_va = &auxt->pvnr_va; pni.pni_va_ttl = &auxt->pvnr_va_ttl; pni.pni_cn_ttl = &auxt->pvnr_cn_ttl; if (buildpath) { error = puffs_path_pcnbuild(pu, &pcn, opcookie); if (error) break; } error = pops->puffs_node_symlink(pu, opcookie, &pni, &pcn, &auxt->pvnr_va, auxt->pvnr_link); if (buildpath) { if (error) { pu->pu_pathfree(pu, &pcn.pcn_po_full); } else { pn = PU_CMAP(pu, auxt->pvnr_newnode); pn->pn_po = pcn.pcn_po_full; } } if (pncookie && !error) { if (pn == NULL) pn = PU_CMAP(pu, auxt->pvnr_newnode); pn->pn_nlookup++; } break; } case PUFFS_VN_READDIR: { struct puffs_vnmsg_readdir *auxt = auxbuf; PUFFS_MAKECRED(pcr, &auxt->pvnr_cred); struct dirent *dent; off_t *cookies; size_t res, origcookies; if (pops->puffs_node_readdir == NULL) { error = 0; break; } if (auxt->pvnr_ncookies) { /* LINTED: pvnr_data is __aligned() */ cookies = (off_t *)auxt->pvnr_data; origcookies = auxt->pvnr_ncookies; } else { cookies = NULL; origcookies = 0; } /* LINTED: dentoff is aligned in the kernel */ dent = (struct dirent *) (auxt->pvnr_data + auxt->pvnr_dentoff); res = auxt->pvnr_resid; error = pops->puffs_node_readdir(pu, opcookie, dent, &auxt->pvnr_offset, &auxt->pvnr_resid, pcr, &auxt->pvnr_eofflag, cookies, &auxt->pvnr_ncookies); /* much easier to track non-working NFS */ assert(auxt->pvnr_ncookies <= origcookies); /* need to move a bit more */ preq->preq_buflen = sizeof(struct puffs_vnmsg_readdir) + auxt->pvnr_dentoff + (res - auxt->pvnr_resid); break; } case PUFFS_VN_READLINK: { struct puffs_vnmsg_readlink *auxt = auxbuf; PUFFS_MAKECRED(pcr, &auxt->pvnr_cred); if (pops->puffs_node_readlink == NULL) { error = EOPNOTSUPP; break; } /*LINTED*/ error = pops->puffs_node_readlink(pu, opcookie, pcr, auxt->pvnr_link, &auxt->pvnr_linklen); break; } case PUFFS_VN_RECLAIM: { struct puffs_vnmsg_reclaim *auxt = auxbuf; struct puffs_node *pn; if (pops->puffs_node_reclaim2 != NULL) { error = pops->puffs_node_reclaim2(pu, opcookie, auxt->pvnr_nlookup); break; } if (pops->puffs_node_reclaim == NULL) { error = 0; break; } /* * This fixes a race condition, * where a node in reclaimed by kernel * after a lookup request is sent, * but before the reply, leaving the kernel * with a invalid vnode/cookie reference. */ if (pncookie) { pn = PU_CMAP(pu, opcookie); pn->pn_nlookup -= auxt->pvnr_nlookup; if (pn->pn_nlookup >= 1) { error = 0; break; } } error = pops->puffs_node_reclaim(pu, opcookie); break; } case PUFFS_VN_INACTIVE: { if (pops->puffs_node_inactive == NULL) { error = EOPNOTSUPP; break; } error = pops->puffs_node_inactive(pu, opcookie); break; } case PUFFS_VN_PATHCONF: { struct puffs_vnmsg_pathconf *auxt = auxbuf; if (pops->puffs_node_pathconf == NULL) { error = 0; break; } error = pops->puffs_node_pathconf(pu, opcookie, auxt->pvnr_name, &auxt->pvnr_retval); break; } case PUFFS_VN_ADVLOCK: { struct puffs_vnmsg_advlock *auxt = auxbuf; if (pops->puffs_node_advlock == NULL) { error = 0; break; } error = pops->puffs_node_advlock(pu, opcookie, auxt->pvnr_id, auxt->pvnr_op, &auxt->pvnr_fl, auxt->pvnr_flags); break; } case PUFFS_VN_PRINT: { if (pops->puffs_node_print == NULL) { error = 0; break; } error = pops->puffs_node_print(pu, opcookie); break; } case PUFFS_VN_ABORTOP: { struct puffs_vnmsg_abortop *auxt = auxbuf; struct puffs_cn pcn; if (pops->puffs_node_abortop == NULL) { error = 0; break; } pcn.pcn_pkcnp = &auxt->pvnr_cn; PUFFS_KCREDTOCRED(pcn.pcn_cred, &auxt->pvnr_cn_cred); error = pops->puffs_node_abortop(pu, opcookie, &pcn); break; } case PUFFS_VN_READ: { struct puffs_vnmsg_read *auxt = auxbuf; PUFFS_MAKECRED(pcr, &auxt->pvnr_cred); size_t res; if (pops->puffs_node_read == NULL) { error = EIO; break; } res = auxt->pvnr_resid; error = pops->puffs_node_read(pu, opcookie, auxt->pvnr_data, auxt->pvnr_offset, &auxt->pvnr_resid, pcr, auxt->pvnr_ioflag); /* need to move a bit more */ preq->preq_buflen = sizeof(struct puffs_vnmsg_read) + (res - auxt->pvnr_resid); break; } case PUFFS_VN_WRITE: { struct puffs_vnmsg_write *auxt = auxbuf; PUFFS_MAKECRED(pcr, &auxt->pvnr_cred); if (pops->puffs_node_write2 != NULL) { int xflag = 0; if (!PUFFSOP_WANTREPLY(preq->preq_opclass)) xflag |= PUFFS_SETATTR_FAF; error = pops->puffs_node_write2(pu, opcookie, auxt->pvnr_data, auxt->pvnr_offset, &auxt->pvnr_resid, pcr, auxt->pvnr_ioflag, xflag); } else if (pops->puffs_node_write != NULL) { error = pops->puffs_node_write(pu, opcookie, auxt->pvnr_data, auxt->pvnr_offset, &auxt->pvnr_resid, pcr, auxt->pvnr_ioflag); } else { error = EIO; break; } /* don't need to move data back to the kernel */ preq->preq_buflen = sizeof(struct puffs_vnmsg_write); break; } case PUFFS_VN_POLL: { struct puffs_vnmsg_poll *auxt = auxbuf; if (pops->puffs_node_poll == NULL) { error = 0; /* emulate genfs_poll() */ auxt->pvnr_events &= (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM); break; } error = pops->puffs_node_poll(pu, opcookie, &auxt->pvnr_events); break; } case PUFFS_VN_GETEXTATTR: { struct puffs_vnmsg_getextattr *auxt = auxbuf; PUFFS_MAKECRED(pcr, &auxt->pvnr_cred); size_t res, *resp, *sizep; uint8_t *data; if (pops->puffs_node_getextattr == NULL) { error = EOPNOTSUPP; break; } if (auxt->pvnr_datasize) sizep = &auxt->pvnr_datasize; else sizep = NULL; res = auxt->pvnr_resid; if (res > 0) { data = auxt->pvnr_data; resp = &auxt->pvnr_resid; } else { data = NULL; resp = NULL; } error = pops->puffs_node_getextattr(pu, opcookie, auxt->pvnr_attrnamespace, auxt->pvnr_attrname, sizep, data, resp, pcr); /* need to move a bit more? */ preq->preq_buflen = sizeof(struct puffs_vnmsg_getextattr) + (res - auxt->pvnr_resid); break; } case PUFFS_VN_SETEXTATTR: { struct puffs_vnmsg_setextattr *auxt = auxbuf; PUFFS_MAKECRED(pcr, &auxt->pvnr_cred); size_t *resp; uint8_t *data; if (pops->puffs_node_setextattr == NULL) { error = EOPNOTSUPP; break; } if (auxt->pvnr_resid > 0) { data = auxt->pvnr_data; resp = &auxt->pvnr_resid; } else { data = NULL; resp = NULL; } error = pops->puffs_node_setextattr(pu, opcookie, auxt->pvnr_attrnamespace, auxt->pvnr_attrname, data, resp, pcr); break; } case PUFFS_VN_LISTEXTATTR: { struct puffs_vnmsg_listextattr *auxt = auxbuf; PUFFS_MAKECRED(pcr, &auxt->pvnr_cred); size_t res, *resp, *sizep; int flag; uint8_t *data; if (pops->puffs_node_listextattr == NULL) { error = EOPNOTSUPP; break; } if (auxt->pvnr_datasize) sizep = &auxt->pvnr_datasize; else sizep = NULL; res = auxt->pvnr_resid; if (res > 0) { data = auxt->pvnr_data; resp = &auxt->pvnr_resid; } else { data = NULL; resp = NULL; } res = auxt->pvnr_resid; flag = auxt->pvnr_flag; error = pops->puffs_node_listextattr(pu, opcookie, auxt->pvnr_attrnamespace, sizep, data, resp, flag, pcr); /* need to move a bit more? */ preq->preq_buflen = sizeof(struct puffs_vnmsg_listextattr) + (res - auxt->pvnr_resid); break; } case PUFFS_VN_DELETEEXTATTR: { struct puffs_vnmsg_deleteextattr *auxt = auxbuf; PUFFS_MAKECRED(pcr, &auxt->pvnr_cred); if (pops->puffs_node_deleteextattr == NULL) { error = EOPNOTSUPP; break; } error = pops->puffs_node_deleteextattr(pu, opcookie, auxt->pvnr_attrnamespace, auxt->pvnr_attrname, pcr); break; } case PUFFS_VN_FALLOCATE: { struct puffs_vnmsg_fallocate *auxt = auxbuf; if (pops->puffs_node_fallocate == NULL) { error = EOPNOTSUPP; break; } error = pops->puffs_node_fallocate(pu, opcookie, auxt->pvnr_off, auxt->pvnr_len); break; } case PUFFS_VN_FDISCARD: { struct puffs_vnmsg_fdiscard *auxt = auxbuf; if (pops->puffs_node_fdiscard == NULL) { error = EOPNOTSUPP; break; } error = pops->puffs_node_fdiscard(pu, opcookie, auxt->pvnr_off, auxt->pvnr_len); break; } default: printf("inval op %d\n", preq->preq_optype); error = EINVAL; break; } #if 0 /* not issued by kernel currently */ } else if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_CACHE) { struct puffs_cacheinfo *pci = (void *)preq; if (pu->pu_ops.puffs_cache_write) { pu->pu_ops.puffs_cache_write(pu, preq->preq_cookie, pci->pcache_nruns, pci->pcache_runs); } error = 0; #endif } else if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_ERROR) { struct puffs_error *perr = (void *)preq; pu->pu_errnotify(pu, preq->preq_optype, perr->perr_error, perr->perr_str, preq->preq_cookie); error = 0; } else { /* * I guess the kernel sees this one coming also */ error = EINVAL; } out: preq->preq_rv = error; if (pu->pu_oppost) pu->pu_oppost(pu); pcc->pcc_flags |= PCC_DONE; }
void puffsdump_req(struct puffs_req *preq) { static struct timeval tv_prev; struct timeval tv_now, tv; const char **map; int isvn = 0; map = NULL; /* yes, we are all interested in your opinion, gcc */ switch (PUFFSOP_OPCLASS(preq->preq_opclass)) { case PUFFSOP_VFS: map = vfsop_revmap; break; case PUFFSOP_VN: map = vnop_revmap; isvn = 1; break; case PUFFSOP_CACHE: map = cacheop_revmap; break; case PUFFSOP_ERROR: map = errnot_revmap; break; case PUFFSOP_FLUSH: map = flush_revmap; break; } printf("\treqid: %" PRIu64 ", opclass %d%s, optype: %s, " "cookie: %p,\n\t\taux: %p, auxlen: %zu, pid: %d, lwpid: %d\n", preq->preq_id, PUFFSOP_OPCLASS(preq->preq_opclass), PUFFSOP_WANTREPLY(preq->preq_opclass) ? "" : " (FAF)", map[preq->preq_optype], preq->preq_cookie, preq->preq_buf, preq->preq_buflen, preq->preq_pid, preq->preq_lid); if (isvn) { switch (preq->preq_optype) { case PUFFS_VN_LOOKUP: puffsdump_lookup(preq); break; case PUFFS_VN_READ: case PUFFS_VN_WRITE: puffsdump_readwrite(preq); break; case PUFFS_VN_OPEN: puffsdump_open(preq); break; case PUFFS_VN_REMOVE: case PUFFS_VN_RMDIR: case PUFFS_VN_LINK: puffsdump_targ(preq); break; case PUFFS_VN_READDIR: puffsdump_readdir(preq); break; default: break; } } PU_LOCK(); gettimeofday(&tv_now, NULL); timersub(&tv_now, &tv_prev, &tv); printf("\t\tsince previous call: %lld.%06ld\n", (long long)tv.tv_sec, (long)tv.tv_usec); gettimeofday(&tv_prev, NULL); PU_UNLOCK(); }