You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
796 lines
20 KiB
796 lines
20 KiB
/* |
|
* Copyright (C) 2016 MediaTek Inc. |
|
* |
|
* This program is free software; you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License version 2 as |
|
* published by the Free Software Foundation. |
|
* |
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
|
* See http://www.gnu.org/licenses/gpl-2.0.html for more details. |
|
*/ |
|
|
|
#define DEBUG 1 |
|
#include <linux/module.h> |
|
#include <linux/fs.h> |
|
#include <linux/aio.h> |
|
#include <linux/uio.h> |
|
#include <linux/slab.h> |
|
#include <linux/uaccess.h> |
|
#include <linux/pagemap.h> |
|
#include "rawfs.h" |
|
|
|
#define CEILING(x, y) rawfs_div(((x)+(y)-1), (y)) |
|
#define FLOOR(x, y) rawfs_div((x), (y)) |
|
#define page_uptodate(page) test_bit(PG_uptodate, &(page)->flags) |
|
|
|
/* ----------------------------------------------------------------------------- */ |
|
/* Regular File Operation */ |
|
/* ----------------------------------------------------------------------------- */ |
|
static ssize_t rawfs_file_read(struct file *filp, struct iovec *iv, loff_t *ppos) |
|
{ |
|
struct super_block *sb = filp->f_path.dentry->d_sb; |
|
struct rawfs_sb_info *rawfs_sb = RAWFS_SB(sb); |
|
struct inode *inode = filp->f_mapping->host; |
|
struct rawfs_inode_info *inode_info = RAWFS_I(inode); |
|
|
|
ssize_t retval = iv->iov_len; |
|
loff_t size; |
|
loff_t pos = *ppos; |
|
|
|
unsigned int curr_file_pos = pos; |
|
unsigned int curr_buf_pos = 0; |
|
int remain_buf_size; |
|
|
|
RAWFS_PRINT(RAWFS_DBG_FILE, |
|
"%s: %s\n", __func__, inode_info->i_name); |
|
|
|
mutex_lock(&rawfs_sb->rawfs_lock); |
|
|
|
size = i_size_read(inode); |
|
|
|
RAWFS_PRINT(RAWFS_DBG_FILE, |
|
"%s: %s, pos %lld, len %ld, filesize: %lld\n", |
|
__func__, inode_info->i_name, pos, (unsigned long)retval, size); |
|
|
|
if ((retval + pos) >= size) |
|
retval = size - pos; |
|
|
|
if (pos < size) { |
|
/* Read File */ |
|
{ |
|
int preceding_pages, rear_pages; |
|
struct rawfs_page *page_buf = NULL; |
|
int i; |
|
|
|
/* Prepare page buffer */ |
|
page_buf = kzalloc(rawfs_sb->page_size, GFP_NOFS); |
|
|
|
if (page_buf == NULL) { |
|
retval = 0; |
|
goto out; |
|
} |
|
|
|
preceding_pages = FLOOR((unsigned)pos, rawfs_sb->page_data_size); |
|
rear_pages = CEILING((unsigned)pos + retval, |
|
rawfs_sb->page_data_size); |
|
remain_buf_size = retval; |
|
|
|
RAWFS_PRINT(RAWFS_DBG_FILE, |
|
"%s: %s, preceding %d, rear %d, remain %d\n", |
|
__func__, inode_info->i_name, preceding_pages, rear_pages, |
|
remain_buf_size); |
|
|
|
/* Step 1: Copy preceding pages, if starting pos is not 0. */ |
|
for (i = preceding_pages; i < rear_pages; i++) { |
|
__u32 crc; |
|
|
|
/* Read page */ |
|
rawfs_sb->dev.read_page(sb, |
|
inode_info->i_location_block, |
|
inode_info->i_location_page+i, |
|
page_buf); |
|
|
|
/* TODO: skip this page, if unrecoverable error occurs */ |
|
|
|
/* CRC error should not happen, since we have already check them at bootup */ |
|
crc = rawfs_page_crc_data(sb, page_buf); |
|
|
|
if (crc != page_buf->i_crc) |
|
RAWFS_PRINT(RAWFS_DBG_FILE, |
|
"%s: %s @ %X, crc fail %X, expected %X\n", |
|
__func__, |
|
page_buf->i_info.i_file_info.i_name, |
|
page_buf->i_info.i_file_info.i_parent_folder_id, crc, |
|
page_buf->i_crc); |
|
else |
|
RAWFS_PRINT(RAWFS_DBG_FILE, |
|
"%s: %s @ %X, crc %X\n", |
|
__func__, |
|
page_buf->i_info.i_file_info.i_name, |
|
page_buf->i_info.i_file_info.i_parent_folder_id, |
|
page_buf->i_crc); |
|
|
|
/* Copy required parts */ |
|
{ |
|
int start_in_buf; |
|
int copy_len; |
|
|
|
start_in_buf = (curr_file_pos % rawfs_sb->page_data_size); |
|
copy_len = ((start_in_buf + remain_buf_size) > |
|
rawfs_sb->page_data_size) ? |
|
(rawfs_sb->page_data_size - start_in_buf) : |
|
remain_buf_size; |
|
|
|
if (copy_to_user((char *)iv->iov_base + curr_buf_pos, |
|
&page_buf->i_data[0] + start_in_buf, copy_len)) { |
|
retval = -EFAULT; |
|
goto out2; |
|
} |
|
|
|
RAWFS_PRINT(RAWFS_DBG_FILE, |
|
"%s: %s, %d, curr %d, remain %d start %d copy %d starting %X\n", |
|
__func__, inode_info->i_name, i, curr_buf_pos, remain_buf_size, |
|
start_in_buf, copy_len, |
|
*(unsigned int *)(&page_buf->i_data[0] + start_in_buf)); |
|
|
|
curr_file_pos += copy_len; |
|
curr_buf_pos += copy_len; |
|
remain_buf_size -= copy_len; |
|
} |
|
} |
|
|
|
out2: |
|
kfree(page_buf); |
|
} |
|
|
|
if (retval > 0) |
|
*ppos = pos + retval; |
|
|
|
if (retval < 0 || *ppos >= size) { |
|
file_accessed(filp); |
|
goto out; |
|
} |
|
} else { |
|
retval = 0; |
|
} |
|
|
|
out: |
|
/* Release Lock */ |
|
mutex_unlock(&rawfs_sb->rawfs_lock); |
|
|
|
return retval; |
|
} |
|
|
|
void rawfs_fill_fileinfo_by_dentry(struct dentry *dentry, |
|
struct rawfs_file_info *file_info) |
|
{ |
|
rawfs_fill_file_info(dentry->d_inode, file_info); |
|
file_info->i_parent_folder_id = RAWFS_I(dentry->d_parent->d_inode)->i_id; |
|
|
|
/* Dentry only has 32 bytes, file it with iname */ |
|
strncpy(file_info->i_name, dentry->d_name.name, RAWFS_MAX_FILENAME_LEN+4); |
|
} |
|
EXPORT_SYMBOL_GPL(rawfs_fill_fileinfo_by_dentry); |
|
|
|
void rawfs_fill_file_info(struct inode *inode, |
|
struct rawfs_file_info *file_info) |
|
{ |
|
int name_len; |
|
struct rawfs_inode_info *inode_info = RAWFS_I(inode); |
|
|
|
name_len = strlen(inode_info->i_name); |
|
file_info->i_atime = inode->i_atime; |
|
file_info->i_mtime = inode->i_mtime; |
|
file_info->i_ctime = inode->i_ctime; |
|
file_info->i_uid = inode->i_uid.val; |
|
file_info->i_gid = inode->i_gid.val; |
|
file_info->i_mode = inode->i_mode; |
|
file_info->i_size = inode->i_size; |
|
file_info->i_id = inode_info->i_id; |
|
file_info->i_parent_folder_id = RAWFS_ROOT_DIR_ID; |
|
|
|
strncpy(file_info->i_name, inode_info->i_name, name_len+1); |
|
} |
|
EXPORT_SYMBOL_GPL(rawfs_fill_file_info); |
|
|
|
static ssize_t rawfs_file_write(struct file *filp, struct iovec *iv, loff_t *ppos) |
|
{ |
|
struct super_block *sb = filp->f_path.dentry->d_sb; |
|
struct rawfs_sb_info *rawfs_sb = RAWFS_SB(sb); |
|
struct inode *inode = filp->f_mapping->host; |
|
struct rawfs_inode_info *inode_info = RAWFS_I(inode); |
|
loff_t pos = *ppos; |
|
|
|
struct rawfs_file_info *fi = NULL; |
|
unsigned int curr_file_pos = pos; |
|
unsigned int curr_buf_pos = 0; |
|
int starting_page, total_pages; |
|
int remain_buf_size; |
|
int result; |
|
struct rawfs_file_list_entry *entry; |
|
|
|
ssize_t retval = iv->iov_len; |
|
|
|
/* Get Lock */ |
|
mutex_lock(&rawfs_sb->rawfs_lock); |
|
|
|
RAWFS_PRINT(RAWFS_DBG_FILE, |
|
"%s: %s, pos %lld, len %ld\n", __func__, |
|
inode_info->i_name, pos, (unsigned long)retval); |
|
|
|
fi = kzalloc(sizeof(struct rawfs_file_info), GFP_NOFS); |
|
|
|
if (!fi) { |
|
retval = 0; |
|
goto out; |
|
} |
|
|
|
rawfs_fill_fileinfo_by_dentry(filp->f_path.dentry, fi); |
|
|
|
if ((pos+retval) > RAWFS_FILE_SIZE_LIMIT) { |
|
retval = -EFBIG; |
|
goto out; |
|
} |
|
|
|
/* Update file_info */ |
|
if ((pos+retval) > fi->i_size) |
|
fi->i_size = pos+retval; |
|
|
|
fi->i_chunk_index = 1; |
|
fi->i_chunk_total = S_ISDIR(fi->i_mode) ? 1 : CEILING((unsigned)fi->i_size, |
|
rawfs_sb->page_data_size); |
|
|
|
/* do GC, if the required space is not enough */ |
|
result = rawfs_reserve_space(sb, fi->i_chunk_total); |
|
if (result < 0) { |
|
retval = result; |
|
goto out; |
|
} |
|
|
|
/* get entry from file list */ |
|
entry = rawfs_file_list_get(sb, fi->i_name, fi->i_parent_folder_id); |
|
|
|
if (!entry) { |
|
RAWFS_PRINT(RAWFS_DBG_FILE, |
|
"%s: %s file list entry missing\n", __func__, fi->i_name); |
|
dump_stack(); |
|
goto out; |
|
} |
|
|
|
/* Write File */ |
|
{ |
|
int preceding_pages, rear_pages; |
|
struct rawfs_page *page_buf = NULL; |
|
int i; |
|
|
|
/* Prepare page buffer */ |
|
page_buf = kzalloc(rawfs_sb->page_size, GFP_NOFS); |
|
|
|
if (page_buf == NULL) { |
|
retval = 0; |
|
goto out; |
|
} |
|
|
|
starting_page = rawfs_sb->data_block_free_page_index; |
|
preceding_pages = FLOOR((unsigned)pos, rawfs_sb->page_data_size); |
|
total_pages = CEILING((unsigned)fi->i_size, rawfs_sb->page_data_size); |
|
rear_pages = CEILING((unsigned)pos + iv->iov_len, |
|
rawfs_sb->page_data_size); |
|
remain_buf_size = iv->iov_len; |
|
|
|
RAWFS_PRINT(RAWFS_DBG_FILE, |
|
"%s: %s, preceding %d, rear %d, total %d, remain %d\n", |
|
__func__, inode_info->i_name, preceding_pages, rear_pages, total_pages, |
|
remain_buf_size); |
|
|
|
|
|
/* Step 1: Copy preceding pages, if starting pos is not 0. */ |
|
for (i = 0; i < total_pages; i++) { |
|
/* Read, if necessary, (no need for new files) */ |
|
if ((i <= preceding_pages) || (i >= rear_pages)) |
|
rawfs_sb->dev.read_page(sb, |
|
entry->i_location_block, |
|
entry->i_location_page+i, |
|
page_buf); |
|
|
|
/* Update info */ |
|
memcpy(&page_buf->i_info.i_file_info, fi, |
|
sizeof(struct rawfs_file_info)); |
|
|
|
/* Within Modify Range: Copy Modify */ |
|
if ((i >= preceding_pages) && (i <= rear_pages)) { |
|
int start_in_buf; |
|
int copy_len; |
|
|
|
start_in_buf = (curr_file_pos % rawfs_sb->page_data_size); |
|
copy_len = ((start_in_buf + remain_buf_size) > |
|
rawfs_sb->page_data_size) ? |
|
(rawfs_sb->page_data_size - start_in_buf) : |
|
remain_buf_size; |
|
|
|
if (copy_from_user(&page_buf->i_data[0] + start_in_buf, |
|
(char *)iv->iov_base + curr_buf_pos, copy_len)) { |
|
retval = -EFAULT; |
|
goto out2; |
|
} |
|
|
|
curr_buf_pos += copy_len; |
|
curr_file_pos += copy_len; |
|
remain_buf_size -= copy_len; |
|
|
|
RAWFS_PRINT(RAWFS_DBG_FILE, |
|
"%s: %s, %d, fpos %d, bpos %d, remain %d start %d copy_len %d starting %X, head %X\n", |
|
__func__, inode_info->i_name, i, curr_file_pos, curr_buf_pos, remain_buf_size, |
|
start_in_buf, copy_len, |
|
*(unsigned int *)(&page_buf->i_data[0] + start_in_buf), |
|
*(unsigned int *)(&page_buf->i_data[0])); |
|
} |
|
|
|
rawfs_page_signature(sb, page_buf); |
|
|
|
/* Write */ |
|
rawfs_sb->dev.write_page(sb, |
|
rawfs_sb->data_block, |
|
rawfs_sb->data_block_free_page_index, |
|
page_buf); |
|
|
|
rawfs_sb->data_block_free_page_index++; |
|
fi->i_chunk_index++; |
|
} |
|
|
|
out2: |
|
kfree(page_buf); |
|
} |
|
|
|
if (retval > 0) |
|
*ppos = pos + retval; |
|
|
|
file_accessed(filp); |
|
|
|
/* Update Inode: file size, block, page */ |
|
i_size_write(inode, fi->i_size); |
|
/* TODO: Get inode lock when update */ |
|
inode_info->i_location_block = rawfs_sb->data_block; |
|
inode_info->i_location_page = starting_page; |
|
inode_info->i_location_page_count = total_pages; |
|
|
|
/* update location */ |
|
entry->i_location_block = rawfs_sb->data_block; |
|
entry->i_location_page = starting_page; |
|
entry->i_location_page_count = total_pages; |
|
|
|
out: |
|
kfree(fi); |
|
|
|
/* Release Lock */ |
|
mutex_unlock(&rawfs_sb->rawfs_lock); |
|
|
|
return retval; |
|
} |
|
|
|
static ssize_t rawfs_file_iter(struct kiocb *iocb, struct iov_iter *iter, int type) |
|
{ |
|
struct file *filp = iocb->ki_filp; |
|
loff_t *ppos = &iocb->ki_pos; |
|
ssize_t ret = 0; |
|
|
|
while (iov_iter_count(iter)) { |
|
struct iovec iovec = iov_iter_iovec(iter); |
|
ssize_t nr; |
|
|
|
if (type == READ) |
|
nr = rawfs_file_read(filp, &iovec, ppos); |
|
else |
|
nr = rawfs_file_write(filp, &iovec, ppos); |
|
|
|
RAWFS_PRINT(RAWFS_DBG_FILE, "%s: %s, pos=%lu, nr=%zd", |
|
__func__, type ? "W" : "R", (unsigned long)*ppos, nr); |
|
|
|
if (nr < 0) { |
|
if (!ret) |
|
ret = nr; |
|
break; |
|
} |
|
ret += nr; |
|
if (nr != iovec.iov_len) |
|
break; |
|
iov_iter_advance(iter, nr); |
|
} |
|
return ret; |
|
} |
|
|
|
ssize_t rawfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) |
|
{ |
|
return rawfs_file_iter(iocb, iter, READ); |
|
} |
|
EXPORT_SYMBOL_GPL(rawfs_file_read_iter); |
|
|
|
ssize_t rawfs_file_write_iter(struct kiocb *iocb, struct iov_iter *iter) |
|
{ |
|
return rawfs_file_iter(iocb, iter, WRITE); |
|
} |
|
EXPORT_SYMBOL_GPL(rawfs_file_write_iter); |
|
|
|
/* ----------------------------------------------------------------------------- */ |
|
/* Internal Functions */ |
|
/* ----------------------------------------------------------------------------- */ |
|
int rawfs_reg_file_create(struct inode *dir, struct dentry *dentry, |
|
umode_t mode) |
|
{ |
|
int result = 0; |
|
struct super_block *sb = dentry->d_sb; |
|
struct inode *inode = dentry->d_inode; |
|
struct rawfs_sb_info *rawfs_sb = RAWFS_SB(sb); |
|
struct rawfs_inode_info *inode_info = RAWFS_I(inode); |
|
struct rawfs_page *page_buf = NULL; |
|
|
|
RAWFS_PRINT(RAWFS_DBG_FILE, "rawfs_reg_file_create: i_name=%s\n", |
|
inode_info->i_name); |
|
|
|
page_buf = kzalloc(rawfs_sb->page_size, GFP_NOFS); |
|
|
|
if (page_buf == NULL) { |
|
result = -ENOMEM; |
|
goto out; |
|
} |
|
|
|
result = rawfs_reserve_space(sb, 1); |
|
if (result < 0) |
|
goto out; |
|
|
|
rawfs_fill_fileinfo_by_dentry(dentry, &page_buf->i_info.i_file_info); |
|
|
|
page_buf->i_info.i_file_info.i_mode = mode; |
|
page_buf->i_info.i_file_info.i_size = 0; |
|
page_buf->i_info.i_file_info.i_chunk_total = 1; |
|
page_buf->i_info.i_file_info.i_chunk_index = 1; |
|
|
|
rawfs_page_signature(sb, page_buf); |
|
|
|
rawfs_sb->dev.write_page(sb, |
|
rawfs_sb->data_block, |
|
rawfs_sb->data_block_free_page_index, |
|
page_buf); |
|
|
|
|
|
/* Update inode_info */ |
|
inode_info->i_location_block = rawfs_sb->data_block; |
|
inode_info->i_location_page = rawfs_sb->data_block_free_page_index; |
|
inode->i_size = 0; |
|
|
|
rawfs_sb->data_block_free_page_index++; |
|
|
|
out: |
|
kfree(page_buf); |
|
return result; |
|
|
|
} |
|
EXPORT_SYMBOL_GPL(rawfs_reg_file_create); |
|
|
|
int rawfs_reg_file_delete(struct inode *dir, struct dentry *dentry) |
|
{ |
|
int result = 0; |
|
struct super_block *sb = dentry->d_sb; |
|
struct inode *inode = dentry->d_inode; |
|
struct rawfs_sb_info *rawfs_sb = RAWFS_SB(sb); |
|
struct rawfs_inode_info *inode_info = RAWFS_I(inode); |
|
struct rawfs_page *page_buf = NULL; |
|
|
|
RAWFS_PRINT(RAWFS_DBG_FILE, "rawfs_reg_file_delete %s\n", |
|
dentry->d_name.name); |
|
|
|
page_buf = kzalloc(rawfs_sb->page_size, GFP_NOFS); |
|
|
|
if (page_buf == NULL) { |
|
result = -ENOMEM; |
|
goto out; |
|
} |
|
|
|
/* Do GC, if there's no free_pages */ |
|
if ((rawfs_sb->data_block_free_page_index+1) > rawfs_sb->pages_per_block) { |
|
int reclaimed_size; |
|
|
|
reclaimed_size = rawfs_dev_garbage_collection(sb); |
|
if (reclaimed_size <= 0) { |
|
RAWFS_PRINT(RAWFS_DBG_FILE, |
|
"rawfs_reg_file_delete: Disk full: %d\n", reclaimed_size); |
|
result = -ENOSPC; |
|
goto out; |
|
} |
|
} |
|
|
|
/* Read existing data */ |
|
rawfs_sb->dev.read_page(sb, |
|
inode_info->i_location_block, |
|
inode_info->i_location_page, |
|
page_buf); |
|
|
|
/* Set i_chun_total as zero to mark delete. */ |
|
page_buf->i_info.i_file_info.i_chunk_total = -1; |
|
page_buf->i_info.i_file_info.i_chunk_index = -1; |
|
page_buf->i_info.i_file_info.i_size = 0; |
|
|
|
rawfs_page_signature(sb, page_buf); |
|
|
|
rawfs_sb->dev.write_page(sb, |
|
rawfs_sb->data_block, |
|
rawfs_sb->data_block_free_page_index, |
|
page_buf); |
|
|
|
rawfs_sb->data_block_free_page_index++; |
|
|
|
out: |
|
kfree(page_buf); |
|
|
|
return result; |
|
} |
|
EXPORT_SYMBOL_GPL(rawfs_reg_file_delete); |
|
|
|
/* |
|
* This function is used by rawfs_dir_rename() & rawfs_setattr() |
|
* This function will put the new copeid file into file list. |
|
*/ |
|
int rawfs_reg_file_copy(struct inode *src_dir, struct dentry *src_dentry, |
|
struct inode *dest_dir, struct dentry *dest_dentry) |
|
{ |
|
int result = 0; |
|
int starting_page; |
|
struct inode *src_inode = src_dentry->d_inode; |
|
struct super_block *sb = src_dentry->d_sb; |
|
struct rawfs_sb_info *rawfs_sb = RAWFS_SB(sb); |
|
struct rawfs_file_info *fi = NULL; |
|
struct rawfs_file_list_entry *src_info; |
|
|
|
if ((dest_dir == NULL) && (dest_dentry == NULL)) { |
|
RAWFS_PRINT(RAWFS_DBG_FILE, "rawfs_reg_file_copy (setattr) %s\n", |
|
src_dentry->d_name.name); |
|
dest_dir = src_dir; |
|
dest_dentry = src_dentry; |
|
} else { |
|
RAWFS_PRINT(RAWFS_DBG_FILE, "rawfs_reg_file_copy (rename) %s -> %s\n", |
|
src_dentry->d_name.name, dest_dentry->d_name.name); |
|
} |
|
|
|
fi = kzalloc(sizeof(struct rawfs_file_info), GFP_NOFS); |
|
|
|
if (!fi) { |
|
result = -ENOMEM; |
|
goto out; |
|
} |
|
|
|
rawfs_fill_file_info(src_inode, fi); |
|
fi->i_parent_folder_id = RAWFS_I(dest_dir)->i_id; |
|
|
|
strncpy(fi->i_name, dest_dentry->d_name.name, |
|
strlen(dest_dentry->d_name.name)+1); |
|
|
|
fi->i_chunk_index = 1; |
|
fi->i_chunk_total = S_ISDIR(fi->i_mode) ? 1 : CEILING((unsigned)fi->i_size, |
|
rawfs_sb->page_data_size); |
|
|
|
/* do GC, if the required space is not enough */ |
|
result = rawfs_reserve_space(sb, fi->i_chunk_total); |
|
if (result < 0) |
|
goto out; |
|
|
|
/* |
|
* Get soruce location & starting page again, |
|
* it might be change to new location after GC |
|
*/ |
|
src_info = rawfs_file_list_get(sb, |
|
src_dentry->d_name.name, |
|
RAWFS_I(src_dir)->i_id); |
|
|
|
starting_page = rawfs_sb->data_block_free_page_index; |
|
|
|
if (!src_info) { |
|
RAWFS_PRINT(RAWFS_DBG_FILE, |
|
"rawfs_reg_file_copy: src file %s @ %X missing after GC\n", |
|
src_dentry->d_name.name, |
|
RAWFS_I(src_dir)->i_id); |
|
goto out; |
|
} |
|
|
|
{ |
|
int total_pages; |
|
struct rawfs_page *page_buf = NULL; |
|
int i; |
|
|
|
page_buf = kzalloc(rawfs_sb->page_size, GFP_NOFS); |
|
|
|
if (page_buf == NULL) { |
|
result = -ENOMEM; |
|
goto out; |
|
} |
|
|
|
total_pages = S_ISDIR(fi->i_mode) ? 1 : CEILING((unsigned)fi->i_size, |
|
rawfs_sb->page_data_size); |
|
|
|
for (i = 0; i < total_pages; i++) { |
|
RAWFS_PRINT(RAWFS_DBG_FILE, |
|
"rawfs_reg_file_copy copy %d,%d to %d,%d\n", |
|
src_info->i_location_block, |
|
src_info->i_location_page+i, |
|
rawfs_sb->data_block, |
|
rawfs_sb->data_block_free_page_index); |
|
|
|
rawfs_sb->dev.read_page(sb, |
|
src_info->i_location_block, |
|
src_info->i_location_page+i, |
|
page_buf); |
|
|
|
/* Update info */ |
|
memcpy(&page_buf->i_info.i_file_info, fi, |
|
sizeof(struct rawfs_file_info)); |
|
|
|
rawfs_page_signature(sb, page_buf); |
|
|
|
rawfs_sb->dev.write_page(sb, |
|
rawfs_sb->data_block, |
|
rawfs_sb->data_block_free_page_index, |
|
page_buf); |
|
|
|
rawfs_sb->data_block_free_page_index++; |
|
fi->i_chunk_index++; |
|
} |
|
|
|
kfree(page_buf); |
|
} |
|
|
|
/* |
|
* Add to file list, so that rawf_dir_rename can get the new |
|
* location of the file from list. |
|
*/ |
|
rawfs_file_list_add(sb, fi, rawfs_sb->data_block, starting_page); |
|
|
|
out: |
|
kfree(fi); |
|
return result; |
|
} |
|
EXPORT_SYMBOL_GPL(rawfs_reg_file_copy); |
|
|
|
/* Read page, by pass page cache, always read from device. */ |
|
static int rawfs_readpage_nolock(struct file *filp, struct page *page) |
|
{ |
|
struct super_block *sb = filp->f_path.dentry->d_sb; |
|
struct inode *inode = filp->f_path.dentry->d_inode; |
|
struct rawfs_sb_info *rawfs_sb = RAWFS_SB(sb); |
|
struct rawfs_inode_info *inode_info = RAWFS_I(inode); |
|
loff_t pos; |
|
unsigned int curr_file_pos; |
|
unsigned int curr_buf_pos = 0; |
|
int remain_buf_size; |
|
unsigned size; |
|
int retval; |
|
unsigned char *pg_buf; |
|
|
|
pg_buf = kmap(page); |
|
|
|
/* TODO: check pg_buf */ |
|
|
|
size = i_size_read(inode); |
|
curr_file_pos = pos = page->index << PAGE_CACHE_SHIFT; |
|
retval = PAGE_CACHE_SIZE; |
|
|
|
RAWFS_PRINT(RAWFS_DBG_FILE, |
|
"rawfs_readpage %s @ folder %X, page %ld, pos %lld, len %d, filesize: %d, pg_buf %lx\n", |
|
inode_info->i_name, |
|
inode_info->i_parent_folder_id, |
|
page->index, |
|
pos, retval, size, (unsigned long)pg_buf); |
|
|
|
if ((retval + pos) >= size) |
|
retval = size - pos; |
|
|
|
if (pos < size) { |
|
{ |
|
int preceding_pages, rear_pages; |
|
struct rawfs_page *page_buf = NULL; |
|
int i; |
|
|
|
/* Prepare page buffer */ |
|
page_buf = kzalloc(rawfs_sb->page_size, GFP_NOFS); |
|
|
|
if (page_buf == NULL) { |
|
retval = 0; |
|
goto out; |
|
} |
|
|
|
preceding_pages = FLOOR((unsigned)pos, rawfs_sb->page_data_size); |
|
rear_pages = CEILING((unsigned)pos + retval, |
|
rawfs_sb->page_data_size); |
|
remain_buf_size = retval; |
|
|
|
RAWFS_PRINT(RAWFS_DBG_FILE, |
|
"rawfs_readpage %s, preceding %d, rear %d, remain %d ", |
|
inode_info->i_name, preceding_pages, rear_pages, |
|
remain_buf_size); |
|
|
|
/* Step 1: Copy preceding pages, if starting pos is not 0. */ |
|
for (i = preceding_pages; i < rear_pages; i++) { |
|
/* Read page */ |
|
rawfs_sb->dev.read_page(sb, |
|
inode_info->i_location_block, |
|
inode_info->i_location_page+i, |
|
page_buf); |
|
|
|
/* Copy required parts */ |
|
{ |
|
int start_in_buf; |
|
int copy_len; |
|
|
|
start_in_buf = (curr_file_pos % rawfs_sb->page_data_size); |
|
copy_len = ((start_in_buf + remain_buf_size) > |
|
rawfs_sb->page_data_size) ? |
|
(rawfs_sb->page_data_size - start_in_buf) : |
|
remain_buf_size; |
|
|
|
memcpy(pg_buf + curr_buf_pos, |
|
&page_buf->i_data[0] + start_in_buf, copy_len); |
|
|
|
RAWFS_PRINT(RAWFS_DBG_FILE, |
|
"rawfs_readpage %s, %d, curr %d, remain %d start %d copy %d pattern %X", |
|
inode_info->i_name, i, curr_buf_pos, remain_buf_size, |
|
start_in_buf, copy_len, |
|
*(unsigned int *)(&page_buf->i_data[0] + start_in_buf)); |
|
|
|
curr_buf_pos += copy_len; |
|
remain_buf_size -= copy_len; |
|
} |
|
} |
|
|
|
kfree(page_buf); |
|
} |
|
} else { |
|
retval = 0; |
|
} |
|
|
|
out: |
|
|
|
SetPageUptodate(page); |
|
ClearPageError(page); |
|
flush_dcache_page(page); |
|
|
|
kunmap(page); |
|
unlock_page(page); |
|
|
|
return 0; |
|
} |
|
|
|
|
|
int rawfs_readpage(struct file *filp, struct page *page) |
|
{ |
|
int result; |
|
|
|
result = rawfs_readpage_nolock(filp, page); |
|
unlock_page(page); |
|
|
|
return result; |
|
} |
|
EXPORT_SYMBOL_GPL(rawfs_readpage); |
|
|
|
int rawfs_write_begin(struct file *filp, struct address_space *mapping, |
|
loff_t pos, unsigned len, unsigned flags, |
|
struct page **pagep, void **fsdata) |
|
{ |
|
RAWFS_PRINT(RAWFS_DBG_FILE, "rawfs_write_begin: unexpected call !"); |
|
WARN_ON(1); |
|
return simple_write_begin(filp, mapping, pos, len, flags, pagep, fsdata); |
|
} |
|
EXPORT_SYMBOL_GPL(rawfs_write_begin); |
|
|
|
int rawfs_write_end(struct file *filp, struct address_space *mapping, |
|
loff_t pos, unsigned len, unsigned copied, |
|
struct page *pg, void *fsdata) |
|
{ |
|
RAWFS_PRINT(RAWFS_DBG_FILE, "rawfs_write_end: unexpected call !"); |
|
WARN_ON(1); |
|
return simple_write_end(filp, mapping, pos, len, copied, pg, fsdata); |
|
} |
|
EXPORT_SYMBOL_GPL(rawfs_write_end); |
|
|
|
MODULE_AUTHOR("Perry Hsu <perry.hsu@mediatek.com>"); |
|
MODULE_DESCRIPTION("RAW file system for NAND flash"); |
|
MODULE_LICENSE("GPL");
|
|
|