Allocates blocks for files ending un-PG-aligned
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 5 Oct 2010 23:12:46 +0000 (16:12 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:35:55 +0000 (17:35 -0700)
Side note: stat() should return the ideal blocksize to use in file IO,
not the size of the blocks reported.  The size of "blocks" (and
i_blocks) is 512B.  man 2 stat for details.

Documentation/vfs.txt
kern/src/ext2fs.c
tests/stat.c [new file with mode: 0644]

index f0c415b..0410c51 100644 (file)
@@ -206,6 +206,26 @@ This also applies to trying to write to a block beyond the EOF.  If the request
 hits the page cache and readpage(), it's because it was already checked and
 cleared in another part of the VFS, such as in generic_file_write().
 
+Files That End Before a Page Boundary
+--------------------------
+So what if we have a file that is only 1024 bytes.  When we read it in, we'll
+have a whole page added to the page cache, with the extra 3K 0'd.  When we go to
+write it back, it will write back the 1K, and ignore the rest.  But what if we
+extend the file later?  That page is already in the page cache, but the buffer
+heads aren't tracking the new 3K.  When that page gets evicted, only the 1K will
+write back.
+
+There are two ways to deal with this: 1) When we extend the file, check and see
+if it is in the middle of a page, and if so, alloc the blocks (all FS
+specific!), and adjust the BHs to map the new data.  2) Preallocate the
+remaining blocks and stitch up the BH mapping at readpage (which is in the
+specific FS).  This way, we reserve the blocks (but don't have to actually read
+them in - we just mark the buffer as dirty).  When we grow a file, we don't need
+to worry about any of these details.  We just make sure the page map has the
+page, and not whether or not it's a half-page that needs a FS-specific solution.
+I'm going with #2 for now.  Note that while the blocks are allocated, the file's
+size is still 1024B.
+
 Kref, Dentries, Inodes, and Files (or "I don't see why it's like X, but..."
 --------------------------
 There are multiple dentries pointing to an inode.  The dentries are (or will be)
index aaeefb0..1354cf8 100644 (file)
@@ -571,11 +571,6 @@ int ext2_mappage(struct page_map *pm, struct page *page)
        unsigned int blk_per_pg = PGSIZE / inode->i_sb->s_blocksize;
        unsigned int sct_per_blk = inode->i_sb->s_blocksize / bdev->b_sector_sz;
        uint32_t ino_blk_num, fs_blk_num = 0, *fs_blk_slot;
-       /* Can't use i_blocks for this.  We could have a file hole, so it's not
-        * about how many blocks there are, but about how many FS blocks there ought
-        * to be for this object/file.  Also note that i_blocks is measured in 512B
-        * chunks. */
-       uint32_t last_ino_blk_num = inode->i_size / inode->i_sb->s_blocksize;
 
        bh = kmem_cache_alloc(bh_kcache, 0);
        page->pg_private = bh;
@@ -603,14 +598,17 @@ int ext2_mappage(struct page_map *pm, struct page *page)
                        ext2_dirty_metablock(fs_blk_slot);
                        /* the block is still on disk, and we don't want its contents */
                        bh->bh_flags = BH_NEEDS_ZEROED;                 /* talking to readpage */
+                       /* update our num blocks, with 512B each "block" (ext2-style) */
+                       inode->i_blocks += inode->i_sb->s_blocksize >> 9;
                } else {        /* there is a block there already */
                        fs_blk_num = *fs_blk_slot;
                }
                ext2_put_metablock(fs_blk_slot);
                bh->bh_sector = fs_blk_num * sct_per_blk;
                bh->bh_nr_sector = sct_per_blk;
-               /* Stop if we're the last block in the inode or the last in the page */
-               if ((ino_blk_num == last_ino_blk_num) || (i == blk_per_pg - 1)) {
+               /* Stop if we're the last block in the page.  We could be going beyond
+                * the end of the file, in which case the next BHs will be zeroed. */
+               if (i == blk_per_pg - 1) {
                        bh->bh_next = 0;
                        break;
                } else {
diff --git a/tests/stat.c b/tests/stat.c
new file mode 100644 (file)
index 0000000..84e3c8b
--- /dev/null
@@ -0,0 +1,38 @@
+#include <stdio.h> 
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+int main(int argc, char *argv[]) 
+{ 
+       int retval;
+       if (argc < 2) {
+               printf("Prints out stats for a file\n");
+               printf("Usage: stat FILENAME\n");
+               return -1;
+       }
+       struct stat st = {0};
+       retval = stat(argv[1], &st);
+       if (retval < 0) {
+               perror("Stat failed");
+       } else {
+               printf("STAT RESULTS\n---------------------\n");
+               printf("dev       : %d\n", st.st_dev);
+               printf("ino       : %d\n", st.st_ino);
+               printf("mode      : %d\n", st.st_mode);
+               printf("nlink     : %d\n", st.st_nlink);
+               printf("uid       : %d\n", st.st_uid);
+               printf("gid       : %d\n", st.st_gid);
+               printf("rdev      : %d\n", st.st_rdev);
+               printf("size      : %d\n", st.st_size);
+               printf("blksize   : %d\n", st.st_blksize);
+               printf("blocks    : %d\n", st.st_blocks);
+               printf("atime     : %d\n", st.st_atime);
+               printf("mtime     : %d\n", st.st_mtime);
+               printf("ctime     : %d\n", st.st_ctime);
+       }
+}