Beispiel #1
0
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;
}
Beispiel #2
0
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;
}
Beispiel #3
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;
}
Beispiel #5
0
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);
}
Beispiel #6
0
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;
}
Beispiel #7
0
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);
}
Beispiel #8
0
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;
}