int affs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { struct super_block *sb = old_dir->i_sb; struct buffer_head *bh = NULL; int retval; pr_debug("%s(old=%lu,\"%pd\" to new=%lu,\"%pd\")\n", __func__, old_dir->i_ino, old_dentry, new_dir->i_ino, new_dentry); retval = affs_check_name(new_dentry->d_name.name, new_dentry->d_name.len, affs_nofilenametruncate(old_dentry)); if (retval) return retval; /* Unlink destination if it already exists */ if (new_dentry->d_inode) { retval = affs_remove_header(new_dentry); if (retval) return retval; } bh = affs_bread(sb, old_dentry->d_inode->i_ino); if (!bh) return -EIO; /* Remove header from its parent directory. */ affs_lock_dir(old_dir); retval = affs_remove_hash(old_dir, bh); affs_unlock_dir(old_dir); if (retval) goto done; /* And insert it into the new directory with the new name. */ affs_copy_name(AFFS_TAIL(sb, bh)->name, new_dentry); affs_fix_checksum(sb, bh); affs_lock_dir(new_dir); retval = affs_insert_hash(new_dir, bh); affs_unlock_dir(new_dir); /* TODO: move it back to old_dir, if error? */ done: mark_buffer_dirty_inode(bh, retval ? old_dir : new_dir); affs_brelse(bh); return retval; }
struct dentry * affs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { struct super_block *sb = dir->i_sb; struct buffer_head *bh; struct inode *inode = NULL; pr_debug("AFFS: lookup(\"%.*s\")\n",(int)dentry->d_name.len,dentry->d_name.name); affs_lock_dir(dir); bh = affs_find_entry(dir, dentry); affs_unlock_dir(dir); if (IS_ERR(bh)) return ERR_CAST(bh); if (bh) { u32 ino = bh->b_blocknr; /* store the real header ino in d_fsdata for faster lookups */ dentry->d_fsdata = (void *)(long)ino; switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) { //link to dirs disabled //case ST_LINKDIR: case ST_LINKFILE: ino = be32_to_cpu(AFFS_TAIL(sb, bh)->original); } affs_brelse(bh); inode = affs_iget(sb, ino); if (IS_ERR(inode)) return ERR_PTR(PTR_ERR(inode)); } dentry->d_op = AFFS_SB(sb)->s_flags & SF_INTL ? &affs_intl_dentry_operations : &affs_dentry_operations; d_add(dentry, inode); return NULL; }
struct dentry * affs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { struct super_block *sb = dir->i_sb; struct buffer_head *bh; struct inode *inode = NULL; pr_debug("%s(\"%pd\")\n", __func__, dentry); affs_lock_dir(dir); bh = affs_find_entry(dir, dentry); affs_unlock_dir(dir); if (IS_ERR(bh)) return ERR_CAST(bh); if (bh) { u32 ino = bh->b_blocknr; /* store the real header ino in d_fsdata for faster lookups */ dentry->d_fsdata = (void *)(long)ino; switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) { //link to dirs disabled //case ST_LINKDIR: case ST_LINKFILE: ino = be32_to_cpu(AFFS_TAIL(sb, bh)->original); } affs_brelse(bh); inode = affs_iget(sb, ino); if (IS_ERR(inode)) return ERR_CAST(inode); } d_add(dentry, inode); return NULL; }
static int affs_readdir(struct file *filp, void *dirent, filldir_t filldir) { struct inode *inode = filp->f_path.dentry->d_inode; struct super_block *sb = inode->i_sb; struct buffer_head *dir_bh; struct buffer_head *fh_bh; unsigned char *name; int namelen; u32 i; int hash_pos; int chain_pos; u32 f_pos; u32 ino; int stored; int res; pr_debug("AFFS: readdir(ino=%lu,f_pos=%lx)\n",inode->i_ino,(unsigned long)filp->f_pos); stored = 0; res = -EIO; dir_bh = NULL; fh_bh = NULL; f_pos = filp->f_pos; if (f_pos == 0) { filp->private_data = (void *)0; if (filldir(dirent, ".", 1, f_pos, inode->i_ino, DT_DIR) < 0) return 0; filp->f_pos = f_pos = 1; stored++; } if (f_pos == 1) { if (filldir(dirent, "..", 2, f_pos, parent_ino(filp->f_path.dentry), DT_DIR) < 0) return stored; filp->f_pos = f_pos = 2; stored++; } affs_lock_dir(inode); chain_pos = (f_pos - 2) & 0xffff; hash_pos = (f_pos - 2) >> 16; if (chain_pos == 0xffff) { affs_warning(sb, "readdir", "More than 65535 entries in chain"); chain_pos = 0; hash_pos++; filp->f_pos = ((hash_pos << 16) | chain_pos) + 2; } dir_bh = affs_bread(sb, inode->i_ino); if (!dir_bh) goto readdir_out; ino = (u32)(long)filp->private_data; if (ino && filp->f_version == inode->i_version) { pr_debug("AFFS: readdir() left off=%d\n", ino); goto inside; } ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]); for (i = 0; ino && i < chain_pos; i++) { fh_bh = affs_bread(sb, ino); if (!fh_bh) { affs_error(sb, "readdir","Cannot read block %d", i); goto readdir_out; } ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain); affs_brelse(fh_bh); fh_bh = NULL; } if (ino) goto inside; hash_pos++; for (; hash_pos < AFFS_SB(sb)->s_hashsize; hash_pos++) { ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]); if (!ino) continue; f_pos = (hash_pos << 16) + 2; inside: do { fh_bh = affs_bread(sb, ino); if (!fh_bh) { affs_error(sb, "readdir","Cannot read block %d", ino); goto readdir_done; } namelen = min(AFFS_TAIL(sb, fh_bh)->name[0], (u8)30); name = AFFS_TAIL(sb, fh_bh)->name + 1; pr_debug("AFFS: readdir(): filldir(\"%.*s\", ino=%u), hash=%d, f_pos=%x\n", namelen, name, ino, hash_pos, f_pos); if (filldir(dirent, name, namelen, f_pos, ino, DT_UNKNOWN) < 0) goto readdir_done; stored++; f_pos++; ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain); affs_brelse(fh_bh); fh_bh = NULL; } while (ino); } readdir_done: filp->f_pos = f_pos; filp->f_version = inode->i_version; filp->private_data = (void *)(long)ino; res = stored; readdir_out: affs_brelse(dir_bh); affs_brelse(fh_bh); affs_unlock_dir(inode); pr_debug("AFFS: readdir()=%d\n", stored); return res; }
int affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s32 type) { struct super_block *sb = dir->i_sb; struct buffer_head *inode_bh = NULL; struct buffer_head *bh = NULL; u32 block = 0; int retval; pr_debug("AFFS: add_entry(dir=%u, inode=%u, \"%*s\", type=%d)\n", (u32)dir->i_ino, (u32)inode->i_ino, (int)dentry->d_name.len, dentry->d_name.name, type); retval = -EIO; bh = affs_bread(sb, inode->i_ino); if (!bh) goto done; affs_lock_link(inode); switch (type) { case ST_LINKFILE: case ST_LINKDIR: retval = -ENOSPC; block = affs_alloc_block(dir, dir->i_ino); if (!block) goto err; retval = -EIO; inode_bh = bh; bh = affs_getzeroblk(sb, block); if (!bh) goto err; break; default: break; } AFFS_HEAD(bh)->ptype = cpu_to_be32(T_SHORT); AFFS_HEAD(bh)->key = cpu_to_be32(bh->b_blocknr); affs_copy_name(AFFS_TAIL(sb, bh)->name, dentry); AFFS_TAIL(sb, bh)->stype = cpu_to_be32(type); AFFS_TAIL(sb, bh)->parent = cpu_to_be32(dir->i_ino); if (inode_bh) { __be32 chain; chain = AFFS_TAIL(sb, inode_bh)->link_chain; AFFS_TAIL(sb, bh)->original = cpu_to_be32(inode->i_ino); AFFS_TAIL(sb, bh)->link_chain = chain; AFFS_TAIL(sb, inode_bh)->link_chain = cpu_to_be32(block); affs_adjust_checksum(inode_bh, block - be32_to_cpu(chain)); mark_buffer_dirty_inode(inode_bh, inode); inode->i_nlink = 2; atomic_inc(&inode->i_count); } affs_fix_checksum(sb, bh); mark_buffer_dirty_inode(bh, inode); dentry->d_fsdata = (void *)(long)bh->b_blocknr; affs_lock_dir(dir); retval = affs_insert_hash(dir, bh); mark_buffer_dirty_inode(bh, inode); affs_unlock_dir(dir); affs_unlock_link(inode); d_instantiate(dentry, inode); done: affs_brelse(inode_bh); affs_brelse(bh); return retval; err: if (block) affs_free_block(sb, block); affs_unlock_link(inode); goto done; }
static int affs_readdir(struct file *file, struct dir_context *ctx) { struct inode *inode = file_inode(file); struct super_block *sb = inode->i_sb; struct buffer_head *dir_bh = NULL; struct buffer_head *fh_bh = NULL; unsigned char *name; int namelen; u32 i; int hash_pos; int chain_pos; u32 ino; int error = 0; pr_debug("%s(ino=%lu,f_pos=%llx)\n", __func__, inode->i_ino, ctx->pos); if (ctx->pos < 2) { file->private_data = (void *)0; if (!dir_emit_dots(file, ctx)) return 0; } affs_lock_dir(inode); chain_pos = (ctx->pos - 2) & 0xffff; hash_pos = (ctx->pos - 2) >> 16; if (chain_pos == 0xffff) { affs_warning(sb, "readdir", "More than 65535 entries in chain"); chain_pos = 0; hash_pos++; ctx->pos = ((hash_pos << 16) | chain_pos) + 2; } dir_bh = affs_bread(sb, inode->i_ino); if (!dir_bh) goto out_unlock_dir; /* If the directory hasn't changed since the last call to readdir(), * we can jump directly to where we left off. */ ino = (u32)(long)file->private_data; if (ino && file->f_version == inode->i_version) { pr_debug("readdir() left off=%d\n", ino); goto inside; } ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]); for (i = 0; ino && i < chain_pos; i++) { fh_bh = affs_bread(sb, ino); if (!fh_bh) { affs_error(sb, "readdir","Cannot read block %d", i); error = -EIO; goto out_brelse_dir; } ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain); affs_brelse(fh_bh); fh_bh = NULL; } if (ino) goto inside; hash_pos++; for (; hash_pos < AFFS_SB(sb)->s_hashsize; hash_pos++) { ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[hash_pos]); if (!ino) continue; ctx->pos = (hash_pos << 16) + 2; inside: do { fh_bh = affs_bread(sb, ino); if (!fh_bh) { affs_error(sb, "readdir", "Cannot read block %d", ino); break; } namelen = min(AFFS_TAIL(sb, fh_bh)->name[0], (u8)AFFSNAMEMAX); name = AFFS_TAIL(sb, fh_bh)->name + 1; pr_debug("readdir(): dir_emit(\"%.*s\", ino=%u), hash=%d, f_pos=%llx\n", namelen, name, ino, hash_pos, ctx->pos); if (!dir_emit(ctx, name, namelen, ino, DT_UNKNOWN)) goto done; ctx->pos++; ino = be32_to_cpu(AFFS_TAIL(sb, fh_bh)->hash_chain); affs_brelse(fh_bh); fh_bh = NULL; } while (ino); } done: file->f_version = inode->i_version; file->private_data = (void *)(long)ino; affs_brelse(fh_bh); out_brelse_dir: affs_brelse(dir_bh); out_unlock_dir: affs_unlock_dir(inode); return error; }
int affs_remove_header(struct dentry *dentry) { struct super_block *sb; struct inode *inode, *dir; struct buffer_head *bh = NULL; int retval; dir = dentry->d_parent->d_inode; sb = dir->i_sb; retval = -ENOENT; inode = dentry->d_inode; if (!inode) goto done; pr_debug("AFFS: remove_header(key=%ld)\n", inode->i_ino); retval = -EIO; bh = affs_bread(sb, (u32)(long)dentry->d_fsdata); if (!bh) goto done; affs_lock_link(inode); affs_lock_dir(dir); switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) { case ST_USERDIR: /* if we ever want to support links to dirs * i_hash_lock of the inode must only be * taken after some checks */ affs_lock_dir(inode); retval = affs_empty_dir(inode); affs_unlock_dir(inode); if (retval) goto done_unlock; break; default: break; } retval = affs_remove_hash(dir, bh); if (retval) goto done_unlock; mark_buffer_dirty_inode(bh, inode); affs_unlock_dir(dir); if (inode->i_nlink > 1) retval = affs_remove_link(dentry); else inode->i_nlink = 0; affs_unlock_link(inode); inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); done: affs_brelse(bh); return retval; done_unlock: affs_unlock_dir(dir); affs_unlock_link(inode); goto done; }
static int affs_remove_link(struct dentry *dentry) { struct inode *dir, *inode = dentry->d_inode; struct super_block *sb = inode->i_sb; struct buffer_head *bh = NULL, *link_bh = NULL; u32 link_ino, ino; int retval; pr_debug("AFFS: remove_link(key=%ld)\n", inode->i_ino); retval = -EIO; bh = affs_bread(sb, inode->i_ino); if (!bh) goto done; link_ino = (u32)(long)dentry->d_fsdata; if (inode->i_ino == link_ino) { /* we can't remove the head of the link, as its blocknr is still used as ino, * so we remove the block of the first link instead. */ link_ino = be32_to_cpu(AFFS_TAIL(sb, bh)->link_chain); link_bh = affs_bread(sb, link_ino); if (!link_bh) goto done; dir = affs_iget(sb, be32_to_cpu(AFFS_TAIL(sb, link_bh)->parent)); if (IS_ERR(dir)) { retval = PTR_ERR(dir); goto done; } affs_lock_dir(dir); affs_fix_dcache(dentry, link_ino); retval = affs_remove_hash(dir, link_bh); if (retval) { affs_unlock_dir(dir); goto done; } mark_buffer_dirty_inode(link_bh, inode); memcpy(AFFS_TAIL(sb, bh)->name, AFFS_TAIL(sb, link_bh)->name, 32); retval = affs_insert_hash(dir, bh); if (retval) { affs_unlock_dir(dir); goto done; } mark_buffer_dirty_inode(bh, inode); affs_unlock_dir(dir); iput(dir); } else { link_bh = affs_bread(sb, link_ino); if (!link_bh) goto done; } while ((ino = be32_to_cpu(AFFS_TAIL(sb, bh)->link_chain)) != 0) { if (ino == link_ino) { __be32 ino2 = AFFS_TAIL(sb, link_bh)->link_chain; AFFS_TAIL(sb, bh)->link_chain = ino2; affs_adjust_checksum(bh, be32_to_cpu(ino2) - link_ino); mark_buffer_dirty_inode(bh, inode); retval = 0; /* Fix the link count, if bh is a normal header block without links */ switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) { case ST_LINKDIR: case ST_LINKFILE: break; default: if (!AFFS_TAIL(sb, bh)->link_chain) inode->i_nlink = 1; } affs_free_block(sb, link_ino); goto done; } affs_brelse(bh); bh = affs_bread(sb, ino); if (!bh) goto done; } retval = -ENOENT; done: affs_brelse(link_bh); affs_brelse(bh); return retval; }