From patchwork Sat Sep 22 04:00:57 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Zheng Liu X-Patchwork-Id: 186082 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 67AC02C009A for ; Sat, 22 Sep 2012 13:51:21 +1000 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753064Ab2IVDvR (ORCPT ); Fri, 21 Sep 2012 23:51:17 -0400 Received: from mail-pb0-f46.google.com ([209.85.160.46]:34114 "EHLO mail-pb0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752976Ab2IVDvP (ORCPT ); Fri, 21 Sep 2012 23:51:15 -0400 Received: by mail-pb0-f46.google.com with SMTP id rr4so3873873pbb.19 for ; Fri, 21 Sep 2012 20:51:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references; bh=JhAI6fYc4ZjzNj3Pn9xjME7eyp8qfQlGjfTwFVSOgK4=; b=ozINIulrtv2WUrJYLI9Ygnbe3EQs52pWw5ReEBHzn94U1VHuAlH9blZoKBODk0bFTm uhu9DuRFhVHrVIo0SSVTnwuGPtHAkucc+FbKCVnBtm0RfMN0xaQuvflGEmFRF+gi/cOK VWnlpEN4dZ+ejPLsUOvu3QNczeLlBHmkWyL9jK6xZkCSFKKqIel+IC3CJu9nEoOlNV4r Uy1Qpo//1I21DlWjnu/FACTuDghA54OHFk2p8KaeZUIe7iv+7gAeQCnlkraAc7eOkI/M J90iueFwYwVv7gOnlp7zTWucABuxM99yrB0MBaRXZitNdWFQPyH56eksod1Spe5Vkdcx S7KQ== Received: by 10.68.240.36 with SMTP id vx4mr20239247pbc.89.1348285875310; Fri, 21 Sep 2012 20:51:15 -0700 (PDT) Received: from lz-desktop.hz.ali.com ([182.92.247.2]) by mx.google.com with ESMTPS id e9sm5079534pay.34.2012.09.21.20.51.13 (version=TLSv1/SSLv3 cipher=OTHER); Fri, 21 Sep 2012 20:51:15 -0700 (PDT) From: Zheng Liu To: linux-ext4@vger.kernel.org Cc: tytso@mit.edu, Zheng Liu Subject: [PATCH 09/21 v5] libext2fs: handle inline data in read/write function Date: Sat, 22 Sep 2012 12:00:57 +0800 Message-Id: <1348286469-31690-10-git-send-email-wenqing.lz@taobao.com> X-Mailer: git-send-email 1.7.12.rc2.18.g61b472e In-Reply-To: <1348286469-31690-1-git-send-email-wenqing.lz@taobao.com> References: <1348286469-31690-1-git-send-email-wenqing.lz@taobao.com> Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org From: Zheng Liu Ext2fs_read/write_inline_data functions copy inline_data from/to user's buffer directly. When we want to write some data to a file with inline_data, ext2fs_try_to_write_inline_data is called. In this function, it firstly check the length of data. If the data can be saved in innode, this function will create a new extend attribute to record information of inline_data, and copy all data into this inode. The following commands in debugfs can handle inline_data feature after applying this patch: - dump - cat - rdump - write Signed-off-by: Zheng Liu --- lib/ext2fs/ext2_err.et.in | 3 + lib/ext2fs/ext2fs.h | 7 ++ lib/ext2fs/fileio.c | 19 +++++- lib/ext2fs/inline_data.c | 170 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 198 insertions(+), 1 deletions(-) diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in index 537d840..17de695 100644 --- a/lib/ext2fs/ext2_err.et.in +++ b/lib/ext2fs/ext2_err.et.in @@ -476,6 +476,9 @@ ec EXT2_ET_UNKNOWN_CSUM, ec EXT2_ET_MMP_CSUM_INVALID, "MMP block checksum does not match MMP block" +ec EXT2_ET_INLINE_DATA_NO_SPACE, + "No free space in inline data" + ec EXT2_ET_BAD_EXTRA_SIZE, "Bad inode extra isizevalue" diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h index 779eca5..801e2dd 100644 --- a/lib/ext2fs/ext2fs.h +++ b/lib/ext2fs/ext2fs.h @@ -1347,6 +1347,13 @@ extern errcode_t ext2fs_inline_data_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t ino, const char *name); extern errcode_t ext2fs_convert_inline_data(ext2_filsys fs, ext2_ino_t ino, void *priv_data); +extern errcode_t ext2fs_read_inline_data(ext2_filsys fs, ext2_ino_t ino, + char *buf); +extern errcode_t ext2fs_write_inline_data(ext2_filsys fs, ext2_ino_t ino, + char *buf); +extern errcode_t ext2fs_try_to_write_inline_data(ext2_filsys fs, ext2_ino_t ino, + const void *buf, unsigned int nbytes, + unsigned int *written); /* inode.c */ extern errcode_t ext2fs_flush_icache(ext2_filsys fs); diff --git a/lib/ext2fs/fileio.c b/lib/ext2fs/fileio.c index 1f7002c..d751666 100644 --- a/lib/ext2fs/fileio.c +++ b/lib/ext2fs/fileio.c @@ -163,7 +163,8 @@ static errcode_t sync_buffer_position(ext2_file_t file) b = file->pos / file->fs->blocksize; if (b != file->blockno) { - retval = ext2fs_file_flush(file); + if (!ext2fs_inode_has_inline_data(file->fs, file->ino)) + retval = ext2fs_file_flush(file); if (retval) return retval; file->flags &= ~EXT2_FILE_BUF_VALID; @@ -186,6 +187,12 @@ static errcode_t load_buffer(ext2_file_t file, int dontfill) ext2_filsys fs = file->fs; errcode_t retval; + /* We first handle inline_data */ + if (file->inode.i_flags & EXT4_INLINE_DATA_FL) { + retval = ext2fs_read_inline_data(fs, file->ino, file->buf); + return retval; + } + if (!(file->flags & EXT2_FILE_BUF_VALID)) { retval = ext2fs_bmap2(fs, file->ino, &file->inode, BMAP_BUFFER, 0, file->blockno, 0, @@ -280,6 +287,16 @@ errcode_t ext2fs_file_write(ext2_file_t file, const void *buf, if (!(file->flags & EXT2_FILE_WRITE)) return EXT2_ET_FILE_RO; + if (fs->super->s_feature_incompat & EXT4_FEATURE_INCOMPAT_INLINE_DATA) { + retval = ext2fs_try_to_write_inline_data(fs, file->ino, buf, + nbytes, written); + if (!retval) + return retval; + if (retval != EXT2_ET_INLINE_DATA_NO_SPACE) + return retval; + retval = 0; + } + while (nbytes > 0) { retval = sync_buffer_position(file); if (retval) diff --git a/lib/ext2fs/inline_data.c b/lib/ext2fs/inline_data.c index ef7b216..a847b68 100644 --- a/lib/ext2fs/inline_data.c +++ b/lib/ext2fs/inline_data.c @@ -24,6 +24,7 @@ static int ext2fs_iget_extra_inode(ext2_filsys fs, struct ext2_inode_large *inod struct inline_data *data); static void *ext2fs_get_inline_xattr_pos(struct ext2_inode_large *inode, struct inline_data *data); +static int ext2fs_get_max_inline_size(ext2_filsys fs, struct ext2_inode_large *inode); static void ext2fs_inline_data_finish_convert(ext2_filsys fs, ext2_ino_t ino, char *target, void *buf, int inline_size); @@ -314,6 +315,73 @@ out: return retval & BLOCK_ERROR ? ctx->errcode : 0; } +errcode_t ext2fs_read_inline_data(ext2_filsys fs, ext2_ino_t ino, char *buf) +{ + struct ext2_inode_large *inode; + struct inline_data data; + errcode_t retval = 0; + int inline_size; + + retval = ext2fs_get_mem(EXT2_INODE_SIZE(fs->super), &inode); + if (retval) + return retval; + + retval = ext2fs_read_inode_full(fs, ino, (void *)inode, + EXT2_INODE_SIZE(fs->super)); + if (retval) + goto err; + + retval = ext2fs_iget_extra_inode(fs, inode, &data); + if (retval) + goto err; + + inline_size = data.inline_size; + + memcpy(buf, (void *)inode->i_block, EXT4_MIN_INLINE_DATA_SIZE); + if (inline_size > EXT4_MIN_INLINE_DATA_SIZE) + memcpy(buf + EXT4_MIN_INLINE_DATA_SIZE, + ext2fs_get_inline_xattr_pos(inode, &data), + inline_size - EXT4_MIN_INLINE_DATA_SIZE); + +err: + ext2fs_free_mem(&inode); + return retval; +} + +errcode_t ext2fs_write_inline_data(ext2_filsys fs, ext2_ino_t ino, char *buf) +{ + struct ext2_inode_large *inode; + struct inline_data data; + errcode_t retval = 0; + int inline_size; + + retval = ext2fs_get_mem(EXT2_INODE_SIZE(fs->super), &inode); + if (retval) + return retval; + + retval = ext2fs_read_inode_full(fs, ino, (void *)inode, + EXT2_INODE_SIZE(fs->super)); + if (retval) + goto err; + + retval = ext2fs_iget_extra_inode(fs, inode, &data); + if (retval) + goto err; + + inline_size = data.inline_size; + + memcpy((void *)inode->i_block, buf, EXT4_MIN_INLINE_DATA_SIZE); + if (inline_size > EXT4_MIN_INLINE_DATA_SIZE) + memcpy(ext2fs_get_inline_xattr_pos(inode, &data), + buf + EXT4_MIN_INLINE_DATA_SIZE, + inline_size - EXT4_MIN_INLINE_DATA_SIZE); + + retval = ext2fs_write_inode_full(fs, ino, (void *)inode, + EXT2_INODE_SIZE(fs->super)); +err: + ext2fs_free_mem(&inode); + return retval; +} errcode_t ext2fs_convert_inline_data(ext2_filsys fs, ext2_ino_t ino, void *priv_data) @@ -519,3 +587,105 @@ out: ext2fs_free_mem(&inode); return retval; } + +static int ext2fs_get_max_inline_size(ext2_filsys fs, struct ext2_inode_large *inode) +{ + struct ext2_ext_attr_entry *entry; + struct ext2_ext_attr_ibody_header *header; + struct inline_data data; + errcode_t retval = 0; + int free, min_offs; + + min_offs = EXT2_INODE_SIZE(fs->super) - + EXT2_GOOD_OLD_INODE_SIZE - + inode->i_extra_isize - + sizeof(struct ext2_ext_attr_ibody_header); + + header = IHDR(inode); + entry = IFIRST(header); + + for (; !EXT2_EXT_IS_LAST_ENTRY(entry); + entry = EXT2_EXT_ATTR_NEXT(entry)) { + if (!entry->e_value_block && entry->e_value_size) { + size_t offs = ext2fs_le16_to_cpu(entry->e_value_offs); + if (offs < min_offs) + min_offs = offs; + } + } + free = min_offs - + ((void *)entry - (void *)IFIRST(header)) - sizeof(__u32); + + /* + * We try to get inline data offset, but maybe it doesn't be + * created. So we ignore this error. + */ + retval = ext2fs_iget_extra_inode(fs, inode, &data); + if (retval && retval != EXT2_ET_BAD_EXT_ATTR_MAGIC) + return 0; + + if (data.inline_off) { + entry = (struct ext2_ext_attr_entry *) + ((void *)inode + data.inline_off); + free += ext2fs_le32_to_cpu(entry->e_value_size); + goto out; + } + + free -= EXT2_EXT_ATTR_LEN(strlen(EXT4_EXT_ATTR_SYSTEM_DATA)); + + if (free > EXT2_EXT_ATTR_ROUND) + free = EXT2_EXT_ATTR_SIZE(free - EXT2_EXT_ATTR_ROUND); + else + free = 0; + +out: + return free + EXT4_MIN_INLINE_DATA_SIZE; +} + +errcode_t ext2fs_try_to_write_inline_data(ext2_filsys fs, ext2_ino_t ino, + const void *buf, unsigned int nbytes, + unsigned int *written) +{ + struct ext2_inode_large *inode; + struct inline_data data; + errcode_t retval = 0; + int inline_size = 0; + + retval = ext2fs_get_mem(EXT2_INODE_SIZE(fs->super), &inode); + if (retval) + return retval; + retval = ext2fs_read_inode_full(fs, ino, (void *)inode, + EXT2_INODE_SIZE(fs->super)); + if (retval) + goto out; + + if (nbytes > ext2fs_get_max_inline_size(fs, inode)) { + retval = EXT2_ET_INLINE_DATA_NO_SPACE; + goto out; + } + + retval = ext2fs_create_inline_data(fs, inode, nbytes); + if (retval) + goto out; + + retval = ext2fs_iget_extra_inode(fs, inode, &data); + if (retval) + goto out; + + inline_size = data.inline_size; + + memcpy((void *)inode->i_block, buf, EXT4_MIN_INLINE_DATA_SIZE); + if (inline_size > EXT4_MIN_INLINE_DATA_SIZE) + memcpy(ext2fs_get_inline_xattr_pos(inode, &data), + buf + EXT4_MIN_INLINE_DATA_SIZE, + inline_size - EXT4_MIN_INLINE_DATA_SIZE); + + inode->i_flags &= ~EXT4_EXTENTS_FL; + inode->i_flags |= EXT4_INLINE_DATA_FL; + + retval = ext2fs_write_inode_full(fs, ino, (void *)inode, + EXT2_INODE_SIZE(fs->super)); + +out: + ext2fs_free_mem(&inode); + return retval; +}