X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fufs%2Fdir.c;fp=fs%2Fufs%2Fdir.c;h=7c10c68902ae2f22ba2e620c197e57d7233b4b5d;hb=64ba3f394c830ec48a1c31b53dcae312c56f1604;hp=e04327cb85c0078789d39830ce113faef85ad4c5;hpb=be1e6109ac94a859551f8e1774eb9a8469fe055c;p=linux-2.6.git diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c index e04327cb8..7c10c6890 100644 --- a/fs/ufs/dir.c +++ b/fs/ufs/dir.c @@ -11,20 +11,31 @@ * 4.4BSD (FreeBSD) support added on February 1st 1998 by * Niels Kristian Bech Jensen partially based * on code by Martin von Loewis . - * - * Migration to usage of "page cache" on May 2006 by - * Evgeniy Dushistov based on ext2 code base. */ #include #include #include #include +#include #include #include "swab.h" #include "util.h" +#undef UFS_DIR_DEBUG + +#ifdef UFS_DIR_DEBUG +#define UFSD(x) printk("(%s, %d), %s: ", __FILE__, __LINE__, __FUNCTION__); printk x; +#else +#define UFSD(x) +#endif + +static int +ufs_check_dir_entry (const char *, struct inode *, struct ufs_dir_entry *, + struct buffer_head *, unsigned long); + + /* * NOTE! unlike strncmp, ufs_match returns 1 for success, 0 for failure. * @@ -40,541 +51,495 @@ static inline int ufs_match(struct super_block *sb, int len, return !memcmp(name, de->d_name, len); } -static int ufs_commit_chunk(struct page *page, unsigned from, unsigned to) +/* + * This is blatantly stolen from ext2fs + */ +static int +ufs_readdir (struct file * filp, void * dirent, filldir_t filldir) { - struct inode *dir = page->mapping->host; - int err = 0; - dir->i_version++; - page->mapping->a_ops->commit_write(NULL, page, from, to); - if (IS_DIRSYNC(dir)) - err = write_one_page(page, 1); - else - unlock_page(page); - return err; -} + struct inode *inode = filp->f_dentry->d_inode; + int error = 0; + unsigned long offset, lblk; + int i, stored; + struct buffer_head * bh; + struct ufs_dir_entry * de; + struct super_block * sb; + int de_reclen; + unsigned flags; + u64 blk= 0L; + + lock_kernel(); + + sb = inode->i_sb; + flags = UFS_SB(sb)->s_flags; + + UFSD(("ENTER, ino %lu f_pos %lu\n", inode->i_ino, (unsigned long) filp->f_pos)) + + stored = 0; + bh = NULL; + offset = filp->f_pos & (sb->s_blocksize - 1); + + while (!error && !stored && filp->f_pos < inode->i_size) { + lblk = (filp->f_pos) >> sb->s_blocksize_bits; + blk = ufs_frag_map(inode, lblk); + if (!blk || !(bh = sb_bread(sb, blk))) { + /* XXX - error - skip to the next block */ + printk("ufs_readdir: " + "dir inode %lu has a hole at offset %lu\n", + inode->i_ino, (unsigned long int)filp->f_pos); + filp->f_pos += sb->s_blocksize - offset; + continue; + } -static inline void ufs_put_page(struct page *page) -{ - kunmap(page); - page_cache_release(page); -} +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 ufs_dir_entry *)(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. */ + de_reclen = fs16_to_cpu(sb, de->d_reclen); + if (de_reclen < 1) + break; + i += de_reclen; + } + offset = i; + filp->f_pos = (filp->f_pos & ~(sb->s_blocksize - 1)) + | offset; + filp->f_version = inode->i_version; + } -static inline unsigned long ufs_dir_pages(struct inode *inode) -{ - return (inode->i_size+PAGE_CACHE_SIZE-1)>>PAGE_CACHE_SHIFT; -} + while (!error && filp->f_pos < inode->i_size + && offset < sb->s_blocksize) { + de = (struct ufs_dir_entry *) (bh->b_data + offset); + /* XXX - put in a real ufs_check_dir_entry() */ + if ((de->d_reclen == 0) || (ufs_get_de_namlen(sb, de) == 0)) { + filp->f_pos = (filp->f_pos & + (sb->s_blocksize - 1)) + + sb->s_blocksize; + brelse(bh); + unlock_kernel(); + return stored; + } + if (!ufs_check_dir_entry ("ufs_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)) + + 1; + brelse (bh); + unlock_kernel(); + return stored; + } + offset += fs16_to_cpu(sb, de->d_reclen); + if (de->d_ino) { + /* 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. */ + unsigned long version = filp->f_version; + unsigned char d_type = DT_UNKNOWN; -ino_t ufs_inode_by_name(struct inode *dir, struct dentry *dentry) -{ - ino_t res = 0; - struct ufs_dir_entry *de; - struct page *page; - - de = ufs_find_entry(dir, dentry, &page); - if (de) { - res = fs32_to_cpu(dir->i_sb, de->d_ino); - ufs_put_page(page); + UFSD(("filldir(%s,%u)\n", de->d_name, + fs32_to_cpu(sb, de->d_ino))) + UFSD(("namlen %u\n", ufs_get_de_namlen(sb, de))) + + if ((flags & UFS_DE_MASK) == UFS_DE_44BSD) + d_type = de->d_u.d_44.d_type; + error = filldir(dirent, de->d_name, + ufs_get_de_namlen(sb, de), filp->f_pos, + fs32_to_cpu(sb, de->d_ino), d_type); + if (error) + break; + if (version != filp->f_version) + goto revalidate; + stored ++; + } + filp->f_pos += fs16_to_cpu(sb, de->d_reclen); + } + offset = 0; + brelse (bh); } - return res; + unlock_kernel(); + return 0; } +/* + * define how far ahead to read directories while searching them. + */ +#define NAMEI_RA_CHUNKS 2 +#define NAMEI_RA_BLOCKS 4 +#define NAMEI_RA_SIZE (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS) +#define NAMEI_RA_INDEX(c,b) (((c) * NAMEI_RA_BLOCKS) + (b)) -/* Releases the page */ -void ufs_set_link(struct inode *dir, struct ufs_dir_entry *de, - struct page *page, struct inode *inode) +/* + * ufs_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_bh). It does NOT read the inode of the + * entry - you'll have to do that yourself if you want to. + */ +struct ufs_dir_entry * ufs_find_entry (struct dentry *dentry, + struct buffer_head ** res_bh) { - unsigned from = (char *) de - (char *) page_address(page); - unsigned to = from + fs16_to_cpu(dir->i_sb, de->d_reclen); - int err; + struct super_block * sb; + struct buffer_head * bh_use[NAMEI_RA_SIZE]; + struct buffer_head * bh_read[NAMEI_RA_SIZE]; + unsigned long offset; + int block, toread, i, err; + struct inode *dir = dentry->d_parent->d_inode; + const char *name = dentry->d_name.name; + int namelen = dentry->d_name.len; - lock_page(page); - err = page->mapping->a_ops->prepare_write(NULL, page, from, to); - BUG_ON(err); - de->d_ino = cpu_to_fs32(dir->i_sb, inode->i_ino); - ufs_set_de_type(dir->i_sb, de, inode->i_mode); - err = ufs_commit_chunk(page, from, to); - ufs_put_page(page); - dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; - mark_inode_dirty(dir); -} + UFSD(("ENTER, dir_ino %lu, name %s, namlen %u\n", dir->i_ino, name, namelen)) + + *res_bh = NULL; + + sb = dir->i_sb; + + if (namelen > UFS_MAXNAMLEN) + return NULL; + memset (bh_use, 0, sizeof (bh_use)); + toread = 0; + for (block = 0; block < NAMEI_RA_SIZE; ++block) { + struct buffer_head * bh; -static void ufs_check_page(struct page *page) -{ - struct inode *dir = page->mapping->host; - struct super_block *sb = dir->i_sb; - char *kaddr = page_address(page); - unsigned offs, rec_len; - unsigned limit = PAGE_CACHE_SIZE; - struct ufs_dir_entry *p; - char *error; - - if ((dir->i_size >> PAGE_CACHE_SHIFT) == page->index) { - limit = dir->i_size & ~PAGE_CACHE_MASK; - if (limit & (UFS_SECTOR_SIZE - 1)) - goto Ebadsize; - if (!limit) - goto out; - } - for (offs = 0; offs <= limit - UFS_DIR_REC_LEN(1); offs += rec_len) { - p = (struct ufs_dir_entry *)(kaddr + offs); - rec_len = fs16_to_cpu(sb, p->d_reclen); - - if (rec_len < UFS_DIR_REC_LEN(1)) - goto Eshort; - if (rec_len & 3) - goto Ealign; - if (rec_len < UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, p))) - goto Enamelen; - if (((offs + rec_len - 1) ^ offs) & ~(UFS_SECTOR_SIZE-1)) - goto Espan; - if (fs32_to_cpu(sb, p->d_ino) > (UFS_SB(sb)->s_uspi->s_ipg * - UFS_SB(sb)->s_uspi->s_ncg)) - goto Einumber; + if ((block << sb->s_blocksize_bits) >= dir->i_size) + break; + bh = ufs_getfrag (dir, block, 0, &err); + bh_use[block] = bh; + if (bh && !buffer_uptodate(bh)) + bh_read[toread++] = bh; } - if (offs != limit) - goto Eend; -out: - SetPageFsMisc(page); - return; - - /* Too bad, we had an error */ - -Ebadsize: - ufs_error(sb, "ufs_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: - ufs_error (sb, "ufs_check_page", "bad entry in directory #%lu: %s - " - "offset=%lu, rec_len=%d, name_len=%d", - dir->i_ino, error, (page->index<i_ino, (page->index<i_mapping; - struct page *page = read_cache_page(mapping, n, - (filler_t*)mapping->a_ops->readpage, NULL); - if (!IS_ERR(page)) { - wait_on_page_locked(page); - kmap(page); - if (!PageUptodate(page)) - goto fail; - if (!PageFsMisc(page)) - ufs_check_page(page); - if (PageError(page)) - goto fail; + for (block = 0, offset = 0; offset < dir->i_size; block++) { + struct buffer_head * bh; + struct ufs_dir_entry * de; + char * dlimit; + + if ((block % NAMEI_RA_BLOCKS) == 0 && toread) { + ll_rw_block (READ, toread, bh_read); + toread = 0; + } + bh = bh_use[block % NAMEI_RA_SIZE]; + if (!bh) { + ufs_error (sb, "ufs_find_entry", + "directory #%lu contains a hole at offset %lu", + dir->i_ino, offset); + offset += sb->s_blocksize; + continue; + } + wait_on_buffer (bh); + if (!buffer_uptodate(bh)) { + /* + * read error: all bets are off + */ + break; + } + + de = (struct ufs_dir_entry *) bh->b_data; + dlimit = bh->b_data + sb->s_blocksize; + while ((char *) de < dlimit && offset < dir->i_size) { + /* this code is executed quadratically often */ + /* do minimal checking by hand */ + int de_len; + + if ((char *) de + namelen <= dlimit && + ufs_match(sb, namelen, name, de)) { + /* found a match - + just to be sure, do a full check */ + if (!ufs_check_dir_entry("ufs_find_entry", + dir, de, bh, offset)) + goto failed; + for (i = 0; i < NAMEI_RA_SIZE; ++i) { + if (bh_use[i] != bh) + brelse (bh_use[i]); + } + *res_bh = bh; + return de; + } + /* prevent looping on a bad block */ + de_len = fs16_to_cpu(sb, de->d_reclen); + if (de_len <= 0) + goto failed; + offset += de_len; + de = (struct ufs_dir_entry *) ((char *) de + de_len); + } + + brelse (bh); + if (((block + NAMEI_RA_SIZE) << sb->s_blocksize_bits ) >= + dir->i_size) + bh = NULL; + else + bh = ufs_getfrag (dir, block + NAMEI_RA_SIZE, 0, &err); + bh_use[block % NAMEI_RA_SIZE] = bh; + if (bh && !buffer_uptodate(bh)) + bh_read[toread++] = bh; } - return page; -fail: - ufs_put_page(page); - return ERR_PTR(-EIO); +failed: + for (i = 0; i < NAMEI_RA_SIZE; ++i) brelse (bh_use[i]); + UFSD(("EXIT\n")) + return NULL; } -/* - * Return the offset into page `page_nr' of the last valid - * byte in that page, plus one. - */ -static unsigned -ufs_last_byte(struct inode *inode, unsigned long page_nr) +static int +ufs_check_dir_entry (const char *function, struct inode *dir, + struct ufs_dir_entry *de, struct buffer_head *bh, + unsigned long offset) { - unsigned last_byte = inode->i_size; - - last_byte -= page_nr << PAGE_CACHE_SHIFT; - if (last_byte > PAGE_CACHE_SIZE) - last_byte = PAGE_CACHE_SIZE; - return last_byte; + struct super_block *sb = dir->i_sb; + const char *error_msg = NULL; + int rlen = fs16_to_cpu(sb, de->d_reclen); + + if (rlen < UFS_DIR_REC_LEN(1)) + error_msg = "reclen is smaller than minimal"; + else if (rlen % 4 != 0) + error_msg = "reclen % 4 != 0"; + else if (rlen < UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de))) + error_msg = "reclen is too small for namlen"; + else if (((char *) de - bh->b_data) + rlen > dir->i_sb->s_blocksize) + error_msg = "directory entry across blocks"; + else if (fs32_to_cpu(sb, de->d_ino) > (UFS_SB(sb)->s_uspi->s_ipg * + UFS_SB(sb)->s_uspi->s_ncg)) + error_msg = "inode out of bounds"; + + if (error_msg != NULL) + ufs_error (sb, function, "bad entry in directory #%lu, size %Lu: %s - " + "offset=%lu, inode=%lu, reclen=%d, namlen=%d", + dir->i_ino, dir->i_size, error_msg, offset, + (unsigned long)fs32_to_cpu(sb, de->d_ino), + rlen, ufs_get_de_namlen(sb, de)); + + return (error_msg == NULL ? 1 : 0); } -static inline struct ufs_dir_entry * -ufs_next_entry(struct super_block *sb, struct ufs_dir_entry *p) +struct ufs_dir_entry *ufs_dotdot(struct inode *dir, struct buffer_head **p) { - return (struct ufs_dir_entry *)((char *)p + - fs16_to_cpu(sb, p->d_reclen)); -} + int err; + struct buffer_head *bh = ufs_bread (dir, 0, 0, &err); + struct ufs_dir_entry *res = NULL; -struct ufs_dir_entry *ufs_dotdot(struct inode *dir, struct page **p) + if (bh) { + res = (struct ufs_dir_entry *) bh->b_data; + res = (struct ufs_dir_entry *)((char *)res + + fs16_to_cpu(dir->i_sb, res->d_reclen)); + } + *p = bh; + return res; +} +ino_t ufs_inode_by_name(struct inode * dir, struct dentry *dentry) { - struct page *page = ufs_get_page(dir, 0); - struct ufs_dir_entry *de = NULL; + ino_t res = 0; + struct ufs_dir_entry * de; + struct buffer_head *bh; - if (!IS_ERR(page)) { - de = ufs_next_entry(dir->i_sb, - (struct ufs_dir_entry *)page_address(page)); - *p = page; + de = ufs_find_entry (dentry, &bh); + if (de) { + res = fs32_to_cpu(dir->i_sb, de->d_ino); + brelse(bh); } - return de; + return res; } -/* - * ufs_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 ufs_dir_entry *ufs_find_entry(struct inode *dir, struct dentry *dentry, - struct page **res_page) +void ufs_set_link(struct inode *dir, struct ufs_dir_entry *de, + struct buffer_head *bh, struct inode *inode) { - struct super_block *sb = dir->i_sb; - const char *name = dentry->d_name.name; - int namelen = dentry->d_name.len; - unsigned reclen = UFS_DIR_REC_LEN(namelen); - unsigned long start, n; - unsigned long npages = ufs_dir_pages(dir); - struct page *page = NULL; - struct ufs_inode_info *ui = UFS_I(dir); - struct ufs_dir_entry *de; - - UFSD("ENTER, dir_ino %lu, name %s, namlen %u\n", dir->i_ino, name, namelen); - - if (npages == 0 || namelen > UFS_MAXNAMLEN) - goto out; - - /* OFFSET_CACHE */ - *res_page = NULL; - - start = ui->i_dir_start_lookup; - - if (start >= npages) - start = 0; - n = start; - do { - char *kaddr; - page = ufs_get_page(dir, n); - if (!IS_ERR(page)) { - kaddr = page_address(page); - de = (struct ufs_dir_entry *) kaddr; - kaddr += ufs_last_byte(dir, n) - reclen; - while ((char *) de <= kaddr) { - if (de->d_reclen == 0) { - ufs_error(dir->i_sb, __FUNCTION__, - "zero-length directory entry"); - ufs_put_page(page); - goto out; - } - if (ufs_match(sb, namelen, name, de)) - goto found; - de = ufs_next_entry(sb, de); - } - ufs_put_page(page); - } - if (++n >= npages) - n = 0; - } while (n != start); -out: - return NULL; - -found: - *res_page = page; - ui->i_dir_start_lookup = n; - return de; + dir->i_version++; + de->d_ino = cpu_to_fs32(dir->i_sb, inode->i_ino); + mark_buffer_dirty(bh); + if (IS_DIRSYNC(dir)) + sync_dirty_buffer(bh); + brelse (bh); } /* - * Parent is locked. + * ufs_add_entry() + * + * adds a file entry to the specified directory, using the same + * semantics as ufs_find_entry(). It returns NULL if it failed. */ int ufs_add_link(struct dentry *dentry, struct inode *inode) { + struct super_block * sb; + struct ufs_sb_private_info * uspi; + unsigned long offset; + unsigned fragoff; + unsigned short rec_len; + struct buffer_head * bh; + struct ufs_dir_entry * de, * de1; struct inode *dir = dentry->d_parent->d_inode; const char *name = dentry->d_name.name; int namelen = dentry->d_name.len; - struct super_block *sb = dir->i_sb; - unsigned reclen = UFS_DIR_REC_LEN(namelen); - unsigned short rec_len, name_len; - struct page *page = NULL; - struct ufs_dir_entry *de; - unsigned long npages = ufs_dir_pages(dir); - unsigned long n; - char *kaddr; - unsigned from, to; int err; - UFSD("ENTER, name %s, namelen %u\n", name, namelen); - - /* - * 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 = ufs_get_page(dir, n); - err = PTR_ERR(page); - if (IS_ERR(page)) - goto out; - lock_page(page); - kaddr = page_address(page); - dir_end = kaddr + ufs_last_byte(dir, n); - de = (struct ufs_dir_entry *)kaddr; - kaddr += PAGE_CACHE_SIZE - reclen; - while ((char *)de <= kaddr) { - if ((char *)de == dir_end) { - /* We hit i_size */ - name_len = 0; - rec_len = UFS_SECTOR_SIZE; - de->d_reclen = cpu_to_fs16(sb, UFS_SECTOR_SIZE); - de->d_ino = 0; - goto got_it; + UFSD(("ENTER, name %s, namelen %u\n", name, namelen)) + + sb = dir->i_sb; + uspi = UFS_SB(sb)->s_uspi; + + if (!namelen) + return -EINVAL; + bh = ufs_bread (dir, 0, 0, &err); + if (!bh) + return err; + rec_len = UFS_DIR_REC_LEN(namelen); + offset = 0; + de = (struct ufs_dir_entry *) bh->b_data; + while (1) { + if ((char *)de >= UFS_SECTOR_SIZE + bh->b_data) { + fragoff = offset & ~uspi->s_fmask; + if (fragoff != 0 && fragoff != UFS_SECTOR_SIZE) + ufs_error (sb, "ufs_add_entry", "internal error" + " fragoff %u", fragoff); + if (!fragoff) { + brelse (bh); + bh = ufs_bread (dir, offset >> sb->s_blocksize_bits, 1, &err); + if (!bh) + return err; } - if (de->d_reclen == 0) { - ufs_error(dir->i_sb, __FUNCTION__, - "zero-length directory entry"); - err = -EIO; - goto out_unlock; + if (dir->i_size <= offset) { + if (dir->i_size == 0) { + brelse(bh); + return -ENOENT; + } + de = (struct ufs_dir_entry *) (bh->b_data + fragoff); + de->d_ino = 0; + de->d_reclen = cpu_to_fs16(sb, UFS_SECTOR_SIZE); + ufs_set_de_namlen(sb, de, 0); + dir->i_size = offset + UFS_SECTOR_SIZE; + mark_inode_dirty(dir); + } else { + de = (struct ufs_dir_entry *) bh->b_data; } - err = -EEXIST; - if (ufs_match(sb, namelen, name, de)) - goto out_unlock; - name_len = UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de)); - rec_len = fs16_to_cpu(sb, de->d_reclen); - if (!de->d_ino && rec_len >= reclen) - goto got_it; - if (rec_len >= name_len + reclen) - goto got_it; - de = (struct ufs_dir_entry *) ((char *) de + rec_len); } - unlock_page(page); - ufs_put_page(page); + if (!ufs_check_dir_entry ("ufs_add_entry", dir, de, bh, offset)) { + brelse (bh); + return -ENOENT; + } + if (ufs_match(sb, namelen, name, de)) { + brelse (bh); + return -EEXIST; + } + if (de->d_ino == 0 && fs16_to_cpu(sb, de->d_reclen) >= rec_len) + break; + + if (fs16_to_cpu(sb, de->d_reclen) >= + UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de)) + rec_len) + break; + offset += fs16_to_cpu(sb, de->d_reclen); + de = (struct ufs_dir_entry *) ((char *) de + fs16_to_cpu(sb, de->d_reclen)); } - BUG(); - return -EINVAL; - -got_it: - from = (char*)de - (char*)page_address(page); - to = from + rec_len; - err = page->mapping->a_ops->prepare_write(NULL, page, from, to); - if (err) - goto out_unlock; - if (de->d_ino) { - struct ufs_dir_entry *de1 = - (struct ufs_dir_entry *) ((char *) de + name_len); - de1->d_reclen = cpu_to_fs16(sb, rec_len - name_len); - de->d_reclen = cpu_to_fs16(sb, name_len); + if (de->d_ino) { + de1 = (struct ufs_dir_entry *) ((char *) de + + UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de))); + de1->d_reclen = + cpu_to_fs16(sb, fs16_to_cpu(sb, de->d_reclen) - + UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de))); + de->d_reclen = + cpu_to_fs16(sb, UFS_DIR_REC_LEN(ufs_get_de_namlen(sb, de))); de = de1; } - + de->d_ino = 0; ufs_set_de_namlen(sb, de, namelen); - memcpy(de->d_name, name, namelen + 1); + memcpy (de->d_name, name, namelen + 1); de->d_ino = cpu_to_fs32(sb, inode->i_ino); ufs_set_de_type(sb, de, inode->i_mode); - - err = ufs_commit_chunk(page, from, to); + mark_buffer_dirty(bh); + if (IS_DIRSYNC(dir)) + sync_dirty_buffer(bh); + brelse (bh); dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; - + dir->i_version++; mark_inode_dirty(dir); - /* OFFSET_CACHE */ -out_put: - ufs_put_page(page); -out: - return err; -out_unlock: - unlock_page(page); - goto out_put; -} -static inline unsigned -ufs_validate_entry(struct super_block *sb, char *base, - unsigned offset, unsigned mask) -{ - struct ufs_dir_entry *de = (struct ufs_dir_entry*)(base + offset); - struct ufs_dir_entry *p = (struct ufs_dir_entry*)(base + (offset&mask)); - while ((char*)p < (char*)de) { - if (p->d_reclen == 0) - break; - p = ufs_next_entry(sb, p); - } - return (char *)p - base; -} - - -/* - * This is blatantly stolen from ext2fs - */ -static int -ufs_readdir(struct file *filp, void *dirent, filldir_t filldir) -{ - loff_t pos = filp->f_pos; - struct inode *inode = filp->f_dentry->d_inode; - struct super_block *sb = inode->i_sb; - unsigned int offset = pos & ~PAGE_CACHE_MASK; - unsigned long n = pos >> PAGE_CACHE_SHIFT; - unsigned long npages = ufs_dir_pages(inode); - unsigned chunk_mask = ~(UFS_SECTOR_SIZE - 1); - int need_revalidate = filp->f_version != inode->i_version; - unsigned flags = UFS_SB(sb)->s_flags; - - UFSD("BEGIN\n"); - - if (pos > inode->i_size - UFS_DIR_REC_LEN(1)) - return 0; - - for ( ; n < npages; n++, offset = 0) { - char *kaddr, *limit; - struct ufs_dir_entry *de; - - struct page *page = ufs_get_page(inode, n); - - if (IS_ERR(page)) { - ufs_error(sb, __FUNCTION__, - "bad page in #%lu", - inode->i_ino); - filp->f_pos += PAGE_CACHE_SIZE - offset; - return -EIO; - } - kaddr = page_address(page); - if (unlikely(need_revalidate)) { - if (offset) { - offset = ufs_validate_entry(sb, kaddr, offset, chunk_mask); - filp->f_pos = (n<f_version = inode->i_version; - need_revalidate = 0; - } - de = (struct ufs_dir_entry *)(kaddr+offset); - limit = kaddr + ufs_last_byte(inode, n) - UFS_DIR_REC_LEN(1); - for ( ;(char*)de <= limit; de = ufs_next_entry(sb, de)) { - if (de->d_reclen == 0) { - ufs_error(sb, __FUNCTION__, - "zero-length directory entry"); - ufs_put_page(page); - return -EIO; - } - if (de->d_ino) { - int over; - unsigned char d_type = DT_UNKNOWN; - - offset = (char *)de - kaddr; - - UFSD("filldir(%s,%u)\n", de->d_name, - fs32_to_cpu(sb, de->d_ino)); - UFSD("namlen %u\n", ufs_get_de_namlen(sb, de)); - - if ((flags & UFS_DE_MASK) == UFS_DE_44BSD) - d_type = de->d_u.d_44.d_type; - - over = filldir(dirent, de->d_name, - ufs_get_de_namlen(sb, de), - (n<d_ino), d_type); - if (over) { - ufs_put_page(page); - return 0; - } - } - filp->f_pos += fs16_to_cpu(sb, de->d_reclen); - } - ufs_put_page(page); - } + UFSD(("EXIT\n")) return 0; } - /* * ufs_delete_entry deletes a directory entry by merging it with the * previous entry. */ -int ufs_delete_entry(struct inode *inode, struct ufs_dir_entry *dir, - struct page * page) +int ufs_delete_entry (struct inode * inode, struct ufs_dir_entry * dir, + struct buffer_head * bh ) + { - struct super_block *sb = inode->i_sb; - struct address_space *mapping = page->mapping; - char *kaddr = page_address(page); - unsigned from = ((char*)dir - kaddr) & ~(UFS_SECTOR_SIZE - 1); - unsigned to = ((char*)dir - kaddr) + fs16_to_cpu(sb, dir->d_reclen); - struct ufs_dir_entry *pde = NULL; - struct ufs_dir_entry *de = (struct ufs_dir_entry *) (kaddr + from); - int err; - - UFSD("ENTER\n"); - - UFSD("ino %u, reclen %u, namlen %u, name %s\n", - fs32_to_cpu(sb, de->d_ino), - fs16_to_cpu(sb, de->d_reclen), - ufs_get_de_namlen(sb, de), de->d_name); + struct super_block * sb; + struct ufs_dir_entry * de, * pde; + unsigned i; + + UFSD(("ENTER\n")) - while ((char*)de < (char*)dir) { - if (de->d_reclen == 0) { - ufs_error(inode->i_sb, __FUNCTION__, - "zero-length directory entry"); - err = -EIO; - goto out; + sb = inode->i_sb; + i = 0; + pde = NULL; + de = (struct ufs_dir_entry *) bh->b_data; + + UFSD(("ino %u, reclen %u, namlen %u, name %s\n", + fs32_to_cpu(sb, de->d_ino), + fs16_to_cpu(sb, de->d_reclen), + ufs_get_de_namlen(sb, de), de->d_name)) + + while (i < bh->b_size) { + if (!ufs_check_dir_entry ("ufs_delete_entry", inode, de, bh, i)) { + brelse(bh); + return -EIO; + } + if (de == dir) { + if (pde) + fs16_add(sb, &pde->d_reclen, + fs16_to_cpu(sb, dir->d_reclen)); + dir->d_ino = 0; + inode->i_version++; + inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; + mark_inode_dirty(inode); + mark_buffer_dirty(bh); + if (IS_DIRSYNC(inode)) + sync_dirty_buffer(bh); + brelse(bh); + UFSD(("EXIT\n")) + return 0; } - pde = de; - de = ufs_next_entry(sb, de); + i += fs16_to_cpu(sb, de->d_reclen); + if (i == UFS_SECTOR_SIZE) pde = NULL; + else pde = de; + de = (struct ufs_dir_entry *) + ((char *) de + fs16_to_cpu(sb, de->d_reclen)); + if (i == UFS_SECTOR_SIZE && de->d_reclen == 0) + break; } - if (pde) - from = (char*)pde - (char*)page_address(page); - lock_page(page); - err = mapping->a_ops->prepare_write(NULL, page, from, to); - BUG_ON(err); - if (pde) - pde->d_reclen = cpu_to_fs16(sb, to-from); - dir->d_ino = 0; - err = ufs_commit_chunk(page, from, to); - inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC; - mark_inode_dirty(inode); -out: - ufs_put_page(page); - UFSD("EXIT\n"); - return err; + UFSD(("EXIT\n")) + brelse(bh); + return -ENOENT; } int ufs_make_empty(struct inode * inode, struct inode *dir) { struct super_block * sb = dir->i_sb; - struct address_space *mapping = inode->i_mapping; - struct page *page = grab_cache_page(mapping, 0); + struct buffer_head * dir_block; struct ufs_dir_entry * de; - char *base; int err; - if (!page) - return -ENOMEM; - kmap(page); - err = mapping->a_ops->prepare_write(NULL, page, 0, UFS_SECTOR_SIZE); - if (err) { - unlock_page(page); - goto fail; - } - - - base = (char*)page_address(page); - memset(base, 0, PAGE_CACHE_SIZE); - - de = (struct ufs_dir_entry *) base; + dir_block = ufs_bread (inode, 0, 1, &err); + if (!dir_block) + return err; + inode->i_blocks = sb->s_blocksize / UFS_SECTOR_SIZE; + de = (struct ufs_dir_entry *) dir_block->b_data; de->d_ino = cpu_to_fs32(sb, inode->i_ino); ufs_set_de_type(sb, de, inode->i_mode); ufs_set_de_namlen(sb, de, 1); @@ -587,68 +552,75 @@ int ufs_make_empty(struct inode * inode, struct inode *dir) de->d_reclen = cpu_to_fs16(sb, UFS_SECTOR_SIZE - UFS_DIR_REC_LEN(1)); ufs_set_de_namlen(sb, de, 2); strcpy (de->d_name, ".."); - - err = ufs_commit_chunk(page, 0, UFS_SECTOR_SIZE); -fail: - kunmap(page); - page_cache_release(page); - return err; + mark_buffer_dirty(dir_block); + brelse (dir_block); + mark_inode_dirty(inode); + return 0; } /* * routine to check that the specified directory is empty (for rmdir) */ -int ufs_empty_dir(struct inode * inode) +int ufs_empty_dir (struct inode * inode) { - struct super_block *sb = inode->i_sb; - struct page *page = NULL; - unsigned long i, npages = ufs_dir_pages(inode); - - for (i = 0; i < npages; i++) { - char *kaddr; - struct ufs_dir_entry *de; - page = ufs_get_page(inode, i); - - if (IS_ERR(page)) - continue; - - kaddr = page_address(page); - de = (struct ufs_dir_entry *)kaddr; - kaddr += ufs_last_byte(inode, i) - UFS_DIR_REC_LEN(1); - - while ((char *)de <= kaddr) { - if (de->d_reclen == 0) { - ufs_error(inode->i_sb, __FUNCTION__, - "zero-length directory entry: " - "kaddr=%p, de=%p\n", kaddr, de); - goto not_empty; - } - if (de->d_ino) { - u16 namelen=ufs_get_de_namlen(sb, de); - /* check for . and .. */ - if (de->d_name[0] != '.') - goto not_empty; - if (namelen > 2) - goto not_empty; - if (namelen < 2) { - if (inode->i_ino != - fs32_to_cpu(sb, de->d_ino)) - goto not_empty; - } else if (de->d_name[1] != '.') - goto not_empty; + struct super_block * sb; + unsigned long offset; + struct buffer_head * bh; + struct ufs_dir_entry * de, * de1; + int err; + + sb = inode->i_sb; + + if (inode->i_size < UFS_DIR_REC_LEN(1) + UFS_DIR_REC_LEN(2) || + !(bh = ufs_bread (inode, 0, 0, &err))) { + ufs_warning (inode->i_sb, "empty_dir", + "bad directory (dir #%lu) - no data block", + inode->i_ino); + return 1; + } + de = (struct ufs_dir_entry *) bh->b_data; + de1 = (struct ufs_dir_entry *) + ((char *)de + fs16_to_cpu(sb, de->d_reclen)); + if (fs32_to_cpu(sb, de->d_ino) != inode->i_ino || de1->d_ino == 0 || + strcmp (".", de->d_name) || strcmp ("..", de1->d_name)) { + ufs_warning (inode->i_sb, "empty_dir", + "bad directory (dir #%lu) - no `.' or `..'", + inode->i_ino); + return 1; + } + offset = fs16_to_cpu(sb, de->d_reclen) + fs16_to_cpu(sb, de1->d_reclen); + de = (struct ufs_dir_entry *) + ((char *)de1 + fs16_to_cpu(sb, de1->d_reclen)); + while (offset < inode->i_size ) { + if (!bh || (void *) de >= (void *) (bh->b_data + sb->s_blocksize)) { + brelse (bh); + bh = ufs_bread (inode, offset >> sb->s_blocksize_bits, 1, &err); + if (!bh) { + ufs_error (sb, "empty_dir", + "directory #%lu contains a hole at offset %lu", + inode->i_ino, offset); + offset += sb->s_blocksize; + continue; } - de = ufs_next_entry(sb, de); + de = (struct ufs_dir_entry *) bh->b_data; + } + if (!ufs_check_dir_entry ("empty_dir", inode, de, bh, offset)) { + brelse (bh); + return 1; } - ufs_put_page(page); + if (de->d_ino) { + brelse (bh); + return 0; + } + offset += fs16_to_cpu(sb, de->d_reclen); + de = (struct ufs_dir_entry *) + ((char *)de + fs16_to_cpu(sb, de->d_reclen)); } + brelse (bh); return 1; - -not_empty: - ufs_put_page(page); - return 0; } -const struct file_operations ufs_dir_operations = { +struct file_operations ufs_dir_operations = { .read = generic_read_dir, .readdir = ufs_readdir, .fsync = file_fsync,