]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] minix directory handling
authorAndrew Morton <akpm@zip.com.au>
Tue, 30 Apr 2002 06:52:26 +0000 (23:52 -0700)
committerLinus Torvalds <torvalds@home.transmeta.com>
Tue, 30 Apr 2002 06:52:26 +0000 (23:52 -0700)
Convert minixfs directory code to not rely on the state of data outside
i_size.

fs/minix/dir.c

index 18022e977903aa00491ca4f3117c5090d4fb14dc..a89969d8371d5d70933c2a1d37ec159331fc0718 100644 (file)
@@ -24,6 +24,20 @@ static inline void dir_put_page(struct page *page)
        page_cache_release(page);
 }
 
+/*
+ * Return the offset into page `page_nr' of the last valid
+ * byte in that page, plus one.
+ */
+static unsigned
+minix_last_byte(struct inode *inode, unsigned long page_nr)
+{
+       unsigned last_byte = PAGE_CACHE_SIZE;
+
+       if (page_nr == (inode->i_size >> PAGE_CACHE_SHIFT))
+               last_byte = inode->i_size & (PAGE_CACHE_SIZE - 1);
+       return last_byte;
+}
+
 static inline unsigned long dir_pages(struct inode *inode)
 {
        return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT;
@@ -90,7 +104,7 @@ static int minix_readdir(struct file * filp, void * dirent, filldir_t filldir)
                        continue;
                kaddr = (char *)page_address(page);
                p = kaddr+offset;
-               limit = kaddr + PAGE_CACHE_SIZE - chunk_size;
+               limit = kaddr + minix_last_byte(inode, n) - chunk_size;
                for ( ; p <= limit ; p = minix_next_entry(p, sbi)) {
                        minix_dirent *de = (minix_dirent *)p;
                        if (de->inode) {
@@ -154,7 +168,7 @@ minix_dirent *minix_find_entry(struct dentry *dentry, struct page **res_page)
 
                kaddr = (char*)page_address(page);
                de = (struct minix_dir_entry *) kaddr;
-               kaddr += PAGE_CACHE_SIZE - sbi->s_dirsize;
+               kaddr += minix_last_byte(dir, n) - sbi->s_dirsize;
                for ( ; (char *) de <= kaddr ; de = minix_next_entry(de,sbi)) {
                        if (!de->inode)
                                continue;
@@ -185,23 +199,37 @@ int minix_add_link(struct dentry *dentry, struct inode *inode)
        unsigned from, to;
        int err;
 
-       /* We take care of directory expansion in the same loop */
+       /*
+        * We take care of directory expansion in the same loop
+        * This code plays outside i_size, so it locks the page
+        * to protect that region.
+        */
        for (n = 0; n <= npages; n++) {
+               char *dir_end;
+
                page = dir_get_page(dir, n);
                err = PTR_ERR(page);
                if (IS_ERR(page))
                        goto out;
+               lock_page(page);
                kaddr = (char*)page_address(page);
+               dir_end = kaddr + minix_last_byte(dir, n);
                de = (minix_dirent *)kaddr;
                kaddr += PAGE_CACHE_SIZE - sbi->s_dirsize;
                while ((char *)de <= kaddr) {
+                       if ((char *)de == dir_end) {
+                               /* We hit i_size */
+                               de->inode = 0;
+                               goto got_it;
+                       }
                        if (!de->inode)
                                goto got_it;
                        err = -EEXIST;
                        if (namecompare(namelen,sbi->s_namelen,name,de->name))
-                               goto out_page;
+                               goto out_unlock;
                        de = minix_next_entry(de, sbi);
                }
+               unlock_page(page);
                dir_put_page(page);
        }
        BUG();
@@ -210,7 +238,6 @@ int minix_add_link(struct dentry *dentry, struct inode *inode)
 got_it:
        from = (char*)de - (char*)page_address(page);
        to = from + sbi->s_dirsize;
-       lock_page(page);
        err = page->mapping->a_ops->prepare_write(NULL, page, from, to);
        if (err)
                goto out_unlock;
@@ -221,8 +248,7 @@ got_it:
        dir->i_mtime = dir->i_ctime = CURRENT_TIME;
        mark_inode_dirty(dir);
 out_unlock:
-       UnlockPage(page);
-out_page:
+       unlock_page(page);
        dir_put_page(page);
 out:
        return err;
@@ -301,7 +327,7 @@ int minix_empty_dir(struct inode * inode)
 
                kaddr = (char *)page_address(page);
                de = (minix_dirent *)kaddr;
-               kaddr += PAGE_CACHE_SIZE - sbi->s_dirsize;
+               kaddr += minix_last_byte(inode, i) - sbi->s_dirsize;
 
                while ((char *)de <= kaddr) {
                        if (de->inode != 0) {