int affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) { struct inode *oldinode = old_dentry->d_inode; struct inode *inode; struct buffer_head *bh; unsigned long i; int error; pr_debug("AFFS: link(%lu,%lu,\"%.*s\")\n",oldinode->i_ino,dir->i_ino, (int)dentry->d_name.len,dentry->d_name.name); /* N.B. Do we need this test? The dentry must be negative ... */ bh = affs_find_entry(dir,dentry,&i); if (bh) { affs_brelse(bh); return -EEXIST; } if (oldinode->u.affs_i.i_hlink) { /* Cannot happen */ affs_warning(dir->i_sb,"link","Impossible link to link"); return -EINVAL; } error = -ENOSPC; if (!(inode = affs_new_inode(dir))) goto out; inode->i_op = oldinode->i_op; inode->u.affs_i.i_protect = mode_to_prot(oldinode->i_mode); inode->u.affs_i.i_original = oldinode->i_ino; inode->u.affs_i.i_hlink = 1; inode->i_mtime = oldinode->i_mtime; if (S_ISDIR(oldinode->i_mode)) error = affs_add_entry(dir,oldinode,inode,dentry,ST_LINKDIR); else error = affs_add_entry(dir,oldinode,inode,dentry,ST_LINKFILE); if (error) inode->i_nlink = 0; else { dir->i_version = ++event; mark_inode_dirty(dir); mark_inode_dirty(oldinode); oldinode->i_count++; d_instantiate(dentry,oldinode); } mark_inode_dirty(inode); iput(inode); out: return error; }
static int affs_remount(struct super_block *sb, int *flags, char *data) { struct affs_sb_info *sbi = AFFS_SB(sb); int blocksize; uid_t uid; gid_t gid; int mode; int reserved; int root_block; unsigned long mount_flags; unsigned long read_only = sbi->s_flags & SF_READONLY; pr_debug("AFFS: remount(flags=0x%x,opts=\"%s\")\n",*flags,data); if (!parse_options(data,&uid,&gid,&mode,&reserved,&root_block, &blocksize,&sbi->s_prefix,sbi->s_volume,&mount_flags)) return -EINVAL; sbi->s_flags = mount_flags | read_only; sbi->s_mode = mode; sbi->s_uid = uid; sbi->s_gid = gid; if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) return 0; if (*flags & MS_RDONLY) { sb->s_dirt = 1; while (sb->s_dirt) affs_write_super(sb); sb->s_flags |= MS_RDONLY; } else if (!(sbi->s_flags & SF_READONLY)) { sb->s_flags &= ~MS_RDONLY; } else { affs_warning(sb,"remount","Cannot remount fs read/write because of errors"); return -EINVAL; } return 0; }
static int affs_readdir(struct file *filp, void *dirent, filldir_t filldir) { int j, namelen; s32 i; int hash_pos; int chain_pos; unsigned long ino; int stored; unsigned char *name; struct buffer_head *dir_bh; struct buffer_head *fh_bh; struct inode *dir; struct inode *inode = filp->f_dentry->d_inode; pr_debug("AFFS: readdir(ino=%lu,f_pos=%lu)\n",inode->i_ino,(unsigned long)filp->f_pos); stored = 0; dir_bh = NULL; fh_bh = NULL; dir = NULL; ino = inode->i_ino; if (filp->f_pos == 0) { filp->private_data = (void *)0; if (filldir(dirent,".",1,filp->f_pos,inode->i_ino) < 0) { return 0; } ++filp->f_pos; stored++; } if (filp->f_pos == 1) { if (filldir(dirent,"..",2,filp->f_pos,affs_parent_ino(inode)) < 0) { return stored; } filp->f_pos = 2; stored++; } chain_pos = (filp->f_pos - 2) & 0xffff; hash_pos = (filp->f_pos - 2) >> 16; if (chain_pos == 0xffff) { affs_warning(inode->i_sb,"readdir","More than 65535 entries in chain"); chain_pos = 0; hash_pos++; filp->f_pos = ((hash_pos << 16) | chain_pos) + 2; } if (!(dir_bh = affs_bread(inode->i_dev,inode->i_ino, AFFS_I2BSIZE(inode)))) goto readdir_done; while (1) { while (hash_pos < AFFS_I2HSIZE(inode) && !((struct dir_front *)dir_bh->b_data)->hashtable[hash_pos]) hash_pos++; if (hash_pos >= AFFS_I2HSIZE(inode)) break; i = be32_to_cpu(((struct dir_front *)dir_bh->b_data)->hashtable[hash_pos]); j = chain_pos; /* If the directory hasn't changed since the last call to readdir(), * we can jump directly to where we left off. */ if (filp->private_data && filp->f_version == inode->i_version) { i = (s32)filp->private_data; j = 0; pr_debug("AFFS: readdir() left off=%d\n",i); } filp->f_version = inode->i_version; pr_debug("AFFS: hash_pos=%d chain_pos=%d\n",hash_pos,chain_pos); while (i) { if (!(fh_bh = affs_bread(inode->i_dev,i,AFFS_I2BSIZE(inode)))) { affs_error(inode->i_sb,"readdir","Cannot read block %d",i); goto readdir_done; } ino = i; i = be32_to_cpu(FILE_END(fh_bh->b_data,inode)->hash_chain); if (j == 0) break; affs_brelse(fh_bh); fh_bh = NULL; j--; } if (fh_bh) { namelen = affs_get_file_name(AFFS_I2BSIZE(inode),fh_bh->b_data,&name); pr_debug("AFFS: readdir(): filldir(\"%.*s\",ino=%lu), i=%d\n", namelen,name,ino,i); filp->private_data = (void *)ino; if (filldir(dirent,name,namelen,filp->f_pos,ino) < 0) goto readdir_done; filp->private_data = (void *)i; affs_brelse(fh_bh); fh_bh = NULL; stored++; } if (i == 0) { hash_pos++; chain_pos = 0; } else chain_pos++; filp->f_pos = ((hash_pos << 16) | chain_pos) + 2; } readdir_done: affs_brelse(dir_bh); affs_brelse(fh_bh); pr_debug("AFFS: readdir()=%d\n",stored); return stored; }
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; }
struct inode *affs_iget(struct super_block *sb, unsigned long ino) { struct affs_sb_info *sbi = AFFS_SB(sb); struct buffer_head *bh; // struct affs_head *head; struct affs_tail *tail; struct inode *inode; u32 block; u32 size; u32 prot; u16 id; inode = iget_locked(sb, ino); if (!inode) return ERR_PTR(-ENOMEM); if (!(inode->i_state & I_NEW)) return inode; pr_debug("AFFS: affs_iget(%lu)\n", inode->i_ino); block = inode->i_ino; bh = affs_bread(sb, block); if (!bh) { affs_warning(sb, "read_inode", "Cannot read block %d", block); goto bad_inode; } if (affs_checksum_block(sb, bh) || be32_to_cpu(AFFS_HEAD(bh)->ptype) != T_SHORT) { affs_warning(sb,"read_inode", "Checksum or type (ptype=%d) error on inode %d", AFFS_HEAD(bh)->ptype, block); goto bad_inode; } // head = AFFS_HEAD(bh); tail = AFFS_TAIL(sb, bh); prot = be32_to_cpu(tail->protect); inode->i_size = 0; inode->i_nlink = 1; inode->i_mode = 0; AFFS_I(inode)->i_extcnt = 1; AFFS_I(inode)->i_ext_last = ~1; AFFS_I(inode)->i_protect = prot; 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_lastalloc = 0; AFFS_I(inode)->i_pa_cnt = 0; if (sbi->s_flags & SF_SETMODE) inode->i_mode = sbi->s_mode; else inode->i_mode = prot_to_mode(prot); id = be16_to_cpu(tail->uid); if (id == 0 || sbi->s_flags & SF_SETUID) inode->i_uid = sbi->s_uid; else if (id == 0xFFFF && sbi->s_flags & SF_MUFS) inode->i_uid = 0; else inode->i_uid = id; id = be16_to_cpu(tail->gid); if (id == 0 || sbi->s_flags & SF_SETGID) inode->i_gid = sbi->s_gid; else if (id == 0xFFFF && sbi->s_flags & SF_MUFS) inode->i_gid = 0; else inode->i_gid = id; switch (be32_to_cpu(tail->stype)) { case ST_ROOT: inode->i_uid = sbi->s_uid; inode->i_gid = sbi->s_gid; /* fall through */ case ST_USERDIR: if (be32_to_cpu(tail->stype) == ST_USERDIR || sbi->s_flags & SF_SETMODE) { if (inode->i_mode & S_IRUSR) inode->i_mode |= S_IXUSR; if (inode->i_mode & S_IRGRP) inode->i_mode |= S_IXGRP; if (inode->i_mode & S_IROTH) inode->i_mode |= S_IXOTH; inode->i_mode |= S_IFDIR; } else inode->i_mode = S_IRUGO | S_IXUGO | S_IWUSR | S_IFDIR; /* Maybe it should be controlled by mount parameter? */ //inode->i_mode |= S_ISVTX; inode->i_op = &affs_dir_inode_operations; inode->i_fop = &affs_dir_operations; break; case ST_LINKDIR: #if 0 affs_warning(sb, "read_inode", "inode is LINKDIR"); goto bad_inode; #else inode->i_mode |= S_IFDIR; /* ... and leave ->i_op and ->i_fop pointing to empty */ break; #endif case ST_LINKFILE: affs_warning(sb, "read_inode", "inode is LINKFILE"); goto bad_inode; case ST_FILE: size = be32_to_cpu(tail->size); inode->i_mode |= S_IFREG; AFFS_I(inode)->mmu_private = inode->i_size = size; if (inode->i_size) { AFFS_I(inode)->i_blkcnt = (size - 1) / sbi->s_data_blksize + 1; AFFS_I(inode)->i_extcnt = (AFFS_I(inode)->i_blkcnt - 1) / sbi->s_hashsize + 1; } if (tail->link_chain) inode->i_nlink = 2; inode->i_mapping->a_ops = (sbi->s_flags & SF_OFS) ? &affs_aops_ofs : &affs_aops; inode->i_op = &affs_file_inode_operations; inode->i_fop = &affs_file_operations; break; case ST_SOFTLINK: inode->i_mode |= S_IFLNK; inode->i_op = &affs_symlink_inode_operations; inode->i_data.a_ops = &affs_symlink_aops; break; } inode->i_mtime.tv_sec = inode->i_atime.tv_sec = inode->i_ctime.tv_sec = (be32_to_cpu(tail->change.days) * (24 * 60 * 60) + be32_to_cpu(tail->change.mins) * 60 + be32_to_cpu(tail->change.ticks) / 50 + ((8 * 365 + 2) * 24 * 60 * 60)) + sys_tz.tz_minuteswest * 60; inode->i_mtime.tv_nsec = inode->i_ctime.tv_nsec = inode->i_atime.tv_nsec = 0; affs_brelse(bh); unlock_new_inode(inode); return inode; bad_inode: affs_brelse(bh); iget_failed(inode); return ERR_PTR(-EIO); }
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; }
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; }