/* * The dce irqs are not acked by the rlc (block in charge of irq management). * It seems the rlc block does not have xrbm access (the block in charge of * register management). * Then those irqs must be acked manually by the CPU. * This is called in hard interrupt context, it does not lock the dce. */ void dce4_irqs_ack(struct dce4 *dce) { u32 disps[CRTCS_N_MAX]; u32 grphs[CRTCS_N_MAX]; unsigned i; u32 tmp; for (i = 0; i < dce->ddev.crtcs_n; ++i) disps[i] = RR32(regs_disp_int_status[i]); for (i = 0; i < dce->ddev.crtcs_n; ++i) grphs[i] = RR32(regs_crtc_grph_int_status[i]); /* XXX: obsolete page flipping */ for (i = 0; i < dce->ddev.crtcs_n; ++i) if (grphs[i] & GRPH_PFLIP_INT_OCCURRED) WR32(GRPH_PFLIP_INT_CLR, regs_crtc_grph_int_status[i]); for (i = 0; i < dce->ddev.crtcs_n; ++i) { if (disps[i] & vals_lb_d_vblank_int[i]) WR32(VBLANK_ACK, regs_crtc_vblank_status[i]); if (disps[i] & vals_lb_d_vline_int[i]) WR32(VLINE_ACK, regs_crtc_vline_status[i]); } for (i = 0; i < HPDS_N; ++i) if (disps[i] & vals_hpd_int[i]) { tmp = RR32(regs_hpd_int_ctl[i]); tmp |= HPDx_INT_CTL_INT_ACK; WR32(tmp, regs_hpd_int_ctl[i]); } }
void dce4_vga_off(struct dce4 *dce) { unsigned i; WR32(VGA_MEM_DIS, VGA_HDP_CTL); WR32(RR32(VGA_RENDER_CTL) & VGA_VSTATUS_CTL_CLR, VGA_RENDER_CTL); for (i = 0; i < dce->ddev.crtcs_n; ++i) WR32(0, regs_vga_ctl[i]); }
void dce6_vga_off(struct dce6 *dce) { u8 i; /* lockout access to vga mem through hdp */ WR32(VHC_VGA_MEM_DIS, VGA_HDP_CTL); WR32(RR32(VGA_RENDER_CTL) & VRC_VGA_VSTATUS_CTL_CLR, VGA_RENDER_CTL); for (i = 0; i < dce->ddev.crtcs_n; ++i) WR32(0, regs_vga_ctl[i]); }
nfsstat4 nfs_op_lookupp(struct nfs_cxn *cxn, struct list_head *writes, struct rpc_write **wr) { nfsstat4 status = NFS4_OK; struct nfs_inode *ino = NULL; if (debugging) applog(LOG_INFO, "op LOOKUPP"); status = dir_curfh(NULL, cxn, &ino, 0); if (status != NFS4_OK) goto out; if (ino->inum == INO_ROOT) { /* root inode, no parents */ status = NFS4ERR_NOENT; goto out; } fh_set(&cxn->current_fh, ino->parent); out: WR32(status); inode_free(ino); return status; }
static long dp_off(struct dce6 *dce, u8 i) { long r; r = atb_trans_link_off(dce->ddev.atb, i); if (r == -ATB_ERR) return -DCE6_ERR; r = atb_trans_link_pwr(dce->ddev.atb, i, 0); if (r == -ATB_ERR) return -DCE6_ERR; r = atb_enc_video(dce->ddev.atb, i, dce->dps[i].hpd, 0); if (r == -ATB_ERR) return -DCE6_ERR; r = atb_crtc_blank(dce->ddev.atb, i, 1); if (r == -ATB_ERR) return -DCE6_ERR; r = atb_crtc(dce->ddev.atb, i, 0); if (r == -ATB_ERR) return -DCE6_ERR; WR32(0, regs_grph_ena[i]); r = atb_crtc_pair_pwr_gate(dce->ddev.atb, i, 1); if (r == -ATB_ERR) return -DCE6_ERR; return 0; }
long dp_dpm_off(struct dce6 *dce, u8 i) { long r; r = atb_trans_link_off(dce->ddev.atb, i); if (r == -ATB_ERR) return -DCE6_ERR; r = atb_enc_video(dce->ddev.atb, i, dce->dps[i].hpd, 0); if (r == -ATB_ERR) return -DCE6_ERR; if (dce->dps[i].edp) {/* shutdown sink power only for edp */ r = atb_trans_link_pwr(dce->ddev.atb, i, 0); if (r == -ATB_ERR) return -DCE6_ERR; } r = atb_crtc_blank(dce->ddev.atb, i, 1); if (r == -ATB_ERR) return -DCE6_ERR; r = atb_crtc(dce->ddev.atb, i, 0); if (r == -ATB_ERR) return -DCE6_ERR; WR32(0, regs_grph_ena[i]); r = atb_crtc_pair_pwr_gate(dce->ddev.atb, i, 1); if (r == -ATB_ERR) return -DCE6_ERR; return 0; }
static void encode_acl(const fattr4_acl *acl, struct list_head *writes, struct rpc_write **wr) { struct nfs_buf nb; nfsace4 *ace; int i; WR32(acl->fattr4_acl_len); for (i = 0; i < acl->fattr4_acl_len; i++) { ace = &acl->fattr4_acl_val[i]; WR32(ace->type); WR32(ace->flag); WR32(ace->access_mask); nb.len = ace->who.utf8string_len; nb.val = ace->who.utf8string_val; WRBUF(&nb); } }
int dce4_init(struct dce4 *dce, u32 dmif_addr_cfg) { int r; /* configure the dce memory interface */ WR32(dmif_addr_cfg, DMIF_ADDR_CFG); mutex_init(&dce->mutex); spin_lock_init(&dce->irq.lock); r = paths_parse(dce); if (r != 0) goto err; dce_dump(dce); r = trans_links_init(dce); if (r != 0) goto err; r = atb_crtc_dcpll(dce->ddev.atb); if (r != 0) goto err; hpds_init(dce); dps_used_pf_init(dce); r = dps_used_pwr_on(dce); if (r != 0) goto err; r = dps_used_dpm_off(dce); if (r != 0) goto err; r = dps_unused_off(dce); if (r != 0) goto err; r = dps_sense(dce); if (r != 0) goto err; r = dps_connected_dpcd_info(dce); if (r != 0) goto err; dps_connected_edid(dce); err: return r; }
long dce6_init(struct dce6 *dce, u32 dmif_addr_cfg) { long r; lock(dce); /* configure the dce memory interface */ WR32(dmif_addr_cfg, DMIF_ADDR_CFG); WR32(dmif_addr_cfg, DMIF_ADDR_CALC); crtcs_atb_states_init(dce); r = trans_links_init(dce); if (r == -DCE6_ERR) return -DCE6_ERR; r = atb_crtc_dcpll(dce->ddev.atb); if (r == -ATB_ERR) return -DCE6_ERR; hpds_init(dce); r = dps_used_pwr_on(dce); if (r == -DCE6_ERR) return -DCE6_ERR; r = dps_used_dpm_off(dce); if (r == -DCE6_ERR) return -DCE6_ERR; r = dps_unused_off(dce); if (r == -DCE6_ERR) return -DCE6_ERR; unlock(dce); return 0; }
nfsstat4 wr_fattr(const struct nfs_fattr_set *attr, uint64_t *_bitmap_out, struct list_head *writes, struct rpc_write **wr) { uint64_t bitmap = attr->bitmap; uint64_t bitmap_out = 0; uint32_t *bmap[2]; uint32_t attr_size; WR32(2); /* bitmap array size */ bmap[0] = WRSKIP(4); /* bitmap array[0] */ bmap[1] = WRSKIP(4); /* bitmap array[1] */ attr_size = fattr_size(attr); WR32(attr_size); if (bitmap & (1ULL << FATTR4_SUPPORTED_ATTRS)) { WRMAP(fattr_supported_mask); bitmap_out |= (1ULL << FATTR4_SUPPORTED_ATTRS); } if (bitmap & (1ULL << FATTR4_TYPE)) { WR32(attr->type); bitmap_out |= (1ULL << FATTR4_TYPE); } if (bitmap & (1ULL << FATTR4_FH_EXPIRE_TYPE)) { WR32(attr->fh_expire_type); bitmap_out |= (1ULL << FATTR4_FH_EXPIRE_TYPE); } if (bitmap & (1ULL << FATTR4_CHANGE)) { WR64(attr->change); bitmap_out |= (1ULL << FATTR4_CHANGE); } if (bitmap & (1ULL << FATTR4_SIZE)) { WR64(attr->size); bitmap_out |= (1ULL << FATTR4_SIZE); } if (bitmap & (1ULL << FATTR4_LINK_SUPPORT)) { WR32(attr->link_support ? 1 : 0); bitmap_out |= (1ULL << FATTR4_LINK_SUPPORT); } if (bitmap & (1ULL << FATTR4_SYMLINK_SUPPORT)) { WR32(attr->symlink_support ? 1 : 0); bitmap_out |= (1ULL << FATTR4_SYMLINK_SUPPORT); } if (bitmap & (1ULL << FATTR4_NAMED_ATTR)) { WR32(attr->named_attr ? 1 : 0); bitmap_out |= (1ULL << FATTR4_NAMED_ATTR); } if (bitmap & (1ULL << FATTR4_FSID)) { WR64(attr->fsid.major); WR64(attr->fsid.minor); bitmap_out |= (1ULL << FATTR4_FSID); } if (bitmap & (1ULL << FATTR4_UNIQUE_HANDLES)) { WR32(attr->unique_handles ? 1 : 0); bitmap_out |= (1ULL << FATTR4_UNIQUE_HANDLES); } if (bitmap & (1ULL << FATTR4_LEASE_TIME)) { WR32(attr->lease_time); bitmap_out |= (1ULL << FATTR4_LEASE_TIME); } if (bitmap & (1ULL << FATTR4_RDATTR_ERROR)) { WR32(attr->rdattr_error); bitmap_out |= (1ULL << FATTR4_RDATTR_ERROR); } #if 0 if (bitmap & (1ULL << FATTR4_ACL)) { encode_acl(&attr->acl, writes, wr); bitmap_out |= (1ULL << FATTR4_ACL); } #endif if (bitmap & (1ULL << FATTR4_ACLSUPPORT)) { WR32(attr->aclsupport); bitmap_out |= (1ULL << FATTR4_ACLSUPPORT); } if (bitmap & (1ULL << FATTR4_ARCHIVE)) { WR32(attr->archive ? 1 : 0); bitmap_out |= (1ULL << FATTR4_ARCHIVE); } if (bitmap & (1ULL << FATTR4_CANSETTIME)) { WR32(attr->cansettime ? 1 : 0); bitmap_out |= (1ULL << FATTR4_CANSETTIME); } if (bitmap & (1ULL << FATTR4_CASE_INSENSITIVE)) { WR32(attr->case_insensitive ? 1 : 0); bitmap_out |= (1ULL << FATTR4_CASE_INSENSITIVE); } if (bitmap & (1ULL << FATTR4_CASE_PRESERVING)) { WR32(attr->case_preserving ? 1 : 0); bitmap_out |= (1ULL << FATTR4_CASE_PRESERVING); } if (bitmap & (1ULL << FATTR4_CHOWN_RESTRICTED)) { WR32(attr->chown_restricted ? 1 : 0); bitmap_out |= (1ULL << FATTR4_CHOWN_RESTRICTED); } if (bitmap & (1ULL << FATTR4_FILEHANDLE)) { WR32(sizeof(struct nfs_fh)); WR64(attr->filehandle); bitmap_out |= (1ULL << FATTR4_FILEHANDLE); } if (bitmap & (1ULL << FATTR4_FILEID)) { WR64(attr->fileid); bitmap_out |= (1ULL << FATTR4_FILEID); } if (bitmap & (1ULL << FATTR4_FILES_AVAIL)) { WR64(attr->files_avail); bitmap_out |= (1ULL << FATTR4_FILES_AVAIL); } if (bitmap & (1ULL << FATTR4_FILES_FREE)) { WR64(attr->files_free); bitmap_out |= (1ULL << FATTR4_FILES_FREE); } if (bitmap & (1ULL << FATTR4_FILES_TOTAL)) { WR64(attr->files_total); bitmap_out |= (1ULL << FATTR4_FILES_TOTAL); } if (bitmap & (1ULL << FATTR4_FS_LOCATIONS)) { WR32(1); /* fs_root len */ WRSTR("/"); /* fs_root */ WR32(1); /* location len */ WR32(1); /* location[0].server len */ WRSTR(my_hostname); /* location[0].server[0] */ WR32(1); /* location[0].rootpath len */ WRSTR("/"); /* location[0].rootpath */ bitmap_out |= (1ULL << FATTR4_FS_LOCATIONS); } if (bitmap & (1ULL << FATTR4_HIDDEN)) { WR32(attr->hidden ? 1 : 0); bitmap_out |= (1ULL << FATTR4_HIDDEN); } if (bitmap & (1ULL << FATTR4_HOMOGENEOUS)) { WR32(attr->homogeneous ? 1 : 0); bitmap_out |= (1ULL << FATTR4_HOMOGENEOUS); } if (bitmap & (1ULL << FATTR4_MAXFILESIZE)) { WR64(attr->maxfilesize); bitmap_out |= (1ULL << FATTR4_MAXFILESIZE); } if (bitmap & (1ULL << FATTR4_MAXLINK)) { WR32(attr->maxlink); bitmap_out |= (1ULL << FATTR4_MAXLINK); } if (bitmap & (1ULL << FATTR4_MAXNAME)) { WR32(attr->maxname); bitmap_out |= (1ULL << FATTR4_MAXNAME); } if (bitmap & (1ULL << FATTR4_MAXREAD)) { WR64(attr->maxread); bitmap_out |= (1ULL << FATTR4_MAXREAD); } if (bitmap & (1ULL << FATTR4_MAXWRITE)) { WR64(attr->maxwrite); bitmap_out |= (1ULL << FATTR4_MAXWRITE); } if ((bitmap & (1ULL << FATTR4_MIMETYPE)) && (attr->mimetype.len)) { WRBUF(&attr->mimetype); bitmap_out |= (1ULL << FATTR4_MIMETYPE); } if (bitmap & (1ULL << FATTR4_MODE)) { WR32(attr->mode); bitmap_out |= (1ULL << FATTR4_MODE); } if (bitmap & (1ULL << FATTR4_NO_TRUNC)) { WR32(attr->no_trunc ? 1 : 0); bitmap_out |= (1ULL << FATTR4_NO_TRUNC); } if (bitmap & (1ULL << FATTR4_NUMLINKS)) { WR32(attr->numlinks); bitmap_out |= (1ULL << FATTR4_NUMLINKS); } if ((bitmap & (1ULL << FATTR4_OWNER)) && (attr->owner.len)) { WRBUF(&attr->owner); bitmap_out |= (1ULL << FATTR4_OWNER); } if ((bitmap & (1ULL << FATTR4_OWNER_GROUP)) && (attr->owner_group.len)){ WRBUF(&attr->owner_group); bitmap_out |= (1ULL << FATTR4_OWNER_GROUP); } if (bitmap & (1ULL << FATTR4_QUOTA_AVAIL_HARD)) { WR64(attr->quota_avail_hard); bitmap_out |= (1ULL << FATTR4_QUOTA_AVAIL_HARD); } if (bitmap & (1ULL << FATTR4_QUOTA_AVAIL_SOFT)) { WR64(attr->quota_avail_soft); bitmap_out |= (1ULL << FATTR4_QUOTA_AVAIL_SOFT); } if (bitmap & (1ULL << FATTR4_QUOTA_USED)) { WR64(attr->quota_used); bitmap_out |= (1ULL << FATTR4_QUOTA_USED); } if (bitmap & (1ULL << FATTR4_RAWDEV)) { /* FIXME: correct order of these two dwords? */ WR32(attr->rawdev.specdata1); WR32(attr->rawdev.specdata2); bitmap_out |= (1ULL << FATTR4_RAWDEV); } if (bitmap & (1ULL << FATTR4_SPACE_AVAIL)) { WR64(attr->space_avail); bitmap_out |= (1ULL << FATTR4_SPACE_AVAIL); } if (bitmap & (1ULL << FATTR4_SPACE_FREE)) { WR64(attr->space_free); bitmap_out |= (1ULL << FATTR4_SPACE_FREE); } if (bitmap & (1ULL << FATTR4_SPACE_TOTAL)) { WR64(attr->space_total); bitmap_out |= (1ULL << FATTR4_SPACE_TOTAL); } if (bitmap & (1ULL << FATTR4_SPACE_USED)) { WR64(attr->space_used); bitmap_out |= (1ULL << FATTR4_SPACE_USED); } if (bitmap & (1ULL << FATTR4_SYSTEM)) { WR32(attr->system ? 1 : 0); bitmap_out |= (1ULL << FATTR4_SYSTEM); } if (bitmap & (1ULL << FATTR4_TIME_ACCESS)) { WR64(attr->time_access.seconds); WR32(attr->time_access.nseconds); bitmap_out |= (1ULL << FATTR4_TIME_ACCESS); } if (bitmap & (1ULL << FATTR4_TIME_BACKUP)) { WR64(attr->time_backup.seconds); WR32(attr->time_backup.nseconds); bitmap_out |= (1ULL << FATTR4_TIME_BACKUP); } if (bitmap & (1ULL << FATTR4_TIME_CREATE)) { WR64(attr->time_create.seconds); WR32(attr->time_create.nseconds); bitmap_out |= (1ULL << FATTR4_TIME_CREATE); } if (bitmap & (1ULL << FATTR4_TIME_DELTA)) { WR64(attr->time_delta.seconds); WR32(attr->time_delta.nseconds); bitmap_out |= (1ULL << FATTR4_TIME_DELTA); } if (bitmap & (1ULL << FATTR4_TIME_METADATA)) { WR64(attr->time_metadata.seconds); WR32(attr->time_metadata.nseconds); bitmap_out |= (1ULL << FATTR4_TIME_METADATA); } if (bitmap & (1ULL << FATTR4_TIME_MODIFY)) { WR64(attr->time_modify.seconds); WR32(attr->time_modify.nseconds); bitmap_out |= (1ULL << FATTR4_TIME_MODIFY); } if (bitmap & (1ULL << FATTR4_MOUNTED_ON_FILEID)) { WR64(attr->mounted_on_fileid); bitmap_out |= (1ULL << FATTR4_MOUNTED_ON_FILEID); } *_bitmap_out = bitmap_out; *bmap[0] = htonl(bitmap_out); *bmap[1] = htonl(bitmap_out >> 32); return NFS4_OK; }
nfsstat4 nfs_op_readdir(struct nfs_cxn *cxn, const READDIR4args *args, struct list_head *writes, struct rpc_write **wr) { nfsstat4 status = NFS4_OK; struct nfs_inode *ino = NULL; uint32_t dircount, maxcount, *status_p; struct readdir_info ri; uint64_t cookie, attr_request; const verifier4 *cookie_verf; DB_TXN *txn = NULL; DB *dirent = srv.fsdb.dirent; DB_ENV *dbenv = srv.fsdb.env; DBT pkey, pval; struct fsdb_de_key key; int cget_flags; DBC *curs = NULL; int rc; uint64_t dirent_inum, db_de; struct fsdb_de_key *rkey; cookie = args->cookie; cookie_verf = &args->cookieverf; dircount = args->dircount; maxcount = args->maxcount; attr_request = bitmap4_decode(&args->attr_request); status_p = WRSKIP(4); if (debugging) { applog(LOG_INFO, "op READDIR (COOKIE:%Lu DIR:%u MAX:%u MAP:%Lx)", (unsigned long long) cookie, dircount, maxcount, (unsigned long long) attr_request); print_fattr_bitmap("op READDIR", attr_request); } /* traditionally "." and "..", hardcoded */ if (cookie == 1 || cookie == 2) { status = NFS4ERR_BAD_COOKIE; goto out; } /* don't permit request of write-only attrib */ if (attr_request & fattr_write_only_mask) { status = NFS4ERR_INVAL; goto out; } /* FIXME: very, very, very poor verifier */ if (cookie && memcmp(cookie_verf, &srv.instance_verf, sizeof(verifier4))) { status = NFS4ERR_NOT_SAME; goto out; } /* read inode of directory being read */ status = dir_curfh(NULL, cxn, &ino, 0); if (status != NFS4_OK) goto out; if (ino->mode == 0) { status = NFS4ERR_ACCESS; goto out; } /* subtract READDIR4resok header and footer size */ if (maxcount < 16) { status = NFS4ERR_TOOSMALL; goto out; } maxcount -= (8 + 4 + 4); /* verify within server limits */ if (dircount > SRV_MAX_READ || maxcount > SRV_MAX_READ) { status = NFS4ERR_INVAL; goto out; } /* open transaction */ rc = dbenv->txn_begin(dbenv, NULL, &txn, 0); if (rc) { status = NFS4ERR_IO; dbenv->err(dbenv, rc, "DB_ENV->txn_begin"); goto out; } /* set up directory iteration */ memset(&ri, 0, sizeof(ri)); ri.cookie = cookie; ri.dircount = dircount; ri.maxcount = maxcount; ri.attr_request = attr_request; ri.status = NFS4_OK; ri.writes = writes; ri.wr = wr; ri.dir_pos = 3; ri.first_time = true; /* if dir is empty, skip directory interation loop completely */ if (dir_is_empty(txn, ino)) { WRMEM(&srv.instance_verf, sizeof(verifier4)); /* cookieverf */ ri.val_follows = WRSKIP(4); if (debugging) applog(LOG_DEBUG, " READDIR: empty directory"); goto the_finale; } /* otherwise, loop through each dirent attached to ino->inum */ rc = dirent->cursor(dirent, txn, &curs, 0); if (rc) { status = NFS4ERR_IO; dirent->err(dirent, rc, "dirent->cursor"); goto out_abort; } key.inum = inum_encode(ino->inum); memset(&pkey, 0, sizeof(pkey)); pkey.data = &key; pkey.size = sizeof(key); pkey.flags = DB_DBT_MALLOC; memset(&pval, 0, sizeof(pval)); pval.data = &db_de; pval.ulen = sizeof(db_de); pval.flags = DB_DBT_USERMEM; cget_flags = DB_SET_RANGE; while (1) { bool iter_rc; rc = curs->get(curs, &pkey, &pval, cget_flags); if (rc) { if (rc != DB_NOTFOUND) dirent->err(dirent, rc, "readdir curs->get"); break; } cget_flags = DB_NEXT; rkey = pkey.data; if (inum_decode(rkey->inum) != ino->inum) { free(rkey); break; } dirent_inum = inum_decode(db_de); iter_rc = readdir_iter(txn, rkey, pkey.size, dirent_inum, &ri); free(rkey); if (iter_rc) break; } if (!ri.n_results) { if (debugging) applog(LOG_INFO, " zero results, status %s", ri.status <= NFS4ERR_CB_PATH_DOWN ? status2str(ri.status) : "n/a"); if (ri.status == NFS4_OK) { WRMEM(&srv.instance_verf, sizeof(verifier4)); /* cookieverf */ ri.val_follows = WRSKIP(4); } } rc = curs->close(curs); if (rc) { status = NFS4ERR_IO; dirent->err(dirent, rc, "dirent->cursor close"); goto out_abort; } the_finale: /* terminate final entry4.nextentry and dirlist4.entries */ if (ri.val_follows) *ri.val_follows = htonl(0); if (ri.cookie_found && !ri.n_results && ri.hit_limit) { status = NFS4ERR_TOOSMALL; goto out_abort; } /* close transaction */ rc = txn->commit(txn, 0); if (rc) { dbenv->err(dbenv, rc, "DB_ENV->txn_commit"); status = NFS4ERR_IO; goto out; } WR32(ri.hit_limit ? 0 : 1); /* reply eof */ out: *status_p = htonl(status); inode_free(ino); return status; out_abort: if (txn->abort(txn)) dbenv->err(dbenv, rc, "DB_ENV->txn_abort"); goto out; }
nfsstat4 nfs_op_rename(struct nfs_cxn *cxn, const RENAME4args *args, struct list_head *writes, struct rpc_write **wr) { nfsstat4 status = NFS4_OK; struct nfs_inode *src_dir = NULL, *target_dir = NULL; struct nfs_inode *old_file = NULL, *new_file = NULL; struct nfs_buf oldname, newname; change_info4 src = { true, 0, 0 }; change_info4 target = { true, 0, 0 }; DB_TXN *txn = NULL; DB_ENV *dbenv = srv.fsdb.env; int rc; nfsino_t old_dirent, new_dirent; oldname.len = args->oldname.utf8string_len; oldname.val = args->oldname.utf8string_val; newname.len = args->newname.utf8string_len; newname.val = args->newname.utf8string_val; if (debugging) applog(LOG_INFO, "op RENAME (OLD:%.*s, NEW:%.*s)", oldname.len, oldname.val, newname.len, newname.val); /* validate text input */ if ((!valid_utf8string(&oldname)) || (!valid_utf8string(&newname))) { status = NFS4ERR_INVAL; goto out; } if (has_dots(&oldname) || has_dots(&newname)) { status = NFS4ERR_BADNAME; goto out; } rc = dbenv->txn_begin(dbenv, NULL, &txn, 0); if (rc) { status = NFS4ERR_IO; dbenv->err(dbenv, rc, "DB_ENV->txn_begin"); goto out; } /* reference source, target directories. * NOTE: src_dir and target_dir may point to the same object */ src_dir = inode_fhdec(txn, cxn->save_fh, DB_RMW); if (fh_equal(cxn->save_fh, cxn->current_fh)) target_dir = src_dir; else target_dir = inode_fhdec(txn, cxn->current_fh, DB_RMW); if (!src_dir || !target_dir) { status = NFS4ERR_NOFILEHANDLE; goto out_abort; } if ((src_dir->type != NF4DIR) || (target_dir->type != NF4DIR)) { status = NFS4ERR_NOTDIR; goto out_abort; } /* lookup source, target names */ status = dir_lookup(txn, src_dir, &oldname, 0, &old_dirent); if (status != NFS4_OK) goto out_abort; old_file = inode_getdec(txn, old_dirent, 0); if (!old_file) { status = NFS4ERR_NOENT; goto out_abort; } status = dir_lookup(txn, target_dir, &newname, 0, &new_dirent); if (status != NFS4_OK && status != NFS4ERR_NOENT) goto out_abort; /* if target (newname) is present, attempt to remove */ if (status == NFS4_OK) { bool ok_to_remove = false; /* read to-be-deleted inode */ new_file = inode_getdec(txn, new_dirent, DB_RMW); if (!new_file) { status = NFS4ERR_NOENT; goto out_abort; } /* do oldname and newname refer to same file? */ if (old_file->inum == new_file->inum) { src.after = src.before = src_dir->version; target.after = target.before = target_dir->version; goto out_abort; } if (old_file->type != NF4DIR && new_file->type != NF4DIR) ok_to_remove = true; else if (old_file->type == NF4DIR && new_file->type == NF4DIR && dir_is_empty(txn, new_file)) ok_to_remove = true; if (!ok_to_remove) { status = NFS4ERR_EXIST; goto out_abort; } /* remove target inode from directory */ rc = fsdb_dirent_del(&srv.fsdb, txn, target_dir->inum, &newname, 0); if (rc == 0) rc = inode_unlink(txn, new_file); if (rc) { status = NFS4ERR_IO; goto out_abort; } } else status = NFS4_OK; new_dirent = old_dirent; /* delete entry from source directory; add to target directory */ rc = fsdb_dirent_del(&srv.fsdb, txn, src_dir->inum, &oldname, 0); if (rc == 0) rc = fsdb_dirent_put(&srv.fsdb, txn, target_dir->inum, &newname, 0, new_dirent); if (rc) { status = NFS4ERR_IO; goto out_abort; } /* if renamed file is a directory, ensure its 'parent' is updated */ if (old_file->type == NF4DIR) { old_file->parent = target_dir->inum; if (inode_touch(txn, old_file)) { status = NFS4ERR_IO; goto out_abort; } } /* record directory change info */ src.before = src_dir->version; target.before = target_dir->version; /* update last-modified stamps of directory inodes */ rc = inode_touch(txn, src_dir); if (rc == 0 && src_dir != target_dir) rc = inode_touch(txn, target_dir); if (rc) { status = NFS4ERR_IO; goto out_abort; } /* close the transaction */ rc = txn->commit(txn, 0); if (rc) { dbenv->err(dbenv, rc, "DB_ENV->txn_commit"); status = NFS4ERR_IO; goto out; } src.after = src_dir->version; target.after = target_dir->version; out: WR32(status); if (status == NFS4_OK) { WR32(src.atomic ? 1 : 0); /* src cinfo.atomic */ WR64(src.before); /* src cinfo.before */ WR64(src.after); /* src cinfo.after */ WR32(target.atomic ? 1 : 0); /* target cinfo.atomic */ WR64(target.before); /* target cinfo.before */ WR64(target.after); /* target cinfo.after */ } inode_free(src_dir); if (src_dir != target_dir) inode_free(target_dir); inode_free(old_file); inode_free(new_file); return status; out_abort: if (txn->abort(txn)) dbenv->err(dbenv, rc, "DB_ENV->txn_abort"); goto out; }
nfsstat4 nfs_op_remove(struct nfs_cxn *cxn, const REMOVE4args *args, struct list_head *writes, struct rpc_write **wr) { nfsstat4 status = NFS4_OK; struct nfs_inode *dir_ino = NULL, *target_ino = NULL; struct nfs_buf target; change_info4 cinfo = { true, 0, 0 }; DB_TXN *txn = NULL; DB_ENV *dbenv = srv.fsdb.env; int rc; nfsino_t de_inum; target.len = args->target.utf8string_len; target.val = args->target.utf8string_val; if (debugging) applog(LOG_INFO, "op REMOVE ('%.*s')", target.len, target.val); if (target.len > SRV_MAX_NAME) { status = NFS4ERR_NAMETOOLONG; goto out; } if (!valid_utf8string(&target)) { status = NFS4ERR_INVAL; goto out; } if (has_dots(&target)) { status = NFS4ERR_BADNAME; goto out; } rc = dbenv->txn_begin(dbenv, NULL, &txn, 0); if (rc) { status = NFS4ERR_IO; dbenv->err(dbenv, rc, "DB_ENV->txn_begin"); goto out; } /* reference container directory */ status = dir_curfh(txn, cxn, &dir_ino, DB_RMW); if (status != NFS4_OK) goto out_abort; /* lookup target name in directory */ status = dir_lookup(txn, dir_ino, &target, 0, &de_inum); if (status != NFS4_OK) goto out_abort; /* reference target inode */ target_ino = inode_getdec(txn, de_inum, DB_RMW); if (!target_ino) { status = NFS4ERR_NOENT; goto out_abort; } /* prevent root dir deletion */ if (target_ino->inum == INO_ROOT) { status = NFS4ERR_INVAL; goto out_abort; } /* prevent removal of non-empty dirs */ if ((target_ino->type == NF4DIR) && !dir_is_empty(txn, target_ino)) { status = NFS4ERR_NOTEMPTY; goto out_abort; } /* remove target inode from directory */ rc = fsdb_dirent_del(&srv.fsdb, txn, dir_ino->inum, &target, 0); if (rc) { status = NFS4ERR_IO; goto out_abort; } /* record directory change info */ cinfo.before = dir_ino->version; rc = inode_touch(txn, dir_ino); if (rc) { status = NFS4ERR_IO; goto out_abort; } cinfo.after = dir_ino->version; /* remove link, possibly deleting inode */ rc = inode_unlink(txn, target_ino); if (rc) { status = NFS4ERR_IO; goto out_abort; } rc = txn->commit(txn, 0); if (rc) { dbenv->err(dbenv, rc, "DB_ENV->txn_commit"); status = NFS4ERR_IO; goto out; } out: WR32(status); if (status == NFS4_OK) { WR32(cinfo.atomic ? 1 : 0); /* cinfo.atomic */ WR64(cinfo.before); /* cinfo.before */ WR64(cinfo.after); /* cinfo.after */ } inode_free(dir_ino); inode_free(target_ino); return status; out_abort: if (txn->abort(txn)) dbenv->err(dbenv, rc, "DB_ENV->txn_abort"); goto out; }
nfsstat4 nfs_op_link(struct nfs_cxn *cxn, const LINK4args *args, struct list_head *writes, struct rpc_write **wr) { nfsstat4 status; struct nfs_inode *dir_ino = NULL, *src_ino = NULL; struct nfs_buf newname; uint64_t before = 0, after = 0; DB_TXN *txn; DB_ENV *dbenv = srv.fsdb.env; int rc; newname.len = args->newname.utf8string_len; newname.val = args->newname.utf8string_val; if (debugging) applog(LOG_INFO, "op LINK (%.*s)", newname.len, newname.val); /* verify input parameters */ if (!valid_fh(cxn->current_fh) || !valid_fh(cxn->save_fh)) { status = NFS4ERR_NOFILEHANDLE; goto out; } if (newname.len > SRV_MAX_NAME) { status = NFS4ERR_NAMETOOLONG; goto out; } /* open transaction */ rc = dbenv->txn_begin(dbenv, NULL, &txn, 0); if (rc) { status = NFS4ERR_IO; dbenv->err(dbenv, rc, "DB_ENV->txn_begin"); goto out; } /* read source inode's directory inode */ dir_ino = inode_fhdec(txn, cxn->current_fh, 0); if (!dir_ino) { status = NFS4ERR_NOFILEHANDLE; goto out_abort; } /* make sure target is a directory */ if (dir_ino->type != NF4DIR) { status = NFS4ERR_NOTDIR; goto out_abort; } /* read source inode */ src_ino = inode_fhdec(txn, cxn->save_fh, 0); if (!src_ino) { status = NFS4ERR_NOFILEHANDLE; goto out_abort; } /* make sure source is a not a directory */ if (src_ino->type == NF4DIR) { status = NFS4ERR_ISDIR; goto out_abort; } before = dir_ino->version; /* add directory entry */ status = dir_add(txn, dir_ino, &newname, src_ino); if (status != NFS4_OK) goto out_abort; after = dir_ino->version; /* update source inode */ src_ino->n_link++; if (inode_touch(txn, src_ino)) { status = NFS4ERR_IO; goto out_abort; } /* close transaction */ rc = txn->commit(txn, 0); if (rc) { dbenv->err(dbenv, rc, "DB_ENV->txn_commit"); status = NFS4ERR_IO; goto out; } out: WR32(status); if (status == NFS4_OK) { WR32(1); /* cinfo.atomic */ WR64(before); /* cinfo.before */ WR64(after); /* cinfo.after */ } inode_free(src_ino); inode_free(dir_ino); return status; out_abort: if (txn->abort(txn)) dbenv->err(dbenv, rc, "DB_ENV->txn_abort"); goto out; }
nfsstat4 nfs_op_lookup(struct nfs_cxn *cxn, const LOOKUP4args *args, struct list_head *writes, struct rpc_write **wr) { nfsstat4 status = NFS4_OK; struct nfs_inode *ino = NULL; bool printed = false; struct nfs_buf objname; nfsino_t inum; DB_TXN *txn = NULL; DB_ENV *dbenv = srv.fsdb.env; int rc; objname.len = args->objname.utf8string_len; objname.val = args->objname.utf8string_val; if (!objname.len) { status = NFS4ERR_INVAL; goto out; } if (!objname.val) { status = NFS4ERR_BADXDR; goto out; } if (objname.len > SRV_MAX_NAME) { status = NFS4ERR_NAMETOOLONG; goto out; } rc = dbenv->txn_begin(dbenv, NULL, &txn, 0); if (rc) { status = NFS4ERR_IO; dbenv->err(dbenv, rc, "DB_ENV->txn_begin"); goto out; } status = dir_curfh(txn, cxn, &ino, 0); if (status != NFS4_OK) { if ((status == NFS4ERR_NOTDIR) && (ino->type == NF4LNK)) status = NFS4ERR_SYMLINK; goto out_abort; } status = dir_lookup(txn, ino, &objname, 0, &inum); if (status != NFS4_OK) goto out_abort; rc = txn->commit(txn, 0); if (rc) { dbenv->err(dbenv, rc, "DB_ENV->txn_commit"); status = NFS4ERR_IO; goto out; } fh_set(&cxn->current_fh, inum); if (debugging) { applog(LOG_INFO, "op LOOKUP ('%.*s') -> %016llX", objname.len, objname.val, (unsigned long long) cxn->current_fh.inum); printed = true; } out: if (!printed) { if (debugging) applog(LOG_INFO, "op LOOKUP ('%.*s')", objname.len, objname.val); } WR32(status); inode_free(ino); return status; out_abort: if (txn->abort(txn)) dbenv->err(dbenv, rc, "DB_ENV->txn_abort"); goto out; }