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)
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;
}
......
......@@ -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);
......
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