[PATCH]: PG_uptodate flag and read_cache_page()....

From: Trond Myklebust (trond.myklebust@fys.uio.no)
Date: Thu May 18 2000 - 16:18:39 EST


The 'read_cache_page()' interface at the moment has a problem: If the
page filler routine fails, then despite an ERR_PTR() being returned,
the non-uptodate page is left in the page cache. Subsequent calls to
read_cache_page() will then fail to retry to fill the page, because
the current implementation is unaware of the PG_uptodate flag.

The following patch provides a drop-in alternative to
read_cache_page() for those functions in which the above may prove a
problem. It simply wraps read_cache_page() with a routine which tries
to refill the page if the PG_uptodate flag is not set.

I've used this drop-in routine as a replacement for read_cache_page()
in the NFS symlink code, where we currently have a vulnerability if,
say, the user aborts the readlink(). I also slipped
the same routine into the readdir code.

Cheers,
  Trond

diff -u --recursive --new-file linux-2.3.99-pre9-2/fs/nfs/dir.c linux-2.3.99-pre9-2-uptodate/fs/nfs/dir.c
--- linux-2.3.99-pre9-2/fs/nfs/dir.c Tue May 16 09:30:15 2000
+++ linux-2.3.99-pre9-2-uptodate/fs/nfs/dir.c Tue May 16 19:30:56 2000
@@ -192,14 +192,12 @@
                 desc->page = NULL;
         }
 
- page = read_cache_page(&inode->i_data, index,
+ page = update_cache_page(&inode->i_data, index,
                                (filler_t *)nfs_readdir_filler, desc);
         if (IS_ERR(page)) {
                 status = PTR_ERR(page);
                 goto out;
         }
- if (!Page_Uptodate(page))
- goto read_error;
 
         /* NOTE: Someone else may have changed the READDIRPLUS flag */
         desc->plus = NFS_USE_READDIRPLUS(inode);
@@ -211,9 +209,6 @@
  out:
         dfprintk(VFS, "NFS: find_dirent_page() returns %d\n", status);
         return status;
- read_error:
- page_cache_release(page);
- return -EIO;
 }
 
 /*
diff -u --recursive --new-file linux-2.3.99-pre9-2/fs/nfs/symlink.c linux-2.3.99-pre9-2-uptodate/fs/nfs/symlink.c
--- linux-2.3.99-pre9-2/fs/nfs/symlink.c Fri Apr 7 22:38:00 2000
+++ linux-2.3.99-pre9-2-uptodate/fs/nfs/symlink.c Tue May 16 19:30:31 2000
@@ -59,19 +59,14 @@
         u32 *p;
 
         /* Caller revalidated the directory inode already. */
- page = read_cache_page(&inode->i_data, 0,
+ page = update_cache_page(&inode->i_data, 0,
                                 (filler_t *)nfs_symlink_filler, dentry);
         if (IS_ERR(page))
                 goto read_failed;
- if (!Page_Uptodate(page))
- goto getlink_read_error;
         *ppage = page;
         p = (u32 *) kmap(page);
         return (char*)(p+1);
                 
-getlink_read_error:
- page_cache_release(page);
- return ERR_PTR(-EIO);
 read_failed:
         return (char*)page;
 }
diff -u --recursive --new-file linux-2.3.99-pre9-2/include/linux/pagemap.h linux-2.3.99-pre9-2-uptodate/include/linux/pagemap.h
--- linux-2.3.99-pre9-2/include/linux/pagemap.h Tue May 16 18:42:16 2000
+++ linux-2.3.99-pre9-2-uptodate/include/linux/pagemap.h Tue May 16 19:28:56 2000
@@ -125,4 +125,6 @@
 
 extern struct page *read_cache_page(struct address_space *, unsigned long,
                                 filler_t *, void *);
+extern struct page *update_cache_page(struct address_space *, unsigned long,
+ filler_t *, void *);
 #endif
diff -u --recursive --new-file linux-2.3.99-pre9-2/kernel/ksyms.c linux-2.3.99-pre9-2-uptodate/kernel/ksyms.c
--- linux-2.3.99-pre9-2/kernel/ksyms.c Tue May 9 07:21:57 2000
+++ linux-2.3.99-pre9-2-uptodate/kernel/ksyms.c Tue May 16 19:29:24 2000
@@ -240,6 +240,7 @@
 EXPORT_SYMBOL(__find_lock_page);
 EXPORT_SYMBOL(grab_cache_page);
 EXPORT_SYMBOL(read_cache_page);
+EXPORT_SYMBOL(update_cache_page);
 EXPORT_SYMBOL(vfs_readlink);
 EXPORT_SYMBOL(vfs_follow_link);
 EXPORT_SYMBOL(page_readlink);
diff -u --recursive --new-file linux-2.3.99-pre9-2/mm/filemap.c linux-2.3.99-pre9-2-uptodate/mm/filemap.c
--- linux-2.3.99-pre9-2/mm/filemap.c Fri May 12 04:10:53 2000
+++ linux-2.3.99-pre9-2-uptodate/mm/filemap.c Tue May 16 19:27:51 2000
@@ -2383,6 +2383,35 @@
         return page;
 }
 
+/*
+ * Read into the page cache. If a page already exists,
+ * and Page_Uptodate() is not set, try to fill the page.
+ */
+struct page *update_cache_page(struct address_space *mapping,
+ unsigned long index,
+ int (*filler)(void *,struct page*),
+ void *data)
+{
+ struct page *page = read_cache_page(mapping, index, filler, data);
+ int err;
+
+ if (IS_ERR(page) || Page_Uptodate(page))
+ goto out;
+
+ lock_page(page);
+ if (Page_Uptodate(page)) {
+ UnlockPage(page);
+ goto out;
+ }
+ err = filler(data, page);
+ if (err < 0) {
+ page_cache_release(page);
+ page = ERR_PTR(err);
+ }
+ out:
+ return page;
+}
+
 static inline void remove_suid(struct inode *inode)
 {
         unsigned int mode;

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Tue May 23 2000 - 21:00:16 EST