int fs_rmdir(const char *path) { struct inode *dir; struct dirent *dent; uint32_t nblock, i, j; char *blk; int r; if ((r = inode_open(path, &dir)) < 0) return r; if (dir == diskaddr(super->s_root)) return -EPERM; if (!S_ISDIR(dir->i_mode)) return -ENOTDIR; nblock = dir->i_size / BLKSIZE; for (i = 0; i < nblock; i++) { if ((r = inode_get_block(dir, i, &blk)) < 0) return r; dent = (struct dirent *)blk; for (j = 0; j < BLKDIRENTS; ++j) if (dent[j].d_name[0] != '\0') return -ENOTEMPTY; } return inode_unlink(path); }
int fs_rename(const char *srcpath, const char *dstpath) { int r; link_retry: if ((r = inode_link(srcpath, dstpath)) < 0) switch(-r) { case EEXIST: if (strcmp(srcpath, dstpath) == 0) return 0; if ((r = inode_unlink(dstpath)) < 0) return r; goto link_retry; default: return r; } return inode_unlink(srcpath); }
int fs_unlink(const char *path) { struct inode *ino; int r; if ((r = inode_open(path, &ino)) < 0) return r; if (S_ISDIR(ino->i_mode)) return -EISDIR; ino->i_ctime = time(NULL); return inode_unlink(path); }
int inode_remove(FAR const char *path) { const char *name = path; FAR struct inode *node; FAR struct inode *left; FAR struct inode *parent; if (!*path || path[0] != '/') { return -EINVAL; } /* Find the node to delete */ node = inode_search(&name, &left, &parent, (const char **)NULL); if (node) { /* Found it, now remove it from the tree */ inode_unlink(node, left, parent); /* We cannot delete it if there reference to the inode */ if (node->i_crefs) { /* In that case, we will mark it deleted, when the FS * releases the inode, we will then, finally delete * the subtree. */ node->i_flags |= FSNODEFLAG_DELETED; return -EBUSY; } else { /* And delete it now -- recursively to delete all of its children */ inode_free(node->i_child); kfree(node); return OK; } } /* The node does not exist or it has references */ return -ENOENT; }
int inode_remove(FAR const char *path) { FAR struct inode *node; /* Find the inode and unlink it from the in-memory inode tree */ node = inode_unlink(path); if (node) { /* Found it! But we cannot delete the inode if there are references * to it */ if (node->i_crefs) { /* In that case, we will mark it deleted, when the filesystem * releases the inode, we will then, finally delete the subtree */ node->i_flags |= FSNODEFLAG_DELETED; return -EBUSY; } else { /* And delete it now -- recursively to delete all of its children. * Since it has been unlinked, then the peer pointer should be NULL. */ DEBUGASSERT(node->i_peer == NULL); inode_free(node); return OK; } } /* The node does not exist */ return -ENOENT; }
int fs_symlink(const char *dstpath, const char *srcpath) { struct inode *ino; struct fuse_context *ctxt; time_t curtime; size_t dstlen; char *blk; int r; if ((dstlen = strlen(dstpath)) >= PATH_MAX) return -ENAMETOOLONG; if ((r = inode_create(srcpath, &ino)) < 0) { return r; } ino->i_size = dstlen; ino->i_mode = S_IFLNK | 0777; ino->i_nlink = 1; curtime = time(NULL); ino->i_atime = curtime; ino->i_ctime = curtime; ino->i_mtime = curtime; ctxt = fuse_get_context(); ino->i_owner = ctxt->uid; ino->i_group = ctxt->gid; if ((r = inode_get_block(ino, 0, &blk)) < 0) { inode_unlink(srcpath); return r; } memcpy(blk, dstpath, dstlen); inode_flush(ino); return 0; }
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; }
void fs_test(void) { struct inode *ino, *ino2; int r; char *blk; uint32_t bits[4096]; // back up bitmap memmove(bits, bitmap, 4096); // allocate block if ((r = alloc_block()) < 0) panic("alloc_block: %s", strerror(-r)); // check that block was free assert(bits[r/32] & (1 << (r%32))); // and is not free any more assert(!(bitmap[r/32] & (1 << (r%32)))); free_block(r); printf("alloc_block is good\n"); if ((r = inode_open("/not-found", &ino)) < 0 && r != -ENOENT) panic("inode_open /not-found: %s", strerror(-r)); else if (r == 0) panic("inode_open /not-found succeeded!"); if ((r = inode_open("/msg", &ino)) < 0) panic("inode_open /msg: %s", strerror(-r)); printf("inode_open is good\n"); if ((r = inode_get_block(ino, 0, &blk)) < 0) panic("inode_get_block: %s", strerror(-r)); if (strcmp(blk, msg) != 0) panic("inode_get_block returned wrong data"); printf("inode_get_block is good\n"); if ((r = inode_set_size(ino, 0)) < 0) panic("inode_set_size: %s", strerror(-r)); assert(ino->i_direct[0] == 0); printf("inode_truncate is good\n"); if ((r = inode_set_size(ino, strlen(msg))) < 0) panic("inode_set_size 2: %s", strerror(-r)); if ((r = inode_get_block(ino, 0, &blk)) < 0) panic("inode_get_block 2: %s", strerror(-r)); strcpy(blk, msg); printf("file rewrite is good\n"); if ((r = inode_link("/msg", "/linkmsg")) < 0) panic("inode_link /msg /linkmsg: %s", strerror(-r)); if ((r = inode_open("/msg", &ino)) < 0) panic("inode_open /msg: %s", strerror(-r)); if ((r = inode_open("/linkmsg", &ino2)) < 0) panic("inode_open /linkmsg: %s", strerror(-r)); if (ino != ino2) panic("linked files do not point to same inode"); if (ino->i_nlink != 2) panic("link count incorrect: %u, expected 2", ino->i_nlink); printf("inode_link is good\n"); if ((r = inode_unlink("/linkmsg")) < 0) panic("inode_unlink /linkmsg: %s", strerror(-r)); if ((r = inode_open("/linkmsg", &ino2)) < 0 && r != -ENOENT) panic("inode_open /linkmsg after unlink: %s", strerror(-r)); else if (r == 0) panic("inode_open /linkmsg after unlink succeeded!"); if ((r = inode_open("/msg", &ino)) < 0) panic("inode_open /msg after /linkmsg unlinked: %s", strerror(-r)); if (ino->i_nlink != 1) panic("link count incorrect: %u, expected 1", ino->i_nlink); printf("inode_unlink is good\n"); }