Commit b920ee56 authored by Dave Kleikamp's avatar Dave Kleikamp

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.
parent 0de4d503
...@@ -2830,6 +2830,22 @@ void dtInitRoot(tid_t tid, struct inode *ip, u32 idotdot) ...@@ -2830,6 +2830,22 @@ void dtInitRoot(tid_t tid, struct inode *ip, u32 idotdot)
return; 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() * jfs_readdir()
* *
...@@ -2846,11 +2862,12 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -2846,11 +2862,12 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
struct inode *ip = filp->f_dentry->d_inode; struct inode *ip = filp->f_dentry->d_inode;
struct nls_table *codepage = JFS_SBI(ip->i_sb)->nls_tab; struct nls_table *codepage = JFS_SBI(ip->i_sb)->nls_tab;
int rc = 0; int rc = 0;
loff_t dtpos; /* legacy OS/2 style position */
struct dtoffset { struct dtoffset {
s16 pn; s16 pn;
s16 index; s16 index;
s32 unused; s32 unused;
} *dtoffset = (struct dtoffset *) &filp->f_pos; } *dtoffset = (struct dtoffset *) &dtpos;
s64 bn; s64 bn;
struct metapage *mp; struct metapage *mp;
dtpage_t *p; dtpage_t *p;
...@@ -2860,12 +2877,16 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -2860,12 +2877,16 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
int i, next; int i, next;
struct ldtentry *d; struct ldtentry *d;
struct dtslot *t; struct dtslot *t;
int d_namleft, d_namlen, len, outlen; int d_namleft, len, outlen;
char *d_name, *name_ptr; unsigned long dirent_buf;
char *name_ptr;
int dtlhdrdatalen; int dtlhdrdatalen;
u32 dir_index; u32 dir_index;
int do_index = 0; int do_index = 0;
uint loop_count = 0; uint loop_count = 0;
struct jfs_dirent *jfs_dirent;
int jfs_dirents;
int overflow;
if (filp->f_pos == DIREND) if (filp->f_pos == DIREND)
return 0; return 0;
...@@ -2885,7 +2906,9 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -2885,7 +2906,9 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
if (dir_index > 1) { if (dir_index > 1) {
struct dir_table_slot dirtab_slot; 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; filp->f_pos = DIREND;
return 0; return 0;
} }
...@@ -2963,13 +2986,15 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -2963,13 +2986,15 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
*/ */
dtlhdrdatalen = DTLHDRDATALEN_LEGACY; dtlhdrdatalen = DTLHDRDATALEN_LEGACY;
if (filp->f_pos == 0) { dtpos = filp->f_pos;
if (dtpos == 0) {
/* build "." entry */ /* build "." entry */
if (filldir(dirent, ".", 1, filp->f_pos, ip->i_ino, if (filldir(dirent, ".", 1, filp->f_pos, ip->i_ino,
DT_DIR)) DT_DIR))
return 0; return 0;
dtoffset->index = 1; dtoffset->index = 1;
filp->f_pos = dtpos;
} }
if (dtoffset->pn == 0) { if (dtoffset->pn == 0) {
...@@ -2985,6 +3010,7 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -2985,6 +3010,7 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
} }
dtoffset->pn = 1; dtoffset->pn = 1;
dtoffset->index = 0; dtoffset->index = 0;
filp->f_pos = dtpos;
} }
if (dtEmpty(ip)) { if (dtEmpty(ip)) {
...@@ -3009,32 +3035,48 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -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); dirent_buf = __get_free_page(GFP_KERNEL);
if (d_name == NULL) { if (dirent_buf == 0) {
DT_PUTPAGE(mp); DT_PUTPAGE(mp);
jERROR(1, ("jfs_readdir: kmalloc failed!\n")); jERROR(1, ("jfs_readdir: __get_free_page failed!\n"));
filp->f_pos = DIREND; filp->f_pos = DIREND;
return 0; return -ENOMEM;
} }
while (1) { while (1) {
jfs_dirent = (struct jfs_dirent *) dirent_buf;
jfs_dirents = 0;
overflow = 0;
stbl = DT_GETSTBL(p); stbl = DT_GETSTBL(p);
for (i = index; i < p->header.nextindex; i++) { for (i = index; i < p->header.nextindex; i++) {
d = (struct ldtentry *) & p->slot[stbl[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; d_namleft = d->namlen;
name_ptr = d_name; name_ptr = jfs_dirent->name;
jfs_dirent->ino = le32_to_cpu(d->inumber);
if (do_index) { if (do_index) {
filp->f_pos = le32_to_cpu(d->index); jfs_dirent->position = le32_to_cpu(d->index);
len = min(d_namleft, DTLHDRDATALEN); len = min(d_namleft, DTLHDRDATALEN);
} else } else {
jfs_dirent->position = dtpos;
len = min(d_namleft, DTLHDRDATALEN_LEGACY); len = min(d_namleft, DTLHDRDATALEN_LEGACY);
}
/* copy the name of head/only segment */ /* copy the name of head/only segment */
outlen = jfs_strfromUCS_le(name_ptr, d->name, len, outlen = jfs_strfromUCS_le(name_ptr, d->name, len,
codepage); codepage);
d_namlen = outlen; jfs_dirent->name_len = outlen;
/* copy name in the additional segment(s) */ /* copy name in the additional segment(s) */
next = d->next; next = d->next;
...@@ -3053,56 +3095,60 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir) ...@@ -3053,56 +3095,60 @@ int jfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
len = min(d_namleft, DTSLOTDATALEN); len = min(d_namleft, DTSLOTDATALEN);
outlen = jfs_strfromUCS_le(name_ptr, t->name, outlen = jfs_strfromUCS_le(name_ptr, t->name,
len, codepage); len, codepage);
d_namlen+= outlen; jfs_dirent->name_len += outlen;
next = t->next; next = t->next;
} }
if (filldir(dirent, d_name, d_namlen, filp->f_pos, jfs_dirents++;
le32_to_cpu(d->inumber), DT_UNKNOWN)) jfs_dirent = next_jfs_dirent(jfs_dirent);
goto out;
skip_one: skip_one:
if (!do_index) if (!do_index)
dtoffset->index++; dtoffset->index++;
} }
/* if (!overflow) {
* get next leaf page /* 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) { /* unpin previous leaf page */
filp->f_pos = DIREND; DT_PUTPAGE(mp);
break;
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 (!overflow && (bn == 0)) {
if (bn == 0) {
filp->f_pos = DIREND; filp->f_pos = DIREND;
break; break;
} }
/* unpin previous leaf page */
DT_PUTPAGE(mp);
/* get next leaf page */
DT_GETPAGE(ip, bn, mp, PSIZE, p, rc); DT_GETPAGE(ip, bn, mp, PSIZE, p, rc);
if (rc) { if (rc) {
kfree(d_name); free_page(dirent_buf);
return -rc; return -rc;
} }
/* update offset (pn:index) for new page */
index = 0;
if (!do_index) {
dtoffset->pn++;
dtoffset->index = 0;
}
} }
out: out:
kfree(d_name); free_page(dirent_buf);
DT_PUTPAGE(mp);
return rc; return rc;
} }
......
...@@ -365,11 +365,7 @@ int diRead(struct inode *ip) ...@@ -365,11 +365,7 @@ int diRead(struct inode *ip)
if ((lengthPXD(&iagp->inoext[extno]) != imap->im_nbperiext) || if ((lengthPXD(&iagp->inoext[extno]) != imap->im_nbperiext) ||
(addressPXD(&iagp->inoext[extno]) == 0)) { (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); release_metapage(mp);
updateSuper(ip->i_sb, FM_DIRTY);
return ESTALE; return ESTALE;
} }
...@@ -416,12 +412,9 @@ int diRead(struct inode *ip) ...@@ -416,12 +412,9 @@ int diRead(struct inode *ip)
jERROR(1, ("diRead: i_ino != di_number\n")); jERROR(1, ("diRead: i_ino != di_number\n"));
updateSuper(ip->i_sb, FM_DIRTY); updateSuper(ip->i_sb, FM_DIRTY);
rc = EIO; rc = EIO;
} else if (le32_to_cpu(dp->di_nlink) == 0) { } 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);
rc = ESTALE; rc = ESTALE;
} else else
/* copy the disk inode to the in-memory inode */ /* copy the disk inode to the in-memory inode */
rc = copy_from_dinode(dp, ip); rc = copy_from_dinode(dp, ip);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment