Beispiel #1
0
static struct buffer_head *minix_find_entry(register struct inode *dir,
					    char *name, size_t namelen,
					    struct minix_dir_entry **res_dir)
{
    register struct buffer_head *bh;
    struct minix_sb_info *info;
    block_t block;
    loff_t offset;

    *res_dir = NULL;
    if (!dir || !dir->i_sb)
	return NULL;
    info = &dir->i_sb->u.minix_sb;
    if (namelen > info->s_namelen) {

#ifdef NO_TRUNCATE
	return NULL;
#else
	namelen = info->s_namelen;
#endif

    }
    bh = NULL;
    block = 0;
    offset = 0L;
    while (block * BLOCK_SIZE + offset < dir->i_size) {
	if (!bh) {
	    bh = minix_bread(dir, block, 0);
	    if (!bh) {
		block++;
		continue;
	    }
	    map_buffer(bh);
	}
	*res_dir = (struct minix_dir_entry *) (bh->b_data + offset);

	if (minix_match(namelen, name, bh, &offset, info)) {
	    return bh;
	}
	if (offset >= BLOCK_SIZE) {
	    unmap_brelse(bh);
	    bh = NULL;
	    offset = 0;
	    block++;
	}
    }
    unmap_brelse(bh);
    *res_dir = NULL;
    return NULL;
}
Beispiel #2
0
int minix_rmdir(register struct inode *dir, char *name, size_t len)
{
    int retval;
    register struct inode *inode;
    struct buffer_head *bh;
    struct minix_dir_entry *de;

    inode = NULL;
    bh = minix_find_entry(dir, name, len, &de);
    retval = -ENOENT;
    if (!bh)
	goto end_rmdir;
    retval = -EPERM;
    if (!(inode = iget(dir->i_sb, (ino_t) de->inode)))
	goto end_rmdir;
    if ((dir->i_mode & S_ISVTX) && !suser() &&
	current->euid != inode->i_uid && current->euid != dir->i_uid)
	goto end_rmdir;
    if (inode->i_dev != dir->i_dev)
	goto end_rmdir;
    if (inode == dir)		/* we may not delete ".", but "../dir" is ok */
	goto end_rmdir;
    if (!S_ISDIR(inode->i_mode)) {
	retval = -ENOTDIR;
	goto end_rmdir;
    }
    if (!empty_dir(inode)) {
	retval = -ENOTEMPTY;
	goto end_rmdir;
    }
    if (de->inode != inode->i_ino) {
	retval = -ENOENT;
	goto end_rmdir;
    }
    if (inode->i_count > 1) {
	retval = -EBUSY;
	goto end_rmdir;
    }
    if (inode->i_nlink != 2)
	printk("empty directory has nlink!=2 (%u)\n", inode->i_nlink);
    de->inode = 0;

#ifdef BLOAT_FS
    dir->i_version = ++event;
#endif

    mark_buffer_dirty(bh, 1);
    inode->i_nlink = 0;
    inode->i_dirt = 1;
    inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
    dir->i_nlink--;
    dir->i_dirt = 1;
    retval = 0;

  end_rmdir:
    iput(dir);
    iput(inode);
    unmap_brelse(bh);
    return retval;
}
Beispiel #3
0
int minix_lookup(register struct inode *dir, char *name, size_t len,
		 register struct inode **result)
{
    struct minix_dir_entry *de;
    struct buffer_head *bh;

    *result = NULL;

    if (dir) {
	if (S_ISDIR(dir->i_mode)) {
	    debug("minix_lookup: Entering minix_find_entry\n");
	    bh = minix_find_entry(dir, name, len, &de);
	    debug2("minix_lookup: minix_find_entry returned %x %d\n", bh,
		   bh->b_mapcount);
	    if (bh) {
		unmap_brelse(bh);
		*result = iget(dir->i_sb, (ino_t) de->inode);
		iput(dir);
		return (!*result) ? -EACCES : 0;
	    }
	}
	iput(dir);
    }
    return -ENOENT;
}
Beispiel #4
0
int minix_link(register struct inode *oldinode, register struct inode *dir,
		char *name, size_t len)
{
    int error;
    struct buffer_head *bh;
    struct minix_dir_entry *de;

    error = -EPERM;
    if (S_ISDIR(oldinode->i_mode))
	goto mlink_err;
    error = -EMLINK;
    if (oldinode->i_nlink >= MINIX_LINK_MAX)
	goto mlink_err;
    error = -EEXIST;
    bh = minix_find_entry(dir, name, len, &de);
    if (bh) {
	unmap_brelse(bh);
	goto mlink_err;
    }
    error = minix_add_entry(dir, name, len, oldinode->i_ino);
    if (!error) {
	oldinode->i_nlink++;
	oldinode->i_ctime = CURRENT_TIME;
	oldinode->i_dirt = 1;
    }
 mlink_err:
    iput(oldinode);
    iput(dir);
    return error;
}
Beispiel #5
0
int elksfs_lookup(register struct inode *dir, char *name, size_t len,
		  register struct inode **result)
{
    struct buffer_head *bh;
    struct elksfs_dir_entry *de;
    ino_t ino;

    *result = NULL;

    if (!dir)
	return -ENOENT;

    if (!S_ISDIR(dir->i_mode)) {
	iput(dir);
	return -ENOENT;
    }
    debug("elksfs_lookup: Entering elksfs_find_entry\n");
    bh = elksfs_find_entry(dir, name, len, &de);
    debug2("elksfs_lookup: elksfs_find_entry returned %x %d\n", bh,
	   bh->b_mapcount);
    if (!bh) {
	iput(dir);
	return -ENOENT;
    }
    map_buffer(bh);
    ino = de->inode;
    unmap_brelse(bh);
    *result = iget(dir->i_sb, (ino_t) ino);
    if (!*result) {
	iput(dir);
	return -EACCES;
    }
    iput(dir);
    return 0;
}
Beispiel #6
0
static int minix_readlink(register struct inode *inode,
			  char *buffer, int buflen)
{
    register struct buffer_head *bh;
    size_t len;

    if (!S_ISLNK(inode->i_mode)) {
	iput(inode);
	return -EINVAL;
    }
    bh = minix_bread(inode, 0, 0);
    iput(inode);

    if (!bh)
	return 0;
    map_buffer(bh);

    if((len = strlen(bh->b_data) + 1) > buflen)
	len = buflen;
    if (len > 1023)
	len = 1023;
    memcpy_tofs(buffer, bh->b_data, len);
    unmap_brelse(bh);
    return len;
}
Beispiel #7
0
int minix_mknod(register struct inode *dir, char *name, size_t len,
		int mode, int rdev)
{
    int error;
    register struct inode *inode;
    struct buffer_head *bh;
    struct minix_dir_entry *de;

/*    if (!dir)
	return -ENOENT;*/	/* Already checked by do_mknod() */

    error = -EEXIST;
    bh = minix_find_entry(dir, name, len, &de);
    if (bh) {
	unmap_brelse(bh);
	goto mknod2;
    }
    error = -ENOSPC;
    inode = minix_new_inode(dir, (__u16)mode);
    if (!inode)
	goto mknod2;
/*----------------------------------------------------------------------*/
    if (S_ISBLK(mode) || S_ISCHR(mode))
	inode->i_rdev = to_kdev_t(rdev);
/*----------------------------------------------------------------------*/
    error = minix_add_entry(dir, name, len, inode->i_ino);
    if (error) {
	inode->i_nlink--;
	inode->i_dirt = 1;
    }
    iput(inode);
 mknod2:
    iput(dir);
    return error;
}
Beispiel #8
0
static int V1_trunc_indirect(register struct inode *inode,
			     int offset, unsigned short *p)
{
    struct buffer_head *bh;
    int i;
    unsigned short tmp;
    register struct buffer_head *ind_bh;
    unsigned short *ind;
    int retry = 0;

    tmp = *p;
    if (!tmp) return 0;
    ind_bh = bread(inode->i_dev, (block_t) tmp);
    if (tmp != *p) {
	brelse(ind_bh);
	return 1;
    }
    if (!ind_bh) {
	*p = 0;
	return 0;
    }
    map_buffer(ind_bh);
  repeat:
    for (i = INDIRECT_BLOCK(offset); i < 512; i++) {
	if (i < 0) i = 0;
	else if (i < INDIRECT_BLOCK(offset)) goto repeat;
	ind = i + (unsigned short *) ind_bh->b_data;
	tmp = *ind;
	if (!tmp) continue;
	bh = get_hash_table(inode->i_dev, (block_t) tmp);
	if (i < INDIRECT_BLOCK(offset)) {
	    brelse(bh);
	    goto repeat;
	}
	if ((bh && bh->b_count != 1) || tmp != *ind) {
	    retry = 1;
	    brelse(bh);
	    continue;
	}
	*ind = 0;
	mark_buffer_dirty(ind_bh, 1);
	brelse(bh);
	minix_free_block(inode->i_sb, tmp);
    }
    ind = (unsigned short *) ind_bh->b_data;
    for (i = 0; i < 512; i++)
	if (*(ind++)) break;
    if (i >= 512) {
	if (ind_bh->b_count != 1) retry = 1;
	else {
	    tmp = *p;
	    *p = 0;
	    minix_free_block(inode->i_sb, tmp);
	}
    }
    unmap_brelse(ind_bh);
    return retry;
}
Beispiel #9
0
int minix_symlink(struct inode *dir, char *name, size_t len, char *symname)
{
    int error;
    register struct inode *inode;
    register struct buffer_head *bh;
    struct minix_dir_entry *de;

    error = -EEXIST;
    bh = minix_find_entry(dir, name, len, &de);
    if (bh) {
	unmap_brelse(bh);
	goto symlink2;
    }
    error = -ENOSPC;
    inode = minix_new_inode(dir, S_IFLNK);
    if (!inode)
	goto symlink2;
/*----------------------------------------------------------------------*/
    bh = minix_bread(inode, 0, 1);
    if (!bh)
	goto symlink1;
    map_buffer(bh);
    if((error = strlen_fromfs(symname)) > 1023)
	error = 1023;
    memcpy_fromfs(bh->b_data, symname, error);
    bh->b_data[error] = 0;
    inode->i_size = (__u32) error;
    mark_buffer_dirty(bh, 1);
    unmap_brelse(bh);
/*----------------------------------------------------------------------*/
    error = minix_add_entry(dir, name, len, inode->i_ino);
    if (error) {
      symlink1:
	inode->i_nlink--;
	inode->i_dirt = 1;
    }
    iput(inode);
 symlink2:
    iput(dir);
    return error;
}
Beispiel #10
0
static int minix_follow_link(register struct inode *dir,
			     register struct inode *inode,
			     int flag, int mode, struct inode **res_inode)
{

    /*
     *      FIXME: #1 Stack use is too high
     *             #2 Needs to be current->link_count as this is blocking
     */

    int error;
    struct buffer_head *bh;
    static int link_count = 0;
    __u16 ds, *pds;

    *res_inode = NULL;
    if (!dir) {
	dir = current->fs.root;
	dir->i_count++;
    }
    if (!inode) {
	iput(dir);
	return -ENOENT;
    }
    if (!S_ISLNK(inode->i_mode)) {
	iput(dir);
	*res_inode = inode;
	return 0;
    }
    if ( /* current-> */ link_count > 5) {
	iput(inode);
	iput(dir);
	return -ELOOP;
    }
    bh = minix_bread(inode, 0, 0);
    iput(inode);
    if (!bh) {
	iput(dir);
	return -EIO;
    }
    /* current-> */ link_count++;
    map_buffer(bh);
    pds = &current->t_regs.ds;
    ds = *pds;
    *pds = kernel_ds;
    error = open_namei(bh->b_data, flag, mode, res_inode, dir);
    *pds = ds;
    /* current-> */ link_count--;
    unmap_brelse(bh);
    return error;
}
Beispiel #11
0
static int V1_trunc_dindirect(register struct inode *inode,
			      int offset, unsigned short *p)
{
    int i;
    unsigned short tmp;
    register struct buffer_head *dind_bh;
    unsigned short *dind;
    int retry = 0;

    if (!(tmp = *p))
	return 0;
    dind_bh = bread(inode->i_dev, (block_t) tmp);
    if (tmp != *p) {
	brelse(dind_bh);
	return 1;
    }
    if (!dind_bh) {
	*p = 0;
	return 0;
    }
    map_buffer(dind_bh);
  repeat:
    for (i = DINDIRECT_BLOCK(offset); i < 512; i++) {
	if (i < 0)
	    i = 0;
	if (i < DINDIRECT_BLOCK(offset))
	    goto repeat;
	dind = i + (unsigned short *) dind_bh->b_data;
	retry |= V1_trunc_indirect(inode, offset + (i << 9), dind);
	mark_buffer_dirty(dind_bh, 1);
    }
    dind = (unsigned short *) dind_bh->b_data;
    for (i = 0; i < 512; i++)
	if (*(dind++))
	    break;
    if (i >= 512)
	if (dind_bh->b_count != 1)
	    retry = 1;
	else {
	    tmp = *p;
	    *p = 0;
	    inode->i_dirt = 1;
	    minix_free_block(inode->i_sb, tmp);
	}
    unmap_brelse(dind_bh);
    return retry;
}
Beispiel #12
0
static int minix_add_entry(register struct inode *dir,
			   char *name,
			   size_t namelen,
			   ino_t ino)
{
    unsigned short block;
    loff_t offset;
    register struct buffer_head *bh;
    struct minix_dir_entry *de;
    struct minix_sb_info *info;

    if (!dir || !dir->i_sb)
	return -ENOENT;
    info = &dir->i_sb->u.minix_sb;
    if (namelen > info->s_namelen) {
#ifdef NO_TRUNCATE
	return -ENAMETOOLONG;
#else
	namelen = info->s_namelen;
#endif
    }
    if (!namelen)
	return -ENOENT;
    bh = NULL;
    block = 0;
    offset = 0L;
    while (1) {
	if (!bh) {
	    bh = minix_bread(dir, block, 1);
	    if (!bh)
		return -ENOSPC;
	    map_buffer(bh);
	}
	de = (struct minix_dir_entry *) (bh->b_data + offset);
	offset += info->s_dirsize;
	if (block * 1024L + offset > dir->i_size) {
	    de->inode = 0;
	    dir->i_size = block * 1024L + offset;
	    dir->i_dirt = 1;
	}
	if (de->inode) {
	    if (namecompare(namelen, info->s_namelen, name, de->name)) {
		debug2("MINIXadd_entry: file %t==%s (already exists)\n",
		     name, de->name);
		unmap_brelse(bh);
		return -EEXIST;
	    }
	} else {
	    size_t i;

	    dir->i_mtime = dir->i_ctime = CURRENT_TIME;
	    dir->i_dirt = 1;
	    memcpy_fromfs(de->name, name, namelen);
	    if((i = info->s_namelen - namelen) > 0)
		memset(de->name + namelen, 0, i);

#ifdef BLOAT_FS
	    dir->i_version = ++event;
#endif

	    de->inode = ino;
	    mark_buffer_dirty(bh, 1);
	    unmap_brelse(bh);
	    break;
	}
	if (offset >= BLOCK_SIZE) {
	    unmap_brelse(bh);
	    bh = NULL;
	    offset = 0;
	    block++;
	}
    }
    return 0;
}
Beispiel #13
0
int elksfs_mkdir(register struct inode *dir, char *name, size_t len, int mode)
{
    struct buffer_head *bh, *dir_block;
    register struct inode *inode;
    struct elksfs_dir_entry *de;
    struct elksfs_sb_info *info;
    int error;

    if (!dir || !dir->i_sb) {
	iput(dir);
	return -EINVAL;
    }
    info = &dir->i_sb->u.elksfs_sb;
    bh = elksfs_find_entry(dir, name, len, &de);
    if (bh) {
	brelse(bh);
	iput(dir);
	return -EEXIST;
    }

#if 0
/*	Above checks if bh is returned and exits, so bh
 *	is NULL at this point
 */
    map_buffer(bh);
#endif

    if (dir->i_nlink >= ELKSFS_LINK_MAX) {
	iput(dir);
	return -EMLINK;
    }
    inode = elksfs_new_inode(dir);
    if (!inode) {
	iput(dir);
	return -ENOSPC;
    }
    debug("m_mkdir: new_inode succeeded\n");
    inode->i_op = &elksfs_dir_inode_operations;
    inode->i_size = 2 * info->s_dirsize;
    debug("m_mkdir: starting elksfs_bread\n");
    dir_block = elksfs_bread(inode, 0, 1);
    if (!dir_block) {
	iput(dir);
	inode->i_nlink--;
	inode->i_dirt = 1;
	iput(inode);
	return -ENOSPC;
    }
    debug("m_mkdir: read succeeded\n");
    map_buffer(dir_block);
    de = (struct elksfs_dir_entry *) dir_block->b_data;
    de->inode = inode->i_ino;
    strcpy(de->name, ".");
    de = (struct elksfs_dir_entry *) (dir_block->b_data + info->s_dirsize);
    de->inode = dir->i_ino;
    strcpy(de->name, "..");
    inode->i_nlink = 2;
    mark_buffer_dirty(dir_block, 1);
    unmap_brelse(dir_block);
    debug("m_mkdir: dir_block update succeeded\n");
    inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs.umask);
    if (dir->i_mode & S_ISGID)
	inode->i_mode |= S_ISGID;
    inode->i_dirt = 1;
    error = elksfs_add_entry(dir, name, len, &bh, &de);
    if (error) {
	iput(dir);
	inode->i_nlink = 0;
	iput(inode);
	return error;
    }
    map_buffer(bh);
    de->inode = inode->i_ino;
    mark_buffer_dirty(bh, 1);
    dir->i_nlink++;
    dir->i_dirt = 1;
    iput(dir);
    iput(inode);
    unmap_brelse(bh);
    debug("m_mkdir: done!\n");
    return 0;
}
Beispiel #14
0
static int elksfs_add_entry(register struct inode *dir, char *name,
			    size_t namelen, struct buffer_head **res_buf,
			    struct elksfs_dir_entry **res_dir)
{
    struct buffer_head *bh;
    struct elksfs_dir_entry *de;
    struct elksfs_sb_info *info;
    unsigned long int i;
    block_t block;
    loff_t offset;

    *res_buf = NULL;
    *res_dir = NULL;
    if (!dir || !dir->i_sb)
	return -ENOENT;
    info = &dir->i_sb->u.elksfs_sb;
    if (namelen > info->s_namelen) {

#ifdef NO_TRUNCATE
	return -ENAMETOOLONG;
#else
	namelen = info->s_namelen;
#endif

    }
    if (!namelen)
	return -ENOENT;
    bh = NULL;
    block = 0;
    offset = 0;
    while (1) {
	if (!bh) {
	    bh = elksfs_bread(dir, block, 1);
	    if (!bh)
		return -ENOSPC;
	}
	map_buffer(bh);
	de = (struct elksfs_dir_entry *) (bh->b_data + offset);
	offset += info->s_dirsize;
	if (block * 1024 + offset > dir->i_size) {
	    de->inode = 0;
	    dir->i_size = block * 1024 + offset;
	    dir->i_dirt = 1;
	}
	if (de->inode) {
	    if (namecompare(namelen, (size_t) info->s_namelen, name,
			    de->name)) {
		debug2("ELKSFSadd_entry: file %t==%s (already exists)\n",
		     name, de->name);
		unmap_brelse(bh);
		return -EEXIST;
	    }
	} else {
	    dir->i_mtime = dir->i_ctime = CURRENT_TIME;
	    dir->i_dirt = 1;
	    for (i = 0; i < info->s_namelen; i++)
		de->name[i] = (i < namelen)
			? (char) get_fs_byte((unsigned char *) name + i)
			: 0;

#ifdef BLOAT_FS
	    dir->i_version = ++event;
#endif

	    unmap_buffer(bh);
	    mark_buffer_dirty(bh, 1);
	    *res_dir = de;
	    break;
	}
	if (offset < 1024)
	    continue;
	printk("elksfs_add_entry may need another unmap_buffer :)\n");
	brelse(bh);
	bh = NULL;
	offset = 0;
	block++;
    }
    *res_buf = bh;
    return 0;
}
Beispiel #15
0
static int minix_add_entry(register struct inode *dir,
			   char *name,
			   size_t namelen,
			   struct buffer_head **res_buf,
			   struct minix_dir_entry **res_dir)
{
    unsigned short block;
    loff_t offset;
    register struct buffer_head *bh;
    struct minix_dir_entry *de;
    struct minix_sb_info *info;

    *res_buf = NULL;
    *res_dir = NULL;
    if (!dir || !dir->i_sb)
	return -ENOENT;
    info = &dir->i_sb->u.minix_sb;
    if (namelen > info->s_namelen) {
#ifdef NO_TRUNCATE
	return -ENAMETOOLONG;
#else
	namelen = info->s_namelen;
#endif
    }
    if (!namelen)
	return -ENOENT;
    bh = NULL;
    block = 0;
    offset = 0L;
    while (1) {
	if (!bh) {
	    bh = minix_bread(dir, block, 1);
	    if (!bh)
		return -ENOSPC;
	    map_buffer(bh);
	}
	de = (struct minix_dir_entry *) (bh->b_data + offset);
	offset += info->s_dirsize;
	if (block * 1024L + offset > dir->i_size) {
	    de->inode = 0;
	    dir->i_size = block * 1024L + offset;
	    dir->i_dirt = 1;
	}
	if (de->inode) {
	    if (namecompare(namelen, info->s_namelen, name, de->name)) {
		debug2("MINIXadd_entry: file %t==%s (already exists)\n",
		     name, de->name);
		unmap_brelse(bh);
		return -EEXIST;
	    }
	} else {
	    size_t i;

	    dir->i_mtime = dir->i_ctime = CURRENT_TIME;
	    dir->i_dirt = 1;
	    for (i = 0; i < info->s_namelen; i++)
		de->name[i] = (i < namelen) ? (char) get_fs_byte(name + i)
					    : '\0';

#ifdef BLOAT_FS
	    dir->i_version = ++event;
#endif

	    unmap_buffer(bh);
	    mark_buffer_dirty(bh, 1);
	    *res_dir = de;
	    break;
	}
	if (offset < 1024)
	    continue;
	unmap_brelse(bh);
	bh = NULL;
	offset = 0;
	block++;
    }
    *res_buf = bh;
    return 0;
}
Beispiel #16
0
int minix_mkdir(register struct inode *dir, char *name, size_t len, int mode)
{
    int error;
    register struct inode *inode;
    struct buffer_head *dir_block;
    struct buffer_head *bh;
    struct minix_dir_entry *de;

    if (!dir || !dir->i_sb) {
	iput(dir);
	return -EINVAL;
    }
    bh = minix_find_entry(dir, name, len, &de);
    if (bh) {
	brelse(bh);
	iput(dir);
	return -EEXIST;
    }
    if (dir->i_nlink >= MINIX_LINK_MAX) {
	iput(dir);
	return -EMLINK;
    }
    inode = minix_new_inode(dir);
    if (!inode) {
	iput(dir);
	return -ENOSPC;
    }
    debug("m_mkdir: new_inode succeeded\n");
    inode->i_op = &minix_dir_inode_operations;
    inode->i_size = 2 * dir->i_sb->u.minix_sb.s_dirsize;
    debug("m_mkdir: starting minix_bread\n");
    dir_block = minix_bread(inode, 0, 1);
    if (!dir_block) {
	iput(dir);
	inode->i_nlink--;
	inode->i_dirt = 1;
	iput(inode);
	return -ENOSPC;
    }
    debug("m_mkdir: read succeeded\n");
    map_buffer(dir_block);
    de = (struct minix_dir_entry *) dir_block->b_data;
    de->inode = inode->i_ino;
    strcpy(de->name, ".");
    de =
	(struct minix_dir_entry *) (dir_block->b_data +
				    dir->i_sb->u.minix_sb.s_dirsize);
    de->inode = dir->i_ino;
    strcpy(de->name, "..");
    inode->i_nlink = 2;
    mark_buffer_dirty(dir_block, 1);
    unmap_brelse(dir_block);
    debug("m_mkdir: dir_block update succeeded\n");
    inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs.umask);
    if (dir->i_mode & S_ISGID)
	inode->i_mode |= S_ISGID;
    inode->i_dirt = 1;
    error = minix_add_entry(dir, name, len, &bh, &de);
    if (error) {
	iput(dir);
	inode->i_nlink = 0;
	iput(inode);
	return error;
    }
    map_buffer(bh);
    de->inode = inode->i_ino;
    mark_buffer_dirty(bh, 1);
    dir->i_nlink++;
    dir->i_dirt = 1;
    iput(dir);
    iput(inode);
    unmap_brelse(bh);
    debug("m_mkdir: done!\n");
    return 0;
}
Beispiel #17
0
static int blk_rw(struct inode *inode, register struct file *filp,
		  char *buf, size_t count, int wr)
{
    register struct buffer_head *bh;
    size_t chars, offset;
    int written = 0;

    while (count > 0) {
    /*
     *      Offset to block/offset
     */
	offset = ((size_t)filp->f_pos) & (BLOCK_SIZE - 1);
	chars = BLOCK_SIZE - offset;
	if (chars > count)
	    chars = count;
	/*
	 *      Read the block in - use getblk on a write
	 *      of a whole block to avoid a read of the data.
	 */
	bh = getblk(inode->i_rdev, (block_t)(filp->f_pos >> BLOCK_SIZE_BITS));
	if ((wr == BLOCK_READ) || (chars != BLOCK_SIZE)) {
	    if (!readbuf(bh)) {
		if (!written) written = -EIO;
		break;
	    }
	}

	map_buffer(bh);
	if (wr == BLOCK_WRITE) {
	    /*
	     *      Alter buffer, mark dirty
	     */
	    memcpy_fromfs(bh->b_data + offset, buf, chars);
	    bh->b_uptodate = bh->b_dirty = 1;
	    /*
	     *      Writing: queue physical I/O
	     */
	    ll_rw_blk(WRITE, bh);
	    wait_on_buffer(bh);
	    if (!bh->b_uptodate) { /* Write error. */
		unmap_brelse(bh);
		if (!written) written = -EIO;
		break;
	    }
	} else {
	    /*
	     *      Empty buffer data. Buffer unchanged
	     */
	    memcpy_tofs(buf, bh->b_data + offset, chars);
	}
	/*
	 *      Move on and release buffer
	 */

	unmap_brelse(bh);

	buf += chars;
	filp->f_pos += chars;
	written += chars;
	count -= chars;
    }
    return written;
}