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; set_nlink(inode, 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; }
void affs_free_prealloc(struct inode *inode) { struct super_block *sb = inode->i_sb; struct affs_zone *zone; int block; pr_debug("AFFS: free_prealloc(ino=%lu)\n", inode->i_ino); while (inode->u.affs_i.i_pa_cnt) { block = inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++]; inode->u.affs_i.i_pa_next &= AFFS_MAX_PREALLOC - 1; inode->u.affs_i.i_pa_cnt--; affs_free_block(sb, block); } if (inode->u.affs_i.i_zone) { zone = &sb->u.affs_sb.s_zones[inode->u.affs_i.i_zone]; if (zone->z_ino == inode->i_ino) zone->z_ino = 0; } }
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_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; }
void affs_truncate(struct inode *inode) { struct buffer_head *bh = NULL; int first; /* First block to be thrown away */ int block; s32 key; s32 *keyp; s32 ekey; s32 ptype, stype; int freethis; int net_blocksize; int blocksize = AFFS_I2BSIZE(inode); int rem; int ext; pr_debug("AFFS: truncate(inode=%ld,size=%lu)\n",inode->i_ino,inode->i_size); net_blocksize = blocksize - ((inode->i_sb->u.affs_sb.s_flags & SF_OFS) ? 24 : 0); first = (inode->i_size + net_blocksize - 1) / net_blocksize; if (inode->u.affs_i.i_lastblock < first - 1) { /* There has to be at least one new block to be allocated */ if (!inode->u.affs_i.i_ec && alloc_ext_cache(inode)) { /* XXX Fine! No way to indicate an error. */ return /* -ENOSPC */; } bh = affs_getblock(inode,first - 1); if (!bh) { affs_warning(inode->i_sb,"truncate","Cannot extend file"); inode->i_size = net_blocksize * (inode->u.affs_i.i_lastblock + 1); } else if (inode->i_sb->u.affs_sb.s_flags & SF_OFS) { rem = inode->i_size % net_blocksize; DATA_FRONT(bh)->data_size = cpu_to_be32(rem ? rem : net_blocksize); affs_fix_checksum(blocksize,bh->b_data,5); mark_buffer_dirty(bh,0); } goto out_truncate; } ekey = inode->i_ino; ext = 0; /* Free all blocks starting at 'first' and all then-empty * extension blocks. Do not free the header block, though. */ while (ekey) { if (!(bh = affs_bread(inode->i_dev,ekey,blocksize))) { affs_error(inode->i_sb,"truncate","Cannot read block %d",ekey); goto out_truncate; } if (affs_checksum_block(blocksize,bh->b_data,&ptype,&stype)) { affs_error(inode->i_sb,"truncate","Checksum error in header/ext block %d", ekey); goto out_truncate; } if (stype != ST_FILE || (ptype != T_SHORT && ptype != T_LIST)) { affs_error(inode->i_sb,"truncate", "Bad block (key=%d, ptype=%d, stype=%d)",ekey,ptype,stype); goto out_truncate; } /* Do we have to free this extension block after * freeing the data blocks pointed to? */ freethis = first == 0 && ekey != inode->i_ino; /* Free the data blocks. 'first' is relative to this * extension block and may well lie behind this block. */ for (block = first; block < AFFS_I2HSIZE(inode); block++) { keyp = &AFFS_BLOCK(bh->b_data,inode,block); key = be32_to_cpu(*keyp); if (key) { *keyp = 0; affs_free_block(inode->i_sb,key); } else break; } keyp = &GET_END_PTR(struct file_end,bh->b_data,blocksize)->extension; key = be32_to_cpu(*keyp); /* If 'first' is in this block or is the first * in the next one, this will be the last in * the list, thus we have to adjust the count * and zero the pointer to the next ext block. */ if (first <= AFFS_I2HSIZE(inode)) { ((struct file_front *)bh->b_data)->block_count = cpu_to_be32(first); first = 0; *keyp = 0; affs_fix_checksum(blocksize,bh->b_data,5); mark_buffer_dirty(bh,1); } else first -= AFFS_I2HSIZE(inode); affs_brelse(bh); bh = NULL; if (freethis) /* Don't bother fixing checksum */ affs_free_block(inode->i_sb,ekey); ekey = key; } block = ((inode->i_size + net_blocksize - 1) / net_blocksize) - 1; inode->u.affs_i.i_lastblock = block; /* If the file is not truncated to a block boundary, * the partial block after the EOF must be zeroed * so it cannot become accessible again. */ rem = inode->i_size % net_blocksize; if (rem) { if ((inode->i_sb->u.affs_sb.s_flags & SF_OFS)) rem += 24; pr_debug("AFFS: Zeroing from offset %d in block %d\n",rem,block); bh = affs_getblock(inode,block); if (bh) { memset(bh->b_data + rem,0,blocksize - rem); if ((inode->i_sb->u.affs_sb.s_flags & SF_OFS)) { ((struct data_front *)bh->b_data)->data_size = cpu_to_be32(rem); ((struct data_front *)bh->b_data)->next_data = 0; affs_fix_checksum(blocksize,bh->b_data,5); } mark_buffer_dirty(bh,1); } else affs_error(inode->i_sb,"truncate","Cannot read block %d",block); } out_truncate: affs_brelse(bh); /* Invalidate cache */ if (inode->u.affs_i.i_ec) { inode->u.affs_i.i_ec->max_ext = 0; for (key = 0; key < 3; key++) { inode->u.affs_i.i_ec->kc[key].kc_next_key = 0; inode->u.affs_i.i_ec->kc[key].kc_last = -1; } } mark_inode_dirty(inode); }
static struct buffer_head * affs_getblock(struct inode *inode, s32 block) { struct super_block *sb = inode->i_sb; int ofs = sb->u.affs_sb.s_flags & SF_OFS; int ext = block / AFFS_I2HSIZE(inode); struct buffer_head *bh, *ebh, *pbh = NULL; struct key_cache *kc; s32 key, nkey; int cf, j, pt; int index; int err; pr_debug("AFFS: getblock(%lu,%d)\n",inode->i_ino,block); if (block < 0) goto out_fail; key = calc_key(inode,&ext); block -= ext * AFFS_I2HSIZE(inode); pt = ext ? T_LIST : T_SHORT; /* Key refers now to the last known extension block, * ext is its sequence number (if 0, key refers to the * header block), and block is the block number relative * to the first block stored in that extension block. */ for (;;) { /* Loop over header block and extension blocks */ struct file_front *fdp; bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)); if (!bh) goto out_fail; fdp = (struct file_front *) bh->b_data; err = affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&cf,&j); if (err || cf != pt || j != ST_FILE) { affs_error(sb, "getblock", "Block %d is not a valid %s", key, pt == T_SHORT ? "file header" : "ext block"); goto out_free_bh; } j = be32_to_cpu(((struct file_front *)bh->b_data)->block_count); for (cf = 0; j < AFFS_I2HSIZE(inode) && j <= block; j++) { if (ofs && !pbh && inode->u.affs_i.i_lastblock >= 0) { if (j > 0) { s32 k = AFFS_BLOCK(bh->b_data, inode, j - 1); pbh = affs_bread(inode->i_dev, be32_to_cpu(k), AFFS_I2BSIZE(inode)); } else pbh = affs_getblock(inode,inode->u.affs_i.i_lastblock); if (!pbh) { affs_error(sb,"getblock", "Cannot get last block in file"); break; } } nkey = affs_new_data(inode); if (!nkey) break; inode->u.affs_i.i_lastblock++; if (AFFS_BLOCK(bh->b_data,inode,j)) { affs_warning(sb,"getblock","Block already allocated"); affs_free_block(sb,nkey); continue; } AFFS_BLOCK(bh->b_data,inode,j) = cpu_to_be32(nkey); if (ofs) { ebh = affs_bread(inode->i_dev,nkey,AFFS_I2BSIZE(inode)); if (!ebh) { affs_error(sb,"getblock", "Cannot get block %d",nkey); affs_free_block(sb,nkey); AFFS_BLOCK(bh->b_data,inode,j) = 0; break; } DATA_FRONT(ebh)->primary_type = cpu_to_be32(T_DATA); DATA_FRONT(ebh)->header_key = cpu_to_be32(inode->i_ino); DATA_FRONT(ebh)->sequence_number = cpu_to_be32(inode->u.affs_i.i_lastblock + 1); affs_fix_checksum(AFFS_I2BSIZE(inode), ebh->b_data, 5); mark_buffer_dirty(ebh, 0); if (pbh) { DATA_FRONT(pbh)->data_size = cpu_to_be32(AFFS_I2BSIZE(inode) - 24); DATA_FRONT(pbh)->next_data = cpu_to_be32(nkey); affs_fix_checksum(AFFS_I2BSIZE(inode),pbh->b_data,5); mark_buffer_dirty(pbh,0); affs_brelse(pbh); } pbh = ebh; } cf = 1; } /* N.B. May need to release pbh after here */ if (cf) { if (pt == T_SHORT) fdp->first_data = AFFS_BLOCK(bh->b_data,inode,0); fdp->block_count = cpu_to_be32(j); affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5); mark_buffer_dirty(bh,1); } if (block < j) { if (pbh) affs_brelse(pbh); break; } if (j < AFFS_I2HSIZE(inode)) { /* N.B. What about pbh here? */ goto out_free_bh; } block -= AFFS_I2HSIZE(inode); key = be32_to_cpu(FILE_END(bh->b_data,inode)->extension); if (!key) { key = affs_new_header(inode); if (!key) goto out_free_bh; ebh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)); if (!ebh) { /* N.B. must free bh here */ goto out_free_block; } ((struct file_front *)ebh->b_data)->primary_type = cpu_to_be32(T_LIST); ((struct file_front *)ebh->b_data)->own_key = cpu_to_be32(key); FILE_END(ebh->b_data,inode)->secondary_type = cpu_to_be32(ST_FILE); FILE_END(ebh->b_data,inode)->parent = cpu_to_be32(inode->i_ino); affs_fix_checksum(AFFS_I2BSIZE(inode),ebh->b_data,5); mark_buffer_dirty(ebh, 1); FILE_END(bh->b_data,inode)->extension = cpu_to_be32(key); affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5); mark_buffer_dirty(bh,1); affs_brelse(bh); bh = ebh; } pt = T_LIST; ext++; index = seqnum_to_index(ext); if (index > inode->u.affs_i.i_ec->max_ext && AFFS_ISINDEX(ext)) { inode->u.affs_i.i_ec->ec[index] = key; inode->u.affs_i.i_ec->max_ext = index; } affs_brelse(bh); } /* Invalidate key cache */ for (j = 0; j < 4; j++) { kc = &inode->u.affs_i.i_ec->kc[j]; kc->kc_last = -1; } key = be32_to_cpu(AFFS_BLOCK(bh->b_data,inode,block)); affs_brelse(bh); if (!key) goto out_fail; bh = affs_bread(inode->i_dev, key, AFFS_I2BSIZE(inode)); return bh; out_free_block: affs_free_block(sb, key); out_free_bh: affs_brelse(bh); out_fail: return NULL; }