From 5636243ee47c831bcde68316fa535ea871864c6c Mon Sep 17 00:00:00 2001 From: braam Date: Mon, 15 Oct 2001 05:51:19 +0000 Subject: [PATCH] more page cache changes --- lustre/include/linux/obdfs.h | 14 - lustre/obdfs/Makefile.am | 2 +- lustre/obdfs/Makefile.in | 10 +- lustre/obdfs/dir.c | 764 ++++++++++++++++++++++++++++++++----------- lustre/obdfs/file.c | 9 - lustre/obdfs/namei.c | 756 ++++++------------------------------------ 6 files changed, 696 insertions(+), 859 deletions(-) diff --git a/lustre/include/linux/obdfs.h b/lustre/include/linux/obdfs.h index ed8198c..b95baba 100644 --- a/lustre/include/linux/obdfs.h +++ b/lustre/include/linux/obdfs.h @@ -153,20 +153,6 @@ struct ext2_super_block { #define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \ ( EXT2_SB(sb)->s_es->s_feature_incompat & cpu_to_le32(mask) ) - -struct dentry *obdfs_lookup(struct inode * dir, struct dentry *dentry); -int obdfs_create (struct inode * dir, struct dentry * dentry, int mode); -int obdfs_mkdir(struct inode *dir, struct dentry *dentry, int mode); -int obdfs_rmdir(struct inode *dir, struct dentry *dentry); -int obdfs_unlink(struct inode *dir, struct dentry *dentry); -int obdfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev); -int obdfs_symlink(struct inode *dir, struct dentry *dentry, - const char *symname); -int obdfs_link(struct dentry *old_dentry, struct inode *dir, - struct dentry *dentry); -int obdfs_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry); - /* rw.c */ int obdfs_do_writepage(struct page *, int sync); int obdfs_init_pgrqcache(void); diff --git a/lustre/obdfs/Makefile.am b/lustre/obdfs/Makefile.am index 62e0606..1432ae7 100644 --- a/lustre/obdfs/Makefile.am +++ b/lustre/obdfs/Makefile.am @@ -6,6 +6,6 @@ MODULE = obdfs modulefs_DATA = obdfs.o EXTRA_PROGRAMS = obdfs -obdfs_SOURCES = dirdata.c flushd.c rw.c file.c dir.c sysctl.c super.c namei.c symlink.c +obdfs_SOURCES = flushd.c rw.c file.c dir.c sysctl.c super.c namei.c symlink.c include $(top_srcdir)/Rules diff --git a/lustre/obdfs/Makefile.in b/lustre/obdfs/Makefile.in index 262ea31..fd9838f 100644 --- a/lustre/obdfs/Makefile.in +++ b/lustre/obdfs/Makefile.in @@ -93,7 +93,7 @@ modulefsdir = @modulefsdir@ MODULE = obdfs modulefs_DATA = obdfs.o EXTRA_PROGRAMS = obdfs -obdfs_SOURCES = dirdata.c flushd.c rw.c file.c dir.c sysctl.c super.c namei.c symlink.c +obdfs_SOURCES = flushd.c rw.c file.c dir.c sysctl.c super.c namei.c symlink.c mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs CONFIG_CLEAN_FILES = @@ -101,8 +101,8 @@ DEFS = @DEFS@ -I. -I$(srcdir) CPPFLAGS = @CPPFLAGS@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ -obdfs_OBJECTS = dirdata.o flushd.o rw.o file.o dir.o sysctl.o super.o \ -namei.o symlink.o +obdfs_OBJECTS = flushd.o rw.o file.o dir.o sysctl.o super.o namei.o \ +symlink.o obdfs_LDADD = $(LDADD) obdfs_DEPENDENCIES = obdfs_LDFLAGS = @@ -119,8 +119,8 @@ DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) TAR = gtar GZIP_ENV = --best -DEP_FILES = .deps/dir.P .deps/dirdata.P .deps/file.P .deps/flushd.P \ -.deps/namei.P .deps/rw.P .deps/super.P .deps/symlink.P .deps/sysctl.P +DEP_FILES = .deps/dir.P .deps/file.P .deps/flushd.P .deps/namei.P \ +.deps/rw.P .deps/super.P .deps/symlink.P .deps/sysctl.P SOURCES = $(obdfs_SOURCES) OBJECTS = $(obdfs_OBJECTS) diff --git a/lustre/obdfs/dir.c b/lustre/obdfs/dir.c index af115d9..e5f929b 100644 --- a/lustre/obdfs/dir.c +++ b/lustre/obdfs/dir.c @@ -1,9 +1,6 @@ /* * linux/fs/ext2/dir.c * - * This code is issued under the GNU General Public License. - * See the file COPYING in this distribution - * * Copyright (C) 1992, 1993, 1994, 1995 * Remy Card (card@masi.ibp.fr) * Laboratoire MASI - Institut Blaise Pascal @@ -20,197 +17,598 @@ * Big-endian to little-endian byte-swapping/bitmaps by * David S. Miller (davem@caip.rutgers.edu), 1995 * - * Changes for use with Object Based Device File System - * - * Copyright (C) 1999, Seagate Technology Inc. - * (author Peter J. Braam, braam@stelias.com) - * + * All code that works with directory layout had been switched to pagecache + * and moved here. AV */ -#include - -#include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include #include -#include +#include -static ssize_t obdfs_dir_read (struct file * filp, char * buf, - size_t count, loff_t *ppos) +typedef struct ext2_dir_entry_2 ext2_dirent; + +#define PageChecked(page) test_bit(PG_checked, &(page)->flags) +#define SetPageChecked(page) set_bit(PG_checked, &(page)->flags) +#define PG_checked 13 /* kill me in 2.5.. */ + +int waitfor_one_page(struct page *page) { - return -EISDIR; + int error = 0; + struct buffer_head *bh, *head = page->buffers; + + bh = head; + do { + wait_on_buffer(bh); + if (buffer_req(bh) && !buffer_uptodate(bh)) + error = -EIO; + } while ((bh = bh->b_this_page) != head); + return error; } -static int obdfs_readdir(struct file *, void *, filldir_t); -extern int new_obdfs_readdir(struct file *, void *, filldir_t); +/* + * ext2 uses block-sized chunks. Arguably, sector-sized ones would be + * more robust, but we have what we have + */ +static inline unsigned ext2_chunk_size(struct inode *inode) +{ + //return inode->i_sb->s_blocksize; + return PAGE_SIZE; +} -struct file_operations obdfs_dir_operations = { - read: obdfs_dir_read, - readdir: new_obdfs_readdir +static inline void ext2_put_page(struct page *page) +{ + kunmap(page); + page_cache_release(page); +} + +static inline unsigned long dir_pages(struct inode *inode) +{ + return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT; +} + +static int ext2_commit_chunk(struct page *page, unsigned from, unsigned to) +{ + struct inode *dir = page->mapping->host; + int err = 0; + dir->i_version = ++event; + page->mapping->a_ops->commit_write(NULL, page, from, to); + if (IS_SYNC(dir)) + err = waitfor_one_page(page); + return err; +} + +static void ext2_check_page(struct page *page) +{ + struct inode *dir = page->mapping->host; + struct super_block *sb = dir->i_sb; + unsigned chunk_size = ext2_chunk_size(dir); + char *kaddr = page_address(page); + // u32 max_inumber = le32_to_cpu(sb->u.ext2_sb.s_es->s_inodes_count); + unsigned offs, rec_len; + unsigned limit = PAGE_CACHE_SIZE; + ext2_dirent *p; + char *error; + + if ((dir->i_size >> PAGE_CACHE_SHIFT) == page->index) { + limit = dir->i_size & ~PAGE_CACHE_MASK; + if (limit & (chunk_size - 1)) + goto Ebadsize; + for (offs = limit; offsrec_len = cpu_to_le16(chunk_size); + } + if (!limit) + goto out; + } + for (offs = 0; offs <= limit - EXT2_DIR_REC_LEN(1); offs += rec_len) { + p = (ext2_dirent *)(kaddr + offs); + rec_len = le16_to_cpu(p->rec_len); + + if (rec_len < EXT2_DIR_REC_LEN(1)) + goto Eshort; + if (rec_len & 3) + goto Ealign; + if (rec_len < EXT2_DIR_REC_LEN(p->name_len)) + goto Enamelen; + if (((offs + rec_len - 1) ^ offs) & ~(chunk_size-1)) + goto Espan; + // if (le32_to_cpu(p->inode) > max_inumber) + //goto Einumber; + } + if (offs != limit) + goto Eend; +out: + SetPageChecked(page); + return; + + /* Too bad, we had an error */ + +Ebadsize: + ext2_error(sb, "ext2_check_page", + "size of directory #%lu is not a multiple of chunk size", + dir->i_ino + ); + goto fail; +Eshort: + error = "rec_len is smaller than minimal"; + goto bad_entry; +Ealign: + error = "unaligned directory entry"; + goto bad_entry; +Enamelen: + error = "rec_len is too small for name_len"; + goto bad_entry; +Espan: + error = "directory entry across blocks"; + goto bad_entry; + //Einumber: + // error = "inode out of bounds"; +bad_entry: + ext2_error (sb, "ext2_check_page", "bad entry in directory #%lu: %s - " + "offset=%lu, inode=%lu, rec_len=%d, name_len=%d", + dir->i_ino, error, (page->index<inode), + rec_len, p->name_len); + goto fail; +Eend: + p = (ext2_dirent *)(kaddr + offs); + ext2_error (sb, "ext2_check_page", + "entry in directory #%lu spans the page boundary" + "offset=%lu, inode=%lu", + dir->i_ino, (page->index<inode)); +fail: + SetPageChecked(page); + SetPageError(page); +} + +static struct page * ext2_get_page(struct inode *dir, unsigned long n) +{ + struct address_space *mapping = dir->i_mapping; + struct page *page = read_cache_page(mapping, n, + (filler_t*)mapping->a_ops->readpage, NULL); + if (!IS_ERR(page)) { + wait_on_page(page); + kmap(page); + if (!Page_Uptodate(page)) + goto fail; + if (!PageChecked(page)) + ext2_check_page(page); + if (PageError(page)) + goto fail; + } + return page; + +fail: + ext2_put_page(page); + return ERR_PTR(-EIO); +} + +/* + * NOTE! unlike strncmp, ext2_match returns 1 for success, 0 for failure. + * + * len <= EXT2_NAME_LEN and de != NULL are guaranteed by caller. + */ +static inline int ext2_match (int len, const char * const name, + struct ext2_dir_entry_2 * de) +{ + if (len != de->name_len) + return 0; + if (!de->inode) + return 0; + return !memcmp(name, de->name, len); +} + +/* + * p is at least 6 bytes before the end of page + */ +static inline ext2_dirent *ext2_next_entry(ext2_dirent *p) +{ + return (ext2_dirent *)((char*)p + le16_to_cpu(p->rec_len)); +} + +static inline unsigned +ext2_validate_entry(char *base, unsigned offset, unsigned mask) +{ + ext2_dirent *de = (ext2_dirent*)(base + offset); + ext2_dirent *p = (ext2_dirent*)(base + (offset&mask)); + while ((char*)p < (char*)de) + p = ext2_next_entry(p); + return (char *)p - base; +} + +static unsigned char ext2_filetype_table[EXT2_FT_MAX] = { + [EXT2_FT_UNKNOWN] DT_UNKNOWN, + [EXT2_FT_REG_FILE] DT_REG, + [EXT2_FT_DIR] DT_DIR, + [EXT2_FT_CHRDEV] DT_CHR, + [EXT2_FT_BLKDEV] DT_BLK, + [EXT2_FT_FIFO] DT_FIFO, + [EXT2_FT_SOCK] DT_SOCK, + [EXT2_FT_SYMLINK] DT_LNK, }; -extern struct dentry *new_obdfs_lookup(struct inode * dir, struct dentry *dentry); -struct inode_operations obdfs_dir_inode_operations = { - create: obdfs_create, - lookup: new_obdfs_lookup, - link: obdfs_link, - unlink: obdfs_unlink, - symlink: obdfs_symlink, - mkdir: obdfs_mkdir, - rmdir: obdfs_rmdir, - mknod: obdfs_mknod, - rename: obdfs_rename, - truncate: obdfs_truncate +#define S_SHIFT 12 +static unsigned char ext2_type_by_mode[S_IFMT >> S_SHIFT] = { + [S_IFREG >> S_SHIFT] EXT2_FT_REG_FILE, + [S_IFDIR >> S_SHIFT] EXT2_FT_DIR, + [S_IFCHR >> S_SHIFT] EXT2_FT_CHRDEV, + [S_IFBLK >> S_SHIFT] EXT2_FT_BLKDEV, + [S_IFIFO >> S_SHIFT] EXT2_FT_FIFO, + [S_IFSOCK >> S_SHIFT] EXT2_FT_SOCK, + [S_IFLNK >> S_SHIFT] EXT2_FT_SYMLINK, }; -int obdfs_check_dir_entry (const char * function, struct inode * dir, - struct ext2_dir_entry_2 * de, - struct page * page, - unsigned long offset) -{ - const char * error_msg = NULL; - return 1; - - ENTRY; - if ( !de ) { - error_msg = "null de passed"; - return 1; - } - - if (le16_to_cpu(de->rec_len) < EXT2_DIR_REC_LEN(1)) - error_msg = "rec_len is smaller than minimal"; - else if (le16_to_cpu(de->rec_len) % 4 != 0) - error_msg = "rec_len % 4 != 0"; - else if (le16_to_cpu(de->rec_len) < EXT2_DIR_REC_LEN(de->name_len)) - error_msg = "rec_len is too small for name_len"; - else if (dir && ((char *) de - (char *)page_address(page)) + le16_to_cpu(de->rec_len) > - dir->i_sb->s_blocksize) - error_msg = "directory entry across blocks"; -#if 0 /* this one doesn't yet work for OBDFS */ - else - -if (dir && le32_to_cpu(de->inode) > le32_to_cpu(dir->i_sb->u.ext2_sb.s_es->s_inodes_count)) - error_msg = "inode out of bounds"; -#endif - if (error_msg != NULL) - ext2_error (dir->i_sb, function, "bad entry in directory #%lu: %s - " - "offset=%lu, inode=%lu, rec_len=%d, name_len=%d", - dir->i_ino, error_msg, offset, - (unsigned long) le32_to_cpu(de->inode), - le16_to_cpu(de->rec_len), de->name_len); - EXIT; - return error_msg == NULL ? 1 : 0; -} - - -static int obdfs_readdir(struct file * filp, void * dirent, filldir_t filldir) -{ - int error = 0; - unsigned long offset; - int stored; - struct ext2_dir_entry_2 * de; - struct super_block * sb; - struct page *page; - struct inode *inode = filp->f_dentry->d_inode; - - ENTRY; - - sb = inode->i_sb; - - stored = 0; - offset = filp->f_pos & (PAGE_SIZE - 1); - - OIDEBUG(inode); - while (!error && !stored && filp->f_pos < inode->i_size) { - page = obdfs_getpage(inode, filp->f_pos, 0, LOCKED); - /* PDEBUG(page, "readdir"); */ - if (!page) { - ext2_error (sb, "ext2_readdir", - "directory #%lu contains a hole at offset %lu", - inode->i_ino, (unsigned long)filp->f_pos); - filp->f_pos += PAGE_SIZE - offset; - continue; - } - -#if 0 - /* XXX need to do read ahead and support stuff below */ -revalidate: - /* If the dir block has changed since the last call to - * readdir(2), then we might be pointing to an invalid - * dirent right now. Scan from the start of the block - * to make sure. */ - if (filp->f_version != inode->i_version) { - for (i = 0; i < sb->s_blocksize && i < offset; ) { - de = (struct ext2_dir_entry_2 *) - (bh->b_data + i); - /* It's too expensive to do a full - * dirent test each time round this - * loop, but we do have to test at - * least that it is non-zero. A - * failure will be detected in the - * dirent test below. */ - if (le16_to_cpu(de->rec_len) < EXT2_DIR_REC_LEN(1)) - break; - i += le16_to_cpu(de->rec_len); - } - offset = i; - filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1)) - | offset; - filp->f_version = inode->i_version; - } -#endif - while (!error && filp->f_pos < inode->i_size - && offset < PAGE_SIZE) { - de = (struct ext2_dir_entry_2 *) ((char *)page_address(page) + offset); -#if 0 - if (!obdfs_check_dir_entry ("ext2_readdir", inode, de, - bh, offset)) { - /* On error, skip the f_pos to the - next block. */ - filp->f_pos = (filp->f_pos & (sb->s_blocksize - 1)) - + sb->s_blocksize; - brelse (bh); - return stored; - } -#endif - offset += le16_to_cpu(de->rec_len); - if (le32_to_cpu(de->inode)) { - unsigned char d_type = DT_UNKNOWN; - /* We might block in the next section - * if the data destination is - * currently swapped out. So, use a - * version stamp to detect whether or - * not the directory has been modified - * during the copy operation. - */ - /* XXX - unsigned long version = inode->i_version; - */ - error = filldir - (dirent, de->name, de->name_len, - filp->f_pos, le32_to_cpu(de->inode), - d_type); - if (error) - break; -#if 0 - if (version != inode->i_version) - goto revalidate; -#endif - stored ++; - } - filp->f_pos += le16_to_cpu(de->rec_len); - } - offset = 0; - obd_unlock_page(page); - page_cache_release(page); - } - UPDATE_ATIME(inode); - EXIT; - return 0; +static inline void ext2_set_de_type(ext2_dirent *de, struct inode *inode) +{ + /* XXX + mode_t mode = inode->i_mode; + if (EXT2_HAS_INCOMPAT_FEATURE(inode->i_sb, EXT2_FEATURE_INCOMPAT_FILETYPE)) + de->file_type = ext2_type_by_mode[(mode & S_IFMT)>>S_SHIFT]; + else + de->file_type = 0; + */ + de->file_type = 0; +} + +int +new_obdfs_readdir (struct file * filp, void * dirent, filldir_t filldir) +{ + loff_t pos = filp->f_pos; + struct inode *inode = filp->f_dentry->d_inode; + // XXX struct super_block *sb = inode->i_sb; + unsigned offset = pos & ~PAGE_CACHE_MASK; + unsigned long n = pos >> PAGE_CACHE_SHIFT; + unsigned long npages = dir_pages(inode); + unsigned chunk_mask = ~(ext2_chunk_size(inode)-1); + unsigned char *types = NULL; + int need_revalidate = (filp->f_version != inode->i_version); + + if (pos > inode->i_size - EXT2_DIR_REC_LEN(1)) + goto done; + + //if (EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE)) + //types = ext2_filetype_table; + + for ( ; n < npages; n++, offset = 0) { + char *kaddr, *limit; + ext2_dirent *de; + struct page *page = ext2_get_page(inode, n); + + if (IS_ERR(page)) + continue; + kaddr = page_address(page); + if (need_revalidate) { + offset = ext2_validate_entry(kaddr, offset, chunk_mask); + need_revalidate = 0; + } + de = (ext2_dirent *)(kaddr+offset); + limit = kaddr + PAGE_CACHE_SIZE - EXT2_DIR_REC_LEN(1); + for ( ;(char*)de <= limit; de = ext2_next_entry(de)) + if (de->inode) { + int over; + unsigned char d_type = DT_UNKNOWN; + + if (types && de->file_type < EXT2_FT_MAX) + d_type = types[de->file_type]; + + offset = (char *)de - kaddr; + over = filldir(dirent, de->name, de->name_len, + (n<inode), d_type); + if (over) { + ext2_put_page(page); + goto done; + } + } + ext2_put_page(page); + } + +done: + filp->f_pos = (n << PAGE_CACHE_SHIFT) | offset; + filp->f_version = inode->i_version; + UPDATE_ATIME(inode); + return 0; } + +/* + * ext2_find_entry() + * + * finds an entry in the specified directory with the wanted name. It + * returns the page in which the entry was found, and the entry itself + * (as a parameter - res_dir). Page is returned mapped and unlocked. + * Entry is guaranteed to be valid. + */ +struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir, + struct dentry *dentry, struct page ** res_page) +{ + const char *name = dentry->d_name.name; + int namelen = dentry->d_name.len; + unsigned reclen = EXT2_DIR_REC_LEN(namelen); + unsigned long start, n; + unsigned long npages = dir_pages(dir); + struct page *page = NULL; + ext2_dirent * de; + + /* OFFSET_CACHE */ + *res_page = NULL; + + // start = dir->u.ext2_i.i_dir_start_lookup; + start = 0; + if (start >= npages) + start = 0; + n = start; + do { + char *kaddr; + page = ext2_get_page(dir, n); + if (!IS_ERR(page)) { + kaddr = page_address(page); + de = (ext2_dirent *) kaddr; + kaddr += PAGE_CACHE_SIZE - reclen; + while ((char *) de <= kaddr) { + if (ext2_match (namelen, name, de)) + goto found; + de = ext2_next_entry(de); + } + ext2_put_page(page); + } + if (++n >= npages) + n = 0; + } while (n != start); + return NULL; + +found: + *res_page = page; + // dir->u.ext2_i.i_dir_start_lookup = n; + return de; +} + +struct ext2_dir_entry_2 * ext2_dotdot (struct inode *dir, struct page **p) +{ + struct page *page = ext2_get_page(dir, 0); + ext2_dirent *de = NULL; + + if (!IS_ERR(page)) { + de = ext2_next_entry((ext2_dirent *) page_address(page)); + *p = page; + } + return de; +} + +ino_t ext2_inode_by_name(struct inode * dir, struct dentry *dentry) +{ + ino_t res = 0; + struct ext2_dir_entry_2 * de; + struct page *page; + + de = ext2_find_entry (dir, dentry, &page); + if (de) { + res = le32_to_cpu(de->inode); + kunmap(page); + page_cache_release(page); + } + return res; +} + +/* Releases the page */ +void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de, + struct page *page, struct inode *inode) +{ + unsigned from = (char *) de - (char *) page_address(page); + unsigned to = from + le16_to_cpu(de->rec_len); + int err; + + lock_page(page); + err = page->mapping->a_ops->prepare_write(NULL, page, from, to); + if (err) + BUG(); + de->inode = cpu_to_le32(inode->i_ino); + ext2_set_de_type (de, inode); + err = ext2_commit_chunk(page, from, to); + UnlockPage(page); + ext2_put_page(page); + dir->i_mtime = dir->i_ctime = CURRENT_TIME; + mark_inode_dirty(dir); +} + +/* + * Parent is locked. + */ +int ext2_add_link (struct dentry *dentry, struct inode *inode) +{ + struct inode *dir = dentry->d_parent->d_inode; + const char *name = dentry->d_name.name; + int namelen = dentry->d_name.len; + unsigned reclen = EXT2_DIR_REC_LEN(namelen); + unsigned short rec_len, name_len; + struct page *page = NULL; + ext2_dirent * de; + unsigned long npages = dir_pages(dir); + unsigned long n; + char *kaddr; + unsigned from, to; + int err; + + /* We take care of directory expansion in the same loop */ + for (n = 0; n <= npages; n++) { + page = ext2_get_page(dir, n); + err = PTR_ERR(page); + if (IS_ERR(page)) + goto out; + kaddr = page_address(page); + de = (ext2_dirent *)kaddr; + kaddr += PAGE_CACHE_SIZE - reclen; + while ((char *)de <= kaddr) { + err = -EEXIST; + if (ext2_match (namelen, name, de)) + goto out_page; + name_len = EXT2_DIR_REC_LEN(de->name_len); + rec_len = le16_to_cpu(de->rec_len); + if (!de->inode && rec_len >= reclen) + goto got_it; + if (rec_len >= name_len + reclen) + goto got_it; + de = (ext2_dirent *) ((char *) de + rec_len); + } + ext2_put_page(page); + } + BUG(); + return -EINVAL; + +got_it: + from = (char*)de - (char*)page_address(page); + to = from + rec_len; + lock_page(page); + err = page->mapping->a_ops->prepare_write(NULL, page, from, to); + if (err) + goto out_unlock; + if (de->inode) { + ext2_dirent *de1 = (ext2_dirent *) ((char *) de + name_len); + de1->rec_len = cpu_to_le16(rec_len - name_len); + de->rec_len = cpu_to_le16(name_len); + de = de1; + } + de->name_len = namelen; + memcpy (de->name, name, namelen); + de->inode = cpu_to_le32(inode->i_ino); + ext2_set_de_type (de, inode); + err = ext2_commit_chunk(page, from, to); + dir->i_mtime = dir->i_ctime = CURRENT_TIME; + mark_inode_dirty(dir); + /* OFFSET_CACHE */ +out_unlock: + UnlockPage(page); +out_page: + ext2_put_page(page); +out: + return err; +} + +/* + * ext2_delete_entry deletes a directory entry by merging it with the + * previous entry. Page is up-to-date. Releases the page. + */ +int ext2_delete_entry (struct ext2_dir_entry_2 * dir, struct page * page ) +{ + struct address_space *mapping = page->mapping; + struct inode *inode = mapping->host; + char *kaddr = page_address(page); + unsigned from = ((char*)dir - kaddr) & ~(ext2_chunk_size(inode)-1); + unsigned to = ((char*)dir - kaddr) + le16_to_cpu(dir->rec_len); + ext2_dirent * pde = NULL; + ext2_dirent * de = (ext2_dirent *) (kaddr + from); + int err; + + while ((char*)de < (char*)dir) { + pde = de; + de = ext2_next_entry(de); + } + if (pde) + from = (char*)pde - (char*)page_address(page); + lock_page(page); + err = mapping->a_ops->prepare_write(NULL, page, from, to); + if (err) + BUG(); + if (pde) + pde->rec_len = cpu_to_le16(to-from); + dir->inode = 0; + err = ext2_commit_chunk(page, from, to); + UnlockPage(page); + ext2_put_page(page); + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + mark_inode_dirty(inode); + return err; +} + +/* + * Set the first fragment of directory. + */ +int ext2_make_empty(struct inode *inode, struct inode *parent) +{ + struct address_space *mapping = inode->i_mapping; + struct page *page = grab_cache_page(mapping, 0); + unsigned chunk_size = ext2_chunk_size(inode); + struct ext2_dir_entry_2 * de; + char *base; + int err; + + if (!page) + return -ENOMEM; + err = mapping->a_ops->prepare_write(NULL, page, 0, chunk_size); + if (err) + goto fail; + + base = page_address(page); + + de = (struct ext2_dir_entry_2 *) base; + de->name_len = 1; + de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(1)); + memcpy (de->name, ".\0\0", 4); + de->inode = cpu_to_le32(inode->i_ino); + ext2_set_de_type (de, inode); + + de = (struct ext2_dir_entry_2 *) (base + EXT2_DIR_REC_LEN(1)); + de->name_len = 2; + de->rec_len = cpu_to_le16(chunk_size - EXT2_DIR_REC_LEN(1)); + de->inode = cpu_to_le32(parent->i_ino); + memcpy (de->name, "..\0", 4); + ext2_set_de_type (de, inode); + + err = ext2_commit_chunk(page, 0, chunk_size); +fail: + UnlockPage(page); + page_cache_release(page); + return err; +} + +/* + * routine to check that the specified directory is empty (for rmdir) + */ +int ext2_empty_dir (struct inode * inode) +{ + struct page *page = NULL; + unsigned long i, npages = dir_pages(inode); + + for (i = 0; i < npages; i++) { + char *kaddr; + ext2_dirent * de; + page = ext2_get_page(inode, i); + + if (IS_ERR(page)) + continue; + + kaddr = page_address(page); + de = (ext2_dirent *)kaddr; + kaddr += PAGE_CACHE_SIZE-EXT2_DIR_REC_LEN(1); + + while ((char *)de <= kaddr) { + if (de->inode != 0) { + /* check for . and .. */ + if (de->name[0] != '.') + goto not_empty; + if (de->name_len > 2) + goto not_empty; + if (de->name_len < 2) { + if (de->inode != + cpu_to_le32(inode->i_ino)) + goto not_empty; + } else if (de->name[1] != '.') + goto not_empty; + } + de = ext2_next_entry(de); + } + ext2_put_page(page); + } + return 1; + +not_empty: + ext2_put_page(page); + return 0; +} + +struct file_operations obdfs_dir_operations = { + read: generic_read_dir, + readdir: new_obdfs_readdir +}; diff --git a/lustre/obdfs/file.c b/lustre/obdfs/file.c index f58c057..fa35fe5 100644 --- a/lustre/obdfs/file.c +++ b/lustre/obdfs/file.c @@ -82,15 +82,6 @@ struct file_operations obdfs_file_operations = { extern int obdfs_notify_change(struct dentry *de, struct iattr *attr); struct inode_operations obdfs_file_inode_operations = { - create: obdfs_create, - lookup: NULL, - link: obdfs_link, - unlink: obdfs_unlink, - symlink: obdfs_symlink, - mkdir: obdfs_mkdir, - rmdir: obdfs_rmdir, - mknod: obdfs_mknod, - rename: obdfs_rename, truncate: obdfs_truncate, setattr: obdfs_notify_change }; diff --git a/lustre/obdfs/namei.c b/lustre/obdfs/namei.c index 9ea0c1c..8315aa5 100644 --- a/lustre/obdfs/namei.c +++ b/lustre/obdfs/namei.c @@ -33,18 +33,22 @@ #include #include extern struct address_space_operations obdfs_aops; -/* - * Couple of helper functions - make the code slightly cleaner. - */ +/* from dir.c */ extern int ext2_add_link (struct dentry *dentry, struct inode *inode); extern ino_t ext2_inode_by_name(struct inode * dir, struct dentry *dentry); int ext2_make_empty(struct inode *inode, struct inode *parent); struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir, struct dentry *dentry, struct page ** res_page); int ext2_delete_entry (struct ext2_dir_entry_2 * dir, struct page * page ); +int ext2_empty_dir (struct inode * inode); +struct ext2_dir_entry_2 * ext2_dotdot (struct inode *dir, struct page **p); +void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de, + struct page *page, struct inode *inode); - +/* + * Couple of helper functions - make the code slightly cleaner. + */ static inline void ext2_inc_count(struct inode *inode) { inode->i_nlink++; @@ -70,7 +74,7 @@ static inline int ext2_add_nondir(struct dentry *dentry, struct inode *inode) } /* methods */ -struct dentry *new_obdfs_lookup(struct inode * dir, struct dentry *dentry) +static struct dentry *obdfs_lookup(struct inode * dir, struct dentry *dentry) { struct inode * inode; ino_t ino; @@ -106,366 +110,6 @@ static inline int ext2_match (int len, const char * const name, return !memcmp(name, de->name, len); } - -/* - * obdfs_find_entry() - * - * finds an entry in the specified directory with the wanted name. It - * returns the cache buffer in which the entry was found, and the entry - * itself (as a parameter - res_dir). It does NOT read the inode of the - * entry - you'll have to do that yourself if you want to. - */ -static struct page * obdfs_find_entry (struct inode * dir, - const char * const name, int namelen, - struct ext2_dir_entry_2 ** res_dir, - int lock) -{ - struct super_block * sb; - unsigned long offset; - struct page * page; - - ENTRY; - CDEBUG(D_INFO, "find entry for %*s\n", namelen, name); - - *res_dir = NULL; - sb = dir->i_sb; - - if (namelen > EXT2_NAME_LEN) - return NULL; - - CDEBUG(D_INFO, "dirsize is %Ld\n", dir->i_size); - - page = 0; - offset = 0; - while ( offset < dir->i_size ) { - struct ext2_dir_entry_2 * de; - char * dlimit; - - page = obdfs_getpage(dir, offset, 0, lock); - - if ( !page ) { - CDEBUG(D_INFO, "No page, offset %lx\n", offset); - return NULL; - } - - de = (struct ext2_dir_entry_2 *) page_address(page); - dlimit = (char *)page_address(page) + PAGE_SIZE; - while ((char *) de < dlimit) { - /* this code is executed quadratically often */ - /* do minimal checking `by hand' */ - int de_len; - /* CDEBUG(D_INFO, "Entry %p len %d, page at %#lx - %#lx , offset %lx\n", - de, le16_to_cpu(de->rec_len), page_address(page), - page_address(page) + PAGE_SIZE, offset); */ - - if ((char *) de + namelen <= dlimit && - ext2_match (namelen, name, de)) { - /* found a match - - just to be sure, do a full check */ - if (!obdfs_check_dir_entry("ext2_find_entry", - dir, de, page, offset)) - goto failure; - *res_dir = de; - EXIT; - return page; - } - /* prevent looping on a bad block */ - de_len = le16_to_cpu(de->rec_len); - if (de_len <= 0) { - printk("Bad entry at %p len %d\n", de, de_len); - goto failure; - } - offset += de_len; - de = (struct ext2_dir_entry_2 *) - ((char *) de + de_len); - /* CDEBUG(D_INFO, "Next while %lx\n", offset); */ - } - obd_unlock_page(page); - page_cache_release(page); - page = NULL; - CDEBUG(D_INFO, "Next for %lx\n", offset); - } - -failure: - CDEBUG(D_INFO, "Negative case, page %p, offset %ld\n", page, offset); - if (page) { - obd_unlock_page(page); - page_cache_release(page); - } - EXIT; - return NULL; -} /* obdfs_find_entry */ - -/* - * obdfs_add_entry() - * - * adds a file entry to the specified directory, using the same - * semantics as ext2_find_entry(). It returns NULL if it failed. - * - * NOTE!! The inode part of 'de' is left at 0 - which means you - * may not sleep between calling this and putting something into - * the entry, as someone else might have used it while you slept. - * - * returns a locked and held page upon success - */ - - -/* We do this with a locked page: that's not necessary, since the semaphore - * on the inode protects this page as well. - */ -static struct page *obdfs_add_entry (struct inode * dir, - const char * name, int namelen, - struct ext2_dir_entry_2 ** res_dir, - int *err) -{ - unsigned long offset; - unsigned short rec_len; - struct page *page; - struct ext2_dir_entry_2 * de, * de1; - struct super_block * sb; - - ENTRY; - *err = -EINVAL; - *res_dir = NULL; - if (!dir || !dir->i_nlink) { - CDEBUG(D_INODE, "bad directory\n"); - EXIT; - return NULL; - } - sb = dir->i_sb; - - if (!namelen) { - CDEBUG(D_INODE, "bad directory\n"); - EXIT; - return NULL; - } - /* - * Is this a busy deleted directory? Can't create new files if so - */ - if (dir->i_size == 0) - { - OIDEBUG(dir); - *err = -ENOENT; - EXIT; - return NULL; - } - page = obdfs_getpage(dir, 0, 0, LOCKED); - if (!page) { - EXIT; - return NULL; - } - rec_len = EXT2_DIR_REC_LEN(namelen); - /* CDEBUG(D_INFO, "reclen: %d\n", rec_len); */ - /* PDEBUG(page, "starting search"); */ - offset = 0; - de = (struct ext2_dir_entry_2 *) page_address(page); - *err = -ENOSPC; - while (1) { - /* CDEBUG(D_INFO, - "Entry at %p, (page at %#lx - %#lx), offset %ld\n", - de, page_address(page), page_address(page) + PAGE_SIZE, - offset); */ - if ((char *)de >= PAGE_SIZE + (char *)page_address(page)) { - obd_unlock_page(page); - page_cache_release(page); - page = obdfs_getpage(dir, offset, 1, LOCKED); - if (!page) { - EXIT; - return NULL; - } - /* PDEBUG(page, "new directory page"); */ - if (dir->i_size <= offset) { - if (dir->i_size == 0) { - *err = -ENOENT; - EXIT; - return NULL; - } - - CDEBUG(D_INFO, "creating next block\n"); - - de = (struct ext2_dir_entry_2 *) page_address(page); - de->inode = 0; - de->rec_len = cpu_to_le16(PAGE_SIZE); - dir->i_size = offset + PAGE_SIZE; - dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; - mark_inode_dirty(dir); - } else { - - CDEBUG(D_INFO, "skipping to next block\n"); - - de = (struct ext2_dir_entry_2 *) page_address(page); - } - } - if (!obdfs_check_dir_entry ("ext2_add_entry", dir, de, page, - offset)) { - *err = -ENOENT; - obd_unlock_page(page); - page_cache_release(page); - EXIT; - return NULL; - } - CDEBUG(D_INFO, "\n"); - if (ext2_match (namelen, name, de)) { - *err = -EEXIST; - obd_unlock_page(page); - page_cache_release(page); - EXIT; - return NULL; - } - /* CDEBUG(D_INFO, "Testing for enough space at de %p\n", de);*/ - if ((le32_to_cpu(de->inode) == 0 && le16_to_cpu(de->rec_len) >= rec_len) || - (le16_to_cpu(de->rec_len) >= EXT2_DIR_REC_LEN(de->name_len) + rec_len)) { - offset += le16_to_cpu(de->rec_len); - /* CDEBUG(D_INFO, - "Found enough space de %p, offset %#lx\n", - de, offset); */ - if (le32_to_cpu(de->inode)) { - /*CDEBUG(D_INFO, "Insert new in %p\n", de);*/ - de1 = (struct ext2_dir_entry_2 *) ((char *) de + - EXT2_DIR_REC_LEN(de->name_len)); - /*CDEBUG(D_INFO, "-- de1 at %p\n", de1);*/ - de1->rec_len = cpu_to_le16(le16_to_cpu(de->rec_len) - - EXT2_DIR_REC_LEN(de->name_len)); - de->rec_len = cpu_to_le16(EXT2_DIR_REC_LEN(de->name_len)); - de = de1; - } - /* CDEBUG(D_INFO, - "Reclen adjusted; copy %d bytes to %p, " - "page at %#lx EOP at %#lx\n", - namelen, de->name, page_address(page), - page_address(page) + PAGE_SIZE); */ - de->inode = 0; - de->name_len = namelen; - de->file_type = 0; - memcpy (de->name, name, namelen); - /* - * XXX shouldn't update any times until successful - * completion of syscall, but too many callers depend - * on this. - * - * XXX similarly, too many callers depend on - * ext2_new_inode() setting the times, but error - * recovery deletes the inode, so the worst that can - * happen is that the times are slightly out of date - * and/or different from the directory change time. - */ - dir->i_mtime = dir->i_ctime = CURRENT_TIME; - dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; - mark_inode_dirty(dir); - dir->i_version = ++event; - *res_dir = de; - *err = 0; - // PDEBUG(page, "add_entry"); - /* XXX unlock page here */ - EXIT; - return page; - } - offset += le16_to_cpu(de->rec_len); - de = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); - } - - obd_unlock_page(page); - page_cache_release(page); - /* PDEBUG(page, "add_entry"); */ - EXIT; - return NULL; -} /* obdfs_add_entry */ - -/* - * obdfs_delete_entry deletes a directory entry by merging it with the - * previous entry - */ -static int obdfs_delete_entry (struct ext2_dir_entry_2 * dir, - struct page * page) -{ - struct ext2_dir_entry_2 * de, * pde; - int i; - - i = 0; - pde = NULL; - de = (struct ext2_dir_entry_2 *) page_address(page); - while (i < PAGE_SIZE) { - if (!obdfs_check_dir_entry ("ext2_delete_entry", NULL, - de, page, i)) - return -EIO; - if (de == dir) { - if (pde) - pde->rec_len = - cpu_to_le16(le16_to_cpu(pde->rec_len) + - le16_to_cpu(dir->rec_len)); - else - dir->inode = 0; - return 0; - } - i += le16_to_cpu(de->rec_len); - pde = de; - de = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); - } - return -ENOENT; -} /* obdfs_delete_entry */ - - -static inline void ext2_set_de_type(struct super_block *sb, - struct ext2_dir_entry_2 *de, - umode_t mode) { - return; - /* XXX fix this to check for obdfs feature, not ext2 feature */ - if (!EXT2_HAS_INCOMPAT_FEATURE(sb, EXT2_FEATURE_INCOMPAT_FILETYPE)) - return; - if (S_ISREG(mode)) - de->file_type = EXT2_FT_REG_FILE; - else if (S_ISDIR(mode)) - de->file_type = EXT2_FT_DIR; - else if (S_ISLNK(mode)) - de->file_type = EXT2_FT_SYMLINK; - else if (S_ISSOCK(mode)) - de->file_type = EXT2_FT_SOCK; - else if (S_ISFIFO(mode)) - de->file_type = EXT2_FT_FIFO; - else if (S_ISCHR(mode)) - de->file_type = EXT2_FT_CHRDEV; - else if (S_ISBLK(mode)) - de->file_type = EXT2_FT_BLKDEV; -} - - -/* - * Display all dentries holding the specified inode. - */ -#if 0 -static void show_dentry(struct list_head * dlist, int subdirs) -{ - struct list_head *tmp = dlist; - - while ((tmp = tmp->next) != dlist) { - struct dentry * dentry; - const char * unhashed = ""; - - if ( subdirs ) - dentry = list_entry(tmp, struct dentry, d_child); - else - dentry = list_entry(tmp, struct dentry, d_alias); - - if (list_empty(&dentry->d_hash)) - unhashed = "(unhashed)"; - - if ( dentry->d_inode ) - printk("show_dentry: %s/%s, d_count=%d%s (ino %ld, dev %d, ct %d)\n", - dentry->d_parent->d_name.name, - dentry->d_name.name, dentry->d_count, - unhashed, dentry->d_inode->i_ino, - dentry->d_inode->i_dev, - dentry->d_inode->i_count); - else - printk("show_dentry: %s/%s, d_count=%d%s \n", - dentry->d_parent->d_name.name, - dentry->d_name.name, dentry->d_count, - unhashed); - } -} /* show_dentry */ -#endif - - static struct inode *obdfs_new_inode(struct inode *dir, int mode) { struct obdo *oa; @@ -530,7 +174,7 @@ static struct inode *obdfs_new_inode(struct inode *dir, int mode) * If the create succeeds, we fill in the inode information * with d_instantiate(). */ -int obdfs_create (struct inode * dir, struct dentry * dentry, int mode) +static int obdfs_create (struct inode * dir, struct dentry * dentry, int mode) { struct inode * inode = obdfs_new_inode (dir, mode); int err = PTR_ERR(inode); @@ -545,7 +189,7 @@ int obdfs_create (struct inode * dir, struct dentry * dentry, int mode) } /* obdfs_create */ -int obdfs_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev) +static int obdfs_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev) { struct inode * inode = obdfs_new_inode (dir, mode); int err = PTR_ERR(inode); @@ -557,7 +201,7 @@ int obdfs_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev) return err; } -int obdfs_symlink (struct inode * dir, struct dentry * dentry, +static int obdfs_symlink (struct inode * dir, struct dentry * dentry, const char * symname) { struct super_block * sb = dir->i_sb; @@ -602,7 +246,7 @@ out_fail: -int obdfs_link (struct dentry * old_dentry, struct inode * dir, +static int obdfs_link (struct dentry * old_dentry, struct inode * dir, struct dentry *dentry) { struct inode *inode = old_dentry->d_inode; @@ -621,7 +265,7 @@ int obdfs_link (struct dentry * old_dentry, struct inode * dir, } -int obdfs_mkdir(struct inode * dir, struct dentry * dentry, int mode) +static int obdfs_mkdir(struct inode * dir, struct dentry * dentry, int mode) { struct inode * inode; int err = -EMLINK; @@ -663,7 +307,7 @@ out_dir: goto out; } -int obdfs_unlink(struct inode * dir, struct dentry *dentry) +static int obdfs_unlink(struct inode * dir, struct dentry *dentry) { struct inode * inode = dentry->d_inode; struct ext2_dir_entry_2 * de; @@ -686,290 +330,108 @@ out: } -/* - * routine to check that the specified directory is empty (for rmdir) - */ -static int empty_dir (struct inode * inode) +static int obdfs_rmdir (struct inode * dir, struct dentry *dentry) { - unsigned long offset; - struct page *page; - struct ext2_dir_entry_2 * de, * de1; - struct super_block * sb; - - sb = inode->i_sb; - if (inode->i_size < EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2) || - !(page = obdfs_getpage (inode, 0, 0, LOCKED))) { - ext2_warning (inode->i_sb, "empty_dir", - "bad directory (dir #%lu) - no data block", - inode->i_ino); - return 1; - } - de = (struct ext2_dir_entry_2 *) page_address(page); - de1 = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); - if (le32_to_cpu(de->inode) != inode->i_ino || !le32_to_cpu(de1->inode) || - strcmp (".", de->name) || strcmp ("..", de1->name)) { - ext2_warning (inode->i_sb, "empty_dir", - "bad directory (dir #%lu) - no `.' or `..'", - inode->i_ino); - page_cache_release(page); - return 1; - } - offset = le16_to_cpu(de->rec_len) + le16_to_cpu(de1->rec_len); - de = (struct ext2_dir_entry_2 *) ((char *) de1 + le16_to_cpu(de1->rec_len)); - while (offset < inode->i_size ) { - if (!page || (void *) de >= (void *) (page_address(page) + PAGE_SIZE)) { - if (page) { - obd_unlock_page(page); - page_cache_release(page); - } - page = obdfs_getpage(inode, offset, 0, LOCKED); - if (!page) { -#if 0 - ext2_error (sb, "empty_dir", - "directory #%lu contains a hole at offset %lu", - inode->i_ino, offset); -#endif - offset += sb->s_blocksize; - continue; - } - de = (struct ext2_dir_entry_2 *) page_address(page); - } - if (!obdfs_check_dir_entry ("empty_dir", inode, de, page, - offset)) { - obd_unlock_page(page); - page_cache_release(page); - return 1; - } - if (le32_to_cpu(de->inode)) { - obd_unlock_page(page); - page_cache_release(page); - return 0; - } - offset += le16_to_cpu(de->rec_len); - de = (struct ext2_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); - } - obd_unlock_page(page); - page_cache_release(page); - return 1; -} /* empty_dir */ + struct inode * inode = dentry->d_inode; + int err = -ENOTEMPTY; + + if (ext2_empty_dir(inode)) { + err = obdfs_unlink(dir, dentry); + if (!err) { + inode->i_size = 0; + ext2_dec_count(inode); + ext2_dec_count(dir); + } + } + return err; +} -int obdfs_rmdir (struct inode * dir, struct dentry *dentry) +static int obdfs_rename (struct inode * old_dir, struct dentry * old_dentry, + struct inode * new_dir, struct dentry * new_dentry ) { - int retval; - struct inode * inode; - struct page *page; - struct ext2_dir_entry_2 * de; - - ENTRY; - - retval = -ENOENT; - page = obdfs_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, LOCKED); - if (!page) - goto end_rmdir; - - inode = dentry->d_inode; - DQUOT_INIT(inode); - - retval = -EIO; - if (le32_to_cpu(de->inode) != inode->i_ino) - goto end_rmdir; - - retval = -ENOTEMPTY; - if (!empty_dir (inode)) - goto end_rmdir; - - retval = obdfs_delete_entry (de, page); - dir->i_version = ++event; - if (retval) - goto end_rmdir; - retval = obdfs_do_writepage(page, IS_SYNC(dir)); - /* XXX handle err? */ - obd_unlock_page(page); - - if (inode->i_nlink != 2) - ext2_warning (inode->i_sb, "ext2_rmdir", - "empty directory has nlink!=2 (%d)", - inode->i_nlink); - inode->i_version = ++event; - inode->i_nlink = 0; - inode->i_size = 0; - mark_inode_dirty(inode); - dir->i_nlink--; - inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; - dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; - mark_inode_dirty(dir); - d_delete(dentry); - -end_rmdir: - if ( page ) - page_cache_release(page); - EXIT; - return retval; -} /* obdfs_rmdir */ + struct inode * old_inode = old_dentry->d_inode; + struct inode * new_inode = new_dentry->d_inode; + struct page * dir_page = NULL; + struct ext2_dir_entry_2 * dir_de = NULL; + struct page * old_page; + struct ext2_dir_entry_2 * old_de; + int err = -ENOENT; + old_de = ext2_find_entry (old_dir, old_dentry, &old_page); + if (!old_de) + goto out; + if (S_ISDIR(old_inode->i_mode)) { + err = -EIO; + dir_de = ext2_dotdot(old_inode, &dir_page); + if (!dir_de) + goto out_old; + } -#define PARENT_INO(buffer) \ - ((struct ext2_dir_entry_2 *) ((char *) buffer + \ - le16_to_cpu(((struct ext2_dir_entry_2 *) buffer)->rec_len)))->inode + if (new_inode) { + struct page *new_page; + struct ext2_dir_entry_2 *new_de; + + err = -ENOTEMPTY; + if (dir_de && !ext2_empty_dir (new_inode)) + goto out_dir; + + err = -ENOENT; + new_de = ext2_find_entry (new_dir, new_dentry, &new_page); + if (!new_de) + goto out_dir; + ext2_inc_count(old_inode); + ext2_set_link(new_dir, new_de, new_page, old_inode); + new_inode->i_ctime = CURRENT_TIME; + if (dir_de) + new_inode->i_nlink--; + ext2_dec_count(new_inode); + } else { + if (dir_de) { + err = -EMLINK; + if (new_dir->i_nlink >= EXT2_LINK_MAX) + goto out_dir; + } + ext2_inc_count(old_inode); + err = ext2_add_link(new_dentry, old_inode); + if (err) { + ext2_dec_count(old_inode); + goto out_dir; + } + if (dir_de) + ext2_inc_count(new_dir); + } -/* - * Anybody can rename anything with this: the permission checks are left to the - * higher-level routines. - */ -int obdfs_rename (struct inode * old_dir, struct dentry *old_dentry, - struct inode * new_dir, struct dentry *new_dentry) -{ - struct inode * old_inode, * new_inode; - struct page * old_page, * new_page, * dir_page; - struct ext2_dir_entry_2 * old_de, * new_de; - int retval; + ext2_delete_entry (old_de, old_page); + ext2_dec_count(old_inode); - ENTRY; + if (dir_de) { + ext2_set_link(old_inode, dir_de, dir_page, new_dir); + ext2_dec_count(old_dir); + } + return 0; - new_page = dir_page = NULL; - - /* does the old entry exist? - if not get out */ - old_page = obdfs_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de, NOLOCK); - /* PDEBUG(old_page, "rename - old page"); */ - /* - * Check for inode number is _not_ due to possible IO errors. - * We might rmdir the source, keep it as pwd of some process - * and merrily kill the link to whatever was created under the - * same name. Goodbye sticky bit ;-< - */ - old_inode = old_dentry->d_inode; - retval = -ENOENT; - if (!old_page || le32_to_cpu(old_de->inode) != old_inode->i_ino) { - EXIT; - goto end_rename; - } - /* find new inode */ - new_inode = new_dentry->d_inode; - new_page = obdfs_find_entry (new_dir, new_dentry->d_name.name, - new_dentry->d_name.len, &new_de, NOLOCK); - /* PDEBUG(new_page, "rename - new page "); */ - if (new_page) { - if (!new_inode) { - page_cache_release(new_page); - new_page = NULL; - } else { - DQUOT_INIT(new_inode); - } - } - /* in this case we to check more ... */ - if (S_ISDIR(old_inode->i_mode)) { - /* can only rename into empty new directory */ - if (new_inode) { - retval = -ENOTEMPTY; - if (!empty_dir (new_inode)) { - EXIT; - goto end_rename; - } - } - retval = -EIO; - dir_page = obdfs_getpage (old_inode, 0, 0, LOCKED); - /* PDEBUG(dir_page, "rename dir page"); */ - - if (!dir_page) { - EXIT; - goto end_rename; - } - if (le32_to_cpu(PARENT_INO(page_address(dir_page))) != - old_dir->i_ino) { - EXIT; - goto end_rename; - } - retval = -EMLINK; - if (!new_inode && new_dir!=old_dir && - new_dir->i_nlink >= EXT2_LINK_MAX) { - EXIT; - goto end_rename; - } - } - /* create the target dir entry */ - if (!new_page) { - new_page = obdfs_add_entry (new_dir, new_dentry->d_name.name, - new_dentry->d_name.len, &new_de, - &retval); - /* PDEBUG(new_page, "rename new page"); */ - if (!new_page) { - EXIT; - goto end_rename; - } - } - new_dir->i_version = ++event; - - /* - * remove the old entry - */ - new_de->inode = le32_to_cpu(old_inode->i_ino); - if (EXT2_HAS_INCOMPAT_FEATURE(new_dir->i_sb, - EXT2_FEATURE_INCOMPAT_FILETYPE)) - new_de->file_type = old_de->file_type; - - obdfs_delete_entry (old_de, old_page); - - old_dir->i_version = ++event; - if (new_inode) { - new_inode->i_nlink--; - new_inode->i_ctime = CURRENT_TIME; - mark_inode_dirty(new_inode); - } - old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME; - old_dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; - mark_inode_dirty(old_dir); - if (dir_page) { - PARENT_INO(page_address(dir_page)) =le32_to_cpu(new_dir->i_ino); - retval = obdfs_do_writepage(dir_page, IS_SYNC(old_inode)); - /* XXX handle err - not sure if this is correct */ - if (retval) { - EXIT; - goto end_rename; - } - old_dir->i_nlink--; - mark_inode_dirty(old_dir); - if (new_inode) { - new_inode->i_nlink--; - mark_inode_dirty(new_inode); - } else { - new_dir->i_nlink++; - new_dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL; - mark_inode_dirty(new_dir); - } - } - if ( old_page != new_page ) { - unsigned long index = old_page->index; - /* lock the old_page and release unlocked copy */ - CDEBUG(D_INFO, "old_page at %p\n", old_page); - page_cache_release(old_page); - old_page = obdfs_getpage(old_dir, index << PAGE_SHIFT, 0, - LOCKED); - CDEBUG(D_INFO, "old_page at %p\n", old_page); - retval = obdfs_do_writepage(old_page,IS_SYNC(old_dir)); - /* XXX handle err - not sure if this is correct */ - if (retval) { - EXIT; - goto end_rename; - } - } +out_dir: + if (dir_de) { + kunmap(dir_page); + page_cache_release(dir_page); + } +out_old: + kunmap(old_page); + page_cache_release(old_page); +out: + return err; +} - retval = obdfs_do_writepage(new_page, IS_SYNC(new_dir)); - -end_rename: - if (old_page && PageLocked(old_page) ) - obd_unlock_page(old_page); - if (old_page) - page_cache_release(old_page); - if (new_page && PageLocked(new_page) ) - obd_unlock_page(new_page); - if (new_page) - page_cache_release(new_page); - if (dir_page && PageLocked(dir_page) ) - obd_unlock_page(dir_page); - if (dir_page) - page_cache_release(dir_page); - - return retval; -} /* obdfs_rename */ +struct inode_operations obdfs_dir_inode_operations = { + create: obdfs_create, + lookup: obdfs_lookup, + link: obdfs_link, + unlink: obdfs_unlink, + symlink: obdfs_symlink, + mkdir: obdfs_mkdir, + rmdir: obdfs_rmdir, + mknod: obdfs_mknod, + rename: obdfs_rename, +}; -- 1.8.3.1