From: Dave Kleikamp Date: Wed, 25 Sep 2002 05:14:55 +0000 (-0500) Subject: JFS: Fix problems with NFS X-Git-Tag: v2.5.39~13^2~2 X-Git-Url: http://git.neil.brown.name/?a=commitdiff_plain;h=b920ee5669e2cd57bec30242de697a9d55da602b;p=history.git JFS: Fix problems with NFS readdir: Don't hold metadata page while calling filldir(). NFS's filldir may call lookup() which could result in a hang. --- diff --git a/fs/jfs/jfs_dtree.c b/fs/jfs/jfs_dtree.c index 28039afdb4f8..f8c3c0881881 100644 --- a/fs/jfs/jfs_dtree.c +++ b/fs/jfs/jfs_dtree.c @@ -2830,6 +2830,22 @@ void dtInitRoot(tid_t tid, struct inode *ip, u32 idotdot) return; } +struct jfs_dirent { + loff_t position; + int ino; + u16 name_len; + char name[0]; +}; + +inline struct jfs_dirent *next_jfs_dirent(struct jfs_dirent *dirent) +{ + return (struct jfs_dirent *) + ((char *)dirent + + ((sizeof (struct jfs_dirent) + dirent->name_len + 1 + + sizeof (loff_t) - 1) & + ~(sizeof (loff_t) - 1))); +} + /* * jfs_readdir() * @@ -2846,11 +2862,12 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) struct inode *ip = filp->f_dentry->d_inode; struct nls_table *codepage = JFS_SBI(ip->i_sb)->nls_tab; int rc = 0; + loff_t dtpos; /* legacy OS/2 style position */ struct dtoffset { s16 pn; s16 index; s32 unused; - } *dtoffset = (struct dtoffset *) &filp->f_pos; + } *dtoffset = (struct dtoffset *) &dtpos; s64 bn; struct metapage *mp; dtpage_t *p; @@ -2860,12 +2877,16 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) int i, next; struct ldtentry *d; struct dtslot *t; - int d_namleft, d_namlen, len, outlen; - char *d_name, *name_ptr; + int d_namleft, len, outlen; + unsigned long dirent_buf; + char *name_ptr; int dtlhdrdatalen; u32 dir_index; int do_index = 0; uint loop_count = 0; + struct jfs_dirent *jfs_dirent; + int jfs_dirents; + int overflow; if (filp->f_pos == DIREND) return 0; @@ -2885,7 +2906,9 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) if (dir_index > 1) { struct dir_table_slot dirtab_slot; - if (dtEmpty(ip)) { + if (dtEmpty(ip) || + (dir_index >= JFS_IP(ip)->next_index)) { + /* Stale position. Directory has shrunk */ filp->f_pos = DIREND; return 0; } @@ -2963,13 +2986,15 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) */ dtlhdrdatalen = DTLHDRDATALEN_LEGACY; - if (filp->f_pos == 0) { + dtpos = filp->f_pos; + if (dtpos == 0) { /* build "." entry */ if (filldir(dirent, ".", 1, filp->f_pos, ip->i_ino, DT_DIR)) return 0; dtoffset->index = 1; + filp->f_pos = dtpos; } if (dtoffset->pn == 0) { @@ -2985,6 +3010,7 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) } dtoffset->pn = 1; dtoffset->index = 0; + filp->f_pos = dtpos; } if (dtEmpty(ip)) { @@ -3009,32 +3035,48 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) } } - d_name = kmalloc((JFS_NAME_MAX + 1) * sizeof(wchar_t), GFP_NOFS); - if (d_name == NULL) { + dirent_buf = __get_free_page(GFP_KERNEL); + if (dirent_buf == 0) { DT_PUTPAGE(mp); - jERROR(1, ("jfs_readdir: kmalloc failed!\n")); + jERROR(1, ("jfs_readdir: __get_free_page failed!\n")); filp->f_pos = DIREND; - return 0; + return -ENOMEM; } + while (1) { + jfs_dirent = (struct jfs_dirent *) dirent_buf; + jfs_dirents = 0; + overflow = 0; + stbl = DT_GETSTBL(p); for (i = index; i < p->header.nextindex; i++) { d = (struct ldtentry *) & p->slot[stbl[i]]; + if (((long) jfs_dirent + d->namlen + 1) > + (dirent_buf + PSIZE)) { + /* DBCS codepages could overrun dirent_buf */ + index = i; + overflow = 1; + break; + } + d_namleft = d->namlen; - name_ptr = d_name; + name_ptr = jfs_dirent->name; + jfs_dirent->ino = le32_to_cpu(d->inumber); if (do_index) { - filp->f_pos = le32_to_cpu(d->index); + jfs_dirent->position = le32_to_cpu(d->index); len = min(d_namleft, DTLHDRDATALEN); - } else + } else { + jfs_dirent->position = dtpos; len = min(d_namleft, DTLHDRDATALEN_LEGACY); + } /* copy the name of head/only segment */ outlen = jfs_strfromUCS_le(name_ptr, d->name, len, codepage); - d_namlen = outlen; + jfs_dirent->name_len = outlen; /* copy name in the additional segment(s) */ next = d->next; @@ -3053,56 +3095,60 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) len = min(d_namleft, DTSLOTDATALEN); outlen = jfs_strfromUCS_le(name_ptr, t->name, len, codepage); - d_namlen+= outlen; + jfs_dirent->name_len += outlen; next = t->next; } - if (filldir(dirent, d_name, d_namlen, filp->f_pos, - le32_to_cpu(d->inumber), DT_UNKNOWN)) - goto out; + jfs_dirents++; + jfs_dirent = next_jfs_dirent(jfs_dirent); skip_one: if (!do_index) dtoffset->index++; } - /* - * get next leaf page - */ + if (!overflow) { + /* Point to next leaf page */ + if (p->header.flag & BT_ROOT) + bn = 0; + else { + bn = le64_to_cpu(p->header.next); + index = 0; + /* update offset (pn:index) for new page */ + if (!do_index) { + dtoffset->pn++; + dtoffset->index = 0; + } + } + } - if (p->header.flag & BT_ROOT) { - filp->f_pos = DIREND; - break; + /* unpin previous leaf page */ + DT_PUTPAGE(mp); + + jfs_dirent = (struct jfs_dirent *) dirent_buf; + while (jfs_dirents--) { + filp->f_pos = jfs_dirent->position; + if (filldir(dirent, jfs_dirent->name, + jfs_dirent->name_len, filp->f_pos, + jfs_dirent->ino, DT_UNKNOWN)) + goto out; + jfs_dirent = next_jfs_dirent(jfs_dirent); } - bn = le64_to_cpu(p->header.next); - if (bn == 0) { + if (!overflow && (bn == 0)) { filp->f_pos = DIREND; break; } - /* unpin previous leaf page */ - DT_PUTPAGE(mp); - - /* get next leaf page */ DT_GETPAGE(ip, bn, mp, PSIZE, p, rc); if (rc) { - kfree(d_name); + free_page(dirent_buf); return -rc; } - - /* update offset (pn:index) for new page */ - index = 0; - if (!do_index) { - dtoffset->pn++; - dtoffset->index = 0; - } - } out: - kfree(d_name); - DT_PUTPAGE(mp); + free_page(dirent_buf); return rc; } diff --git a/fs/jfs/jfs_imap.c b/fs/jfs/jfs_imap.c index 67102cca446c..081d32a9a6a5 100644 --- a/fs/jfs/jfs_imap.c +++ b/fs/jfs/jfs_imap.c @@ -365,11 +365,7 @@ int diRead(struct inode *ip) if ((lengthPXD(&iagp->inoext[extno]) != imap->im_nbperiext) || (addressPXD(&iagp->inoext[extno]) == 0)) { - jERROR(1, ("diRead: Bad inoext: 0x%lx, 0x%lx\n", - (ulong) addressPXD(&iagp->inoext[extno]), - (ulong) lengthPXD(&iagp->inoext[extno]))); release_metapage(mp); - updateSuper(ip->i_sb, FM_DIRTY); return ESTALE; } @@ -416,12 +412,9 @@ int diRead(struct inode *ip) jERROR(1, ("diRead: i_ino != di_number\n")); updateSuper(ip->i_sb, FM_DIRTY); rc = EIO; - } else if (le32_to_cpu(dp->di_nlink) == 0) { - jERROR(1, - ("diRead: di_nlink is zero. ino=%ld\n", ip->i_ino)); - updateSuper(ip->i_sb, FM_DIRTY); + } else if (le32_to_cpu(dp->di_nlink) == 0) rc = ESTALE; - } else + else /* copy the disk inode to the in-memory inode */ rc = copy_from_dinode(dp, ip);