static inline void truncate (struct inode * inode) { struct super_block *sb = inode->i_sb; block_t *idata = i_data(inode); int offsets[DEPTH]; Indirect chain[DEPTH]; Indirect *partial; block_t nr = 0; int n; int first_whole; long iblock; iblock = (inode->i_size + sb->s_blocksize -1) >> sb->s_blocksize_bits; block_truncate_page(inode->i_mapping, inode->i_size, get_block); n = block_to_path(inode, iblock, offsets); if (!n) return; if (n == 1) { free_data(inode, idata+offsets[0], idata + DIRECT); first_whole = 0; goto do_indirects; } first_whole = offsets[0] + 1 - DIRECT; partial = find_shared(inode, n, offsets, chain, &nr); if (nr) { if (partial == chain) mark_inode_dirty(inode); else mark_buffer_dirty_inode(partial->bh, inode); free_branches(inode, &nr, &nr+1, (chain+n-1) - partial); } /* Clear the ends of indirect blocks on the shared branch */ while (partial > chain) { free_branches(inode, partial->p + 1, block_end(partial->bh), (chain+n-1) - partial); mark_buffer_dirty_inode(partial->bh, inode); brelse (partial->bh); partial--; } do_indirects: /* Kill the remaining (whole) subtrees */ while (first_whole < DEPTH-1) { nr = idata[DIRECT+first_whole]; if (nr) { idata[DIRECT+first_whole] = 0; mark_inode_dirty(inode); free_branches(inode, &nr, &nr+1, first_whole+1); } first_whole++; } inode->i_mtime = inode->i_ctime = current_time(inode); mark_inode_dirty(inode); }
int __ext4_handle_dirty_metadata(const char *where, unsigned int line, handle_t *handle, struct inode *inode, struct buffer_head *bh) { int err = 0; if (ext4_handle_valid(handle)) { err = jbd2_journal_dirty_metadata(handle, bh); if (err) { /* Errors can only happen if there is a bug */ handle->h_err = err; __ext4_journal_stop(where, line, handle); } } else { if (inode) mark_buffer_dirty_inode(bh, inode); else mark_buffer_dirty(bh); if (inode && inode_needs_sync(inode)) { sync_dirty_buffer(bh); if (buffer_req(bh) && !buffer_uptodate(bh)) { struct ext4_super_block *es; es = EXT4_SB(inode->i_sb)->s_es; es->s_last_error_block = cpu_to_le64(bh->b_blocknr); ext4_error_inode(inode, where, line, bh->b_blocknr, "IO error syncing itable block"); err = -EIO; } } } return err; }
static inline void dirty_indirect(struct buffer_head *bh, struct inode *inode) { mark_buffer_dirty_inode(bh, inode); if (IS_SYNC(inode)) { ll_rw_block (WRITE, 1, &bh); wait_on_buffer (bh); } }
int __ext4_handle_dirty_metadata(const char *where, unsigned int line, handle_t *handle, struct inode *inode, struct buffer_head *bh) { int err = 0; might_sleep(); set_buffer_meta(bh); set_buffer_prio(bh); if (ext4_handle_valid(handle)) { err = jbd2_journal_dirty_metadata(handle, bh); /* Errors can only happen if there is a bug */ if (WARN_ON_ONCE(err)) { ext4_journal_abort_handle(where, line, __func__, bh, handle, err); if (inode == NULL) { pr_err("EXT4: jbd2_journal_dirty_metadata " "failed: handle type %u started at " "line %u, credits %u/%u, errcode %d", handle->h_type, handle->h_line_no, handle->h_requested_credits, handle->h_buffer_credits, err); return err; } ext4_error_inode(inode, where, line, bh->b_blocknr, "journal_dirty_metadata failed: " "handle type %u started at line %u, " "credits %u/%u, errcode %d", handle->h_type, handle->h_line_no, handle->h_requested_credits, handle->h_buffer_credits, err); } } else { if (inode) mark_buffer_dirty_inode(bh, inode); else mark_buffer_dirty(bh); if (inode && inode_needs_sync(inode)) { sync_dirty_buffer(bh); if (buffer_req(bh) && !buffer_uptodate(bh)) { struct ext4_super_block *es; es = EXT4_SB(inode->i_sb)->s_es; es->s_last_error_block = cpu_to_le64(bh->b_blocknr); ext4_error_inode(inode, where, line, bh->b_blocknr, "IO error syncing itable block"); err = -EIO; } } } return err; }
struct inode * affs_new_inode(struct inode *dir) { struct super_block *sb = dir->i_sb; struct inode *inode; u32 block; struct buffer_head *bh; if (!(inode = new_inode(sb))) goto err_inode; if (!(block = affs_alloc_block(dir, dir->i_ino))) goto err_block; bh = affs_getzeroblk(sb, block); if (!bh) goto err_bh; mark_buffer_dirty_inode(bh, inode); affs_brelse(bh); inode->i_uid = current_fsuid(); inode->i_gid = current_fsgid(); inode->i_ino = block; inode->i_nlink = 1; inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC; atomic_set(&AFFS_I(inode)->i_opencnt, 0); AFFS_I(inode)->i_blkcnt = 0; AFFS_I(inode)->i_lc = NULL; AFFS_I(inode)->i_lc_size = 0; AFFS_I(inode)->i_lc_shift = 0; AFFS_I(inode)->i_lc_mask = 0; AFFS_I(inode)->i_ac = NULL; AFFS_I(inode)->i_ext_bh = NULL; AFFS_I(inode)->mmu_private = 0; AFFS_I(inode)->i_protect = 0; AFFS_I(inode)->i_lastalloc = 0; AFFS_I(inode)->i_pa_cnt = 0; AFFS_I(inode)->i_extcnt = 1; AFFS_I(inode)->i_ext_last = ~1; insert_inode_hash(inode); return inode; err_bh: affs_free_block(sb, block); err_block: iput(inode); err_inode: return NULL; }
int __ext4bf_handle_dirty_metadata(const char *where, unsigned int line, handle_t *handle, struct inode *inode, struct buffer_head *bh) { int err = 0; if (ext4bf_handle_valid(handle)) { #ifdef DCHECKSUM /* ext4bf: handle cases where it is a data block. */ if (bh && bh->b_blocktype == B_BLOCKTYPE_DATA) { #endif #ifdef PARTJ if (!buffer_new(bh)) err = jbdbf_journal_dirty_metadata(handle, bh); else #endif #ifdef DCHECKSUM jbdbf_journal_dirty_data(handle, bh); } else #endif err = jbdbf_journal_dirty_metadata(handle, bh); if (err) { /* Errors can only happen if there is a bug */ handle->h_err = err; __ext4bf_journal_stop(where, line, handle); } } else { if (inode) mark_buffer_dirty_inode(bh, inode); else mark_buffer_dirty(bh); if (inode && inode_needs_sync(inode)) { sync_dirty_buffer(bh); if (buffer_req(bh) && !buffer_uptodate(bh)) { struct ext4bf_super_block *es; es = EXT4_SB(inode->i_sb)->s_es; es->s_last_error_block = cpu_to_le64(bh->b_blocknr); ext4bf_error_inode(inode, where, line, bh->b_blocknr, "IO error syncing itable block"); err = -EIO; } } } return err; }
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; }
int affs_write_inode(struct inode *inode, struct writeback_control *wbc) { struct super_block *sb = inode->i_sb; struct buffer_head *bh; struct affs_tail *tail; uid_t uid; gid_t gid; pr_debug("write_inode(%lu)\n", inode->i_ino); if (!inode->i_nlink) // possibly free block return 0; bh = affs_bread(sb, inode->i_ino); if (!bh) { affs_error(sb,"write_inode","Cannot read block %lu",inode->i_ino); return -EIO; } tail = AFFS_TAIL(sb, bh); if (tail->stype == cpu_to_be32(ST_ROOT)) { affs_secs_to_datestamp(inode->i_mtime.tv_sec, &AFFS_ROOT_TAIL(sb, bh)->root_change); } else { tail->protect = cpu_to_be32(AFFS_I(inode)->i_protect); tail->size = cpu_to_be32(inode->i_size); affs_secs_to_datestamp(inode->i_mtime.tv_sec, &tail->change); if (!(inode->i_ino == AFFS_SB(sb)->s_root_block)) { uid = i_uid_read(inode); gid = i_gid_read(inode); if (affs_test_opt(AFFS_SB(sb)->s_flags, SF_MUFS)) { if (uid == 0 || uid == 0xFFFF) uid = uid ^ ~0; if (gid == 0 || gid == 0xFFFF) gid = gid ^ ~0; } if (!affs_test_opt(AFFS_SB(sb)->s_flags, SF_SETUID)) tail->uid = cpu_to_be16(uid); if (!affs_test_opt(AFFS_SB(sb)->s_flags, SF_SETGID)) tail->gid = cpu_to_be16(gid); } } affs_fix_checksum(sb, bh); mark_buffer_dirty_inode(bh, inode); affs_brelse(bh); affs_free_prealloc(inode); return 0; }
int affs_remove_hash(struct inode *dir, struct buffer_head *rem_bh) { struct super_block *sb; struct buffer_head *bh; u32 rem_ino, hash_ino; __be32 ino; int offset, retval; sb = dir->i_sb; rem_ino = rem_bh->b_blocknr; offset = affs_hash_name(sb, AFFS_TAIL(sb, rem_bh)->name+1, AFFS_TAIL(sb, rem_bh)->name[0]); pr_debug("AFFS: remove_hash(dir=%d, ino=%d, hashval=%d)\n", (u32)dir->i_ino, rem_ino, offset); bh = affs_bread(sb, dir->i_ino); if (!bh) return -EIO; retval = -ENOENT; hash_ino = be32_to_cpu(AFFS_HEAD(bh)->table[offset]); while (hash_ino) { if (hash_ino == rem_ino) { ino = AFFS_TAIL(sb, rem_bh)->hash_chain; if (dir->i_ino == bh->b_blocknr) AFFS_HEAD(bh)->table[offset] = ino; else AFFS_TAIL(sb, bh)->hash_chain = ino; affs_adjust_checksum(bh, be32_to_cpu(ino) - hash_ino); mark_buffer_dirty_inode(bh, dir); AFFS_TAIL(sb, rem_bh)->parent = 0; retval = 0; break; } affs_brelse(bh); bh = affs_bread(sb, hash_ino); if (!bh) return -EIO; hash_ino = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain); } affs_brelse(bh); dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; dir->i_version++; mark_inode_dirty(dir); return retval; }
int qnx4_rmdir(struct inode *dir, struct dentry *dentry) { struct buffer_head *bh; struct qnx4_inode_entry *de; struct inode *inode; int retval; int ino; QNX4DEBUG(("qnx4: qnx4_rmdir [%s]\n", dentry->d_name.name)); lock_kernel(); bh = qnx4_find_entry(dentry->d_name.len, dir, dentry->d_name.name, &de, &ino); if (bh == NULL) { unlock_kernel(); return -ENOENT; } inode = dentry->d_inode; if (inode->i_ino != ino) { retval = -EIO; goto end_rmdir; } #if 0 if (!empty_dir(inode)) { retval = -ENOTEMPTY; goto end_rmdir; } #endif if (inode->i_nlink != 2) { QNX4DEBUG(("empty directory has nlink!=2 (%d)\n", inode->i_nlink)); } QNX4DEBUG(("qnx4: deleting directory\n")); de->di_status = 0; memset(de->di_fname, 0, sizeof de->di_fname); de->di_mode = 0; mark_buffer_dirty_inode(bh, dir); clear_nlink(inode); mark_inode_dirty(inode); inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC; inode_dec_link_count(dir); retval = 0; end_rmdir: brelse(bh); unlock_kernel(); return retval; }
int affs_insert_hash(struct inode *dir, struct buffer_head *bh) { struct super_block *sb = dir->i_sb; struct buffer_head *dir_bh; u32 ino, hash_ino; int offset; ino = bh->b_blocknr; offset = affs_hash_name(sb, AFFS_TAIL(sb, bh)->name + 1, AFFS_TAIL(sb, bh)->name[0]); pr_debug("AFFS: insert_hash(dir=%u, ino=%d)\n", (u32)dir->i_ino, ino); dir_bh = affs_bread(sb, dir->i_ino); if (!dir_bh) return -EIO; hash_ino = be32_to_cpu(AFFS_HEAD(dir_bh)->table[offset]); while (hash_ino) { affs_brelse(dir_bh); dir_bh = affs_bread(sb, hash_ino); if (!dir_bh) return -EIO; hash_ino = be32_to_cpu(AFFS_TAIL(sb, dir_bh)->hash_chain); } AFFS_TAIL(sb, bh)->parent = cpu_to_be32(dir->i_ino); AFFS_TAIL(sb, bh)->hash_chain = 0; affs_fix_checksum(sb, bh); if (dir->i_ino == dir_bh->b_blocknr) AFFS_HEAD(dir_bh)->table[offset] = cpu_to_be32(ino); else AFFS_TAIL(sb, dir_bh)->hash_chain = cpu_to_be32(ino); affs_adjust_checksum(dir_bh, ino); mark_buffer_dirty_inode(dir_bh, dir); affs_brelse(dir_bh); dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; dir->i_version++; mark_inode_dirty(dir); return 0; }
int qnx4_unlink(struct inode *dir, struct dentry *dentry) { struct buffer_head *bh; struct qnx4_inode_entry *de; struct inode *inode; int retval; int ino; QNX4DEBUG(("qnx4: qnx4_unlink [%s]\n", dentry->d_name.name)); lock_kernel(); bh = qnx4_find_entry(dentry->d_name.len, dir, dentry->d_name.name, &de, &ino); if (bh == NULL) { unlock_kernel(); return -ENOENT; } inode = dentry->d_inode; if (inode->i_ino != ino) { retval = -EIO; goto end_unlink; } retval = -EPERM; if (!inode->i_nlink) { QNX4DEBUG(("Deleting nonexistent file (%s:%lu), %d\n", inode->i_sb->s_id, inode->i_ino, inode->i_nlink)); inode->i_nlink = 1; } de->di_status = 0; memset(de->di_fname, 0, sizeof de->di_fname); de->di_mode = 0; mark_buffer_dirty_inode(bh, dir); dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC; mark_inode_dirty(dir); inode->i_ctime = dir->i_ctime; inode_dec_link_count(inode); retval = 0; end_unlink: unlock_kernel(); brelse(bh); return retval; }
static int alloc_branch(struct inode *inode, int num, int *offsets, Indirect *branch) { int n = 0; int i; int parent = minix_new_block(inode); printk("parent new block %d\n",parent); branch[0].key = cpu_to_block(parent); if (parent) for (n = 1; n < num; n++) { struct buffer_head *bh; /* Allocate the next block */ int nr = minix_new_block(inode); printk("loop new block %d",nr); if (!nr) break; branch[n].key = cpu_to_block(nr); bh = sb_getblk(inode->i_sb, parent); lock_buffer(bh); memset(bh->b_data, 0, bh->b_size); branch[n].bh = bh; branch[n].p = (block_t*) bh->b_data + offsets[n]; *branch[n].p = branch[n].key; set_buffer_uptodate(bh); unlock_buffer(bh); mark_buffer_dirty_inode(bh, inode); parent = nr; } if (n == num) return 0; /* Allocation failed, free what we already allocated */ for (i = 1; i < n; i++) bforget(branch[i].bh); for (i = 0; i < n; i++) minix_free_block(inode, block_to_cpu(branch[i].key)); return -ENOSPC; }
static inline int splice_branch(struct inode *inode, Indirect chain[DEPTH], Indirect *where, int num) { int i; /* Verify that place we are splicing to is still there and vacant */ /* Writer: pointers */ if (!verify_chain(chain, where-1) || *where->p) /* Writer: end */ goto changed; /* That's it */ *where->p = where->key; /* Writer: end */ /* We are done with atomic stuff, now do the rest of housekeeping */ inode->i_ctime = CURRENT_TIME; /* had we spliced it onto indirect block? */ if (where->bh) mark_buffer_dirty_inode(where->bh, inode); mark_inode_dirty(inode); return 0; changed: for (i = 1; i < num; i++) bforget(where[i].bh); for (i = 0; i < num; i++) minix_free_block(inode, block_to_cpu(where[i].key)); return -EAGAIN; }
static inline int splice_branch(struct inode *inode, Indirect chain[DEPTH], Indirect *where, int num) { int i; write_lock(&pointers_lock); /* */ if (!verify_chain(chain, where-1) || *where->p) goto changed; *where->p = where->key; write_unlock(&pointers_lock); /* */ inode->i_ctime = CURRENT_TIME_SEC; /* */ if (where->bh) mark_buffer_dirty_inode(where->bh, inode); mark_inode_dirty(inode); return 0; changed: write_unlock(&pointers_lock); for (i = 1; i < num; i++) bforget(where[i].bh); for (i = 0; i < num; i++) minix_free_block(inode, block_to_cpu(where[i].key)); return -EAGAIN; }
static inline int splice_branch(struct inode *inode, Indirect chain[DEPTH], Indirect *where, int num) { int i; write_lock(&pointers_lock); /* Verify that place we are splicing to is still there and vacant */ if (!verify_chain(chain, where-1) || *where->p) goto changed; *where->p = where->key; write_unlock(&pointers_lock); /* We are done with atomic stuff, now do the rest of housekeeping */ inode->i_ctime = current_time(inode); /* had we spliced it onto indirect block? */ if (where->bh) mark_buffer_dirty_inode(where->bh, inode); mark_inode_dirty(inode); return 0; changed: write_unlock(&pointers_lock); for (i = 1; i < num; i++) bforget(where[i].bh); for (i = 0; i < num; i++) xiafs_free_block(inode, block_to_cpu(where[i].key)); return -EAGAIN; }
static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name, struct dentry *old_dentry, struct inode *new_dir, unsigned char *new_name, struct dentry *new_dentry, int is_hid) { struct buffer_head *dotdot_bh; struct msdos_dir_entry *dotdot_de; struct inode *old_inode, *new_inode; struct fat_slot_info old_sinfo, sinfo; struct timespec ts; loff_t dotdot_i_pos, new_i_pos; int err, old_attrs, is_dir, update_dotdot, corrupt = 0; old_sinfo.bh = sinfo.bh = dotdot_bh = NULL; old_inode = old_dentry->d_inode; new_inode = new_dentry->d_inode; err = fat_scan(old_dir, old_name, &old_sinfo); if (err) { err = -EIO; goto out; } is_dir = S_ISDIR(old_inode->i_mode); update_dotdot = (is_dir && old_dir != new_dir); if (update_dotdot) { if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de, &dotdot_i_pos) < 0) { err = -EIO; goto out; } } old_attrs = MSDOS_I(old_inode)->i_attrs; err = fat_scan(new_dir, new_name, &sinfo); if (!err) { if (!new_inode) { if (sinfo.de != old_sinfo.de) { err = -EINVAL; goto out; } if (is_hid) MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN; else MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN; if (IS_DIRSYNC(old_dir)) { err = fat_sync_inode(old_inode); if (err) { MSDOS_I(old_inode)->i_attrs = old_attrs; goto out; } } else mark_inode_dirty(old_inode); old_dir->i_version++; old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME_SEC; if (IS_DIRSYNC(old_dir)) (void)fat_sync_inode(old_dir); else mark_inode_dirty(old_dir); goto out; } } ts = CURRENT_TIME_SEC; if (new_inode) { if (err) goto out; if (is_dir) { err = fat_dir_empty(new_inode); if (err) goto out; } new_i_pos = MSDOS_I(new_inode)->i_pos; fat_detach(new_inode); } else { err = msdos_add_entry(new_dir, new_name, is_dir, is_hid, 0, &ts, &sinfo); if (err) goto out; new_i_pos = sinfo.i_pos; } new_dir->i_version++; fat_detach(old_inode); fat_attach(old_inode, new_i_pos); if (is_hid) MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN; else MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN; if (IS_DIRSYNC(new_dir)) { err = fat_sync_inode(old_inode); if (err) goto error_inode; } else mark_inode_dirty(old_inode); if (update_dotdot) { int start = MSDOS_I(new_dir)->i_logstart; dotdot_de->start = cpu_to_le16(start); dotdot_de->starthi = cpu_to_le16(start >> 16); mark_buffer_dirty_inode(dotdot_bh, old_inode); if (IS_DIRSYNC(new_dir)) { err = sync_dirty_buffer(dotdot_bh); if (err) goto error_dotdot; } drop_nlink(old_dir); if (!new_inode) inc_nlink(new_dir); } err = fat_remove_entries(old_dir, &old_sinfo); old_sinfo.bh = NULL; if (err) goto error_dotdot; old_dir->i_version++; old_dir->i_ctime = old_dir->i_mtime = ts; if (IS_DIRSYNC(old_dir)) (void)fat_sync_inode(old_dir); else mark_inode_dirty(old_dir); if (new_inode) { drop_nlink(new_inode); if (is_dir) drop_nlink(new_inode); new_inode->i_ctime = ts; } out: brelse(sinfo.bh); brelse(dotdot_bh); brelse(old_sinfo.bh); return err; error_dotdot: corrupt = 1; if (update_dotdot) { int start = MSDOS_I(old_dir)->i_logstart; dotdot_de->start = cpu_to_le16(start); dotdot_de->starthi = cpu_to_le16(start >> 16); mark_buffer_dirty_inode(dotdot_bh, old_inode); corrupt |= sync_dirty_buffer(dotdot_bh); } error_inode: fat_detach(old_inode); fat_attach(old_inode, old_sinfo.i_pos); MSDOS_I(old_inode)->i_attrs = old_attrs; if (new_inode) { fat_attach(new_inode, new_i_pos); if (corrupt) corrupt |= fat_sync_inode(new_inode); } else { int err2 = fat_remove_entries(new_dir, &sinfo); if (corrupt) corrupt |= err2; sinfo.bh = NULL; } if (corrupt < 0) { fat_fs_error(new_dir->i_sb, "%s: Filesystem corrupted (i_pos %lld)", __func__, sinfo.i_pos); } goto out; }
static int do_vxext_rename(struct inode *old_dir, unsigned char *old_name, struct dentry *old_dentry, struct inode *new_dir, unsigned char *new_name, struct dentry *new_dentry, int is_hid) { struct buffer_head *dotdot_bh; struct msdos_dir_entry *dotdot_de; struct inode *old_inode, *new_inode; struct fat_slot_info old_sinfo, sinfo; struct timespec ts; loff_t new_i_pos; int err, old_attrs, is_dir, update_dotdot, corrupt = 0; old_sinfo.bh = sinfo.bh = dotdot_bh = NULL; old_inode = old_dentry->d_inode; new_inode = new_dentry->d_inode; err = fat_scan(old_dir, old_name, &old_sinfo); if (err) { err = -EIO; goto out; } is_dir = S_ISDIR(old_inode->i_mode); update_dotdot = (is_dir && old_dir != new_dir); if (update_dotdot) { if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de)) { err = -EIO; goto out; } } old_attrs = MSDOS_I(old_inode)->i_attrs; err = fat_scan(new_dir, new_name, &sinfo); if (!err) { if (!new_inode) { /* "foo" -> ".foo" case. just change the ATTR_HIDDEN */ if (sinfo.de != old_sinfo.de) { err = -EINVAL; goto out; } if (is_hid) MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN; else MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN; if (IS_DIRSYNC(old_dir)) { err = fat_sync_inode(old_inode); if (err) { MSDOS_I(old_inode)->i_attrs = old_attrs; goto out; } } else mark_inode_dirty(old_inode); old_dir->i_version++; old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME_SEC; if (IS_DIRSYNC(old_dir)) (void)fat_sync_inode(old_dir); else mark_inode_dirty(old_dir); goto out; } } ts = CURRENT_TIME_SEC; if (new_inode) { if (err) goto out; if (is_dir) { err = fat_dir_empty(new_inode); if (err) goto out; } new_i_pos = MSDOS_I(new_inode)->i_pos; fat_detach(new_inode); } else { err = vxext_add_entry(new_dir, new_name, is_dir, is_hid, 0, &ts, &sinfo); if (err) goto out; new_i_pos = sinfo.i_pos; } new_dir->i_version++; fat_detach(old_inode); fat_attach(old_inode, new_i_pos); if (is_hid) MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN; else MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN; if (IS_DIRSYNC(new_dir)) { err = fat_sync_inode(old_inode); if (err) goto error_inode; } else mark_inode_dirty(old_inode); if (update_dotdot) { fat_set_start(dotdot_de, MSDOS_I(new_dir)->i_logstart); mark_buffer_dirty_inode(dotdot_bh, old_inode); if (IS_DIRSYNC(new_dir)) { err = sync_dirty_buffer(dotdot_bh); if (err) goto error_dotdot; } drop_nlink(old_dir); if (!new_inode) inc_nlink(new_dir); } err = fat_remove_entries(old_dir, &old_sinfo); /* and releases bh */ old_sinfo.bh = NULL; if (err) goto error_dotdot; old_dir->i_version++; old_dir->i_ctime = old_dir->i_mtime = ts; if (IS_DIRSYNC(old_dir)) (void)fat_sync_inode(old_dir); else mark_inode_dirty(old_dir); if (new_inode) { drop_nlink(new_inode); if (is_dir) drop_nlink(new_inode); new_inode->i_ctime = ts; } out: brelse(sinfo.bh); brelse(dotdot_bh); brelse(old_sinfo.bh); return err; error_dotdot: /* data cluster is shared, serious corruption */ corrupt = 1; if (update_dotdot) { fat_set_start(dotdot_de, MSDOS_I(old_dir)->i_logstart); mark_buffer_dirty_inode(dotdot_bh, old_inode); corrupt |= sync_dirty_buffer(dotdot_bh); } error_inode: fat_detach(old_inode); fat_attach(old_inode, old_sinfo.i_pos); MSDOS_I(old_inode)->i_attrs = old_attrs; if (new_inode) { fat_attach(new_inode, new_i_pos); if (corrupt) corrupt |= fat_sync_inode(new_inode); } else { /* * If new entry was not sharing the data cluster, it * shouldn't be serious corruption. */ int err2 = fat_remove_entries(new_dir, &sinfo); if (corrupt) corrupt |= err2; sinfo.bh = NULL; } if (corrupt < 0) { fat_fs_error(new_dir->i_sb, "%s: Filesystem corrupted (i_pos %lld)", __func__, sinfo.i_pos); } goto out; }
int affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { struct super_block *sb = dir->i_sb; struct buffer_head *bh; struct inode *inode; char *p; int i, maxlen, error; char c, lc; pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino, (int)dentry->d_name.len,dentry->d_name.name,symname); maxlen = AFFS_SB(sb)->s_hashsize * sizeof(u32) - 1; inode = affs_new_inode(dir); if (!inode) return -ENOSPC; inode->i_op = &affs_symlink_inode_operations; inode->i_data.a_ops = &affs_symlink_aops; inode->i_mode = S_IFLNK | 0777; mode_to_prot(inode); error = -EIO; bh = affs_bread(sb, inode->i_ino); if (!bh) goto err; i = 0; p = (char *)AFFS_HEAD(bh)->table; lc = '/'; if (*symname == '/') { struct affs_sb_info *sbi = AFFS_SB(sb); while (*symname == '/') symname++; spin_lock(&sbi->symlink_lock); while (sbi->s_volume[i]) /* Cannot overflow */ *p++ = sbi->s_volume[i++]; spin_unlock(&sbi->symlink_lock); } while (i < maxlen && (c = *symname++)) { if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') { *p++ = '/'; i++; symname += 2; lc = '/'; } else if (c == '.' && lc == '/' && *symname == '/') { symname++; lc = '/'; } else { *p++ = c; lc = c; i++; } if (lc == '/') while (*symname == '/') symname++; } *p = 0; mark_buffer_dirty_inode(bh, inode); affs_brelse(bh); mark_inode_dirty(inode); error = affs_add_entry(dir, inode, dentry, ST_SOFTLINK); if (error) goto err; return 0; err: inode->i_nlink = 0; mark_inode_dirty(inode); iput(inode); return error; }
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; }
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 inline void dirty_indirect(struct buffer_head *bh, struct inode *inode) { mark_buffer_dirty_inode(bh, inode); if (IS_SYNC(inode)) sync_dirty_buffer(bh); }
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; }