From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id ; Thu, 22 Feb 2001 09:49:28 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id ; Thu, 22 Feb 2001 09:49:17 -0500 Received: from mons.uio.no ([129.240.130.14]:16545 "EHLO mons.uio.no") by vger.kernel.org with ESMTP id ; Thu, 22 Feb 2001 09:49:05 -0500 Message-ID: <14997.9938.106305.635202@charged.uio.no> Date: Thu, 22 Feb 2001 15:48:50 +0100 (CET) To: NFS maillist Cc: Linux Kernel Subject: Updated patch for the [2.4.x] NFS 'missing directory entry a.k.a. IRIX server' problem... X-Mailer: VM 6.75 under 21.1 (patch 14) "Cuyahoga Valley" XEmacs Lucid Reply-To: trond.myklebust@fys.uio.no From: Trond Myklebust Mime-Version: 1.0 (generated by tm-edit 1.7) Content-Type: text/plain; charset=US-ASCII Sender: linux-kernel-owner@vger.kernel.org X-Mailing-List: linux-kernel@vger.kernel.org Hi, After having tried to thrash out what exactly is the kernel interface for telldir/seekdir w.r.t. the existence of negative offsets with the glibc people, I've finally found a way to work within the current scheme. The problem at hand is that we currently would like to support the existence of directory cookies that take unsigned 32-bit values in NFSv2, unsigned 64-bit values in NFSv3. Given that most NFSv3 servers can/do use 32-bit cookies for compatibility with 32-bit systems, we would like to be able to pass this type of cookie back up to userland for use by the 32-bit interface. However the interface chosen both in glibc and partly in the kernel itself assumes all cookies are 32-bit signed values. Thus you have to find a way to cope with the kernel and glibc sign extending (almost) all cookies which have bit 31 set. The following patch therefore does 3 things: - Patch linux/fs/readdir.c so that file->f_pos is copied into the dirent64 structure with sign extension. This is for consistency with the behaviour of filldir64. - Patch NFSv2 xdr routines so that 32-bit cookies get extended to take 64-bit signed values (as opposed to unsigned values) for consistency with the fact that (l|)off_t are both signed. - Patch NFSv3 xdr routines with a hack that mimics sign extension on those cookies which are truly 32-bit unsigned. To do this we use the transformation (cookie & 0x80000000) ? cookie ^ 0xFFFFFFFF00000000 : cookie; Note that this a transformation has no effect on true 64-bit cookies because it is reversible. - Make a special version of 'lseek()' for NFS directories that returns 0 if the offset used was negative (rather than returning the offset itself). This avoids userland misinterpreting the return value as an error. The above fixes should ensure that all cookies taking values between 0 and (2^32-1) on the NFS server are preserved through the 32-bit VFS interface, and are accepted by glibc as valid entries. It should also work fine with existing 64-bit architectures. Please could people give this a try, and report if it fixes the problems. Cheers, Trond diff -u --recursive --new-file linux-2.4.2-fh_align/fs/nfs/dir.c linux-2.4.2-dir/fs/nfs/dir.c --- linux-2.4.2-fh_align/fs/nfs/dir.c Fri Feb 9 20:29:44 2001 +++ linux-2.4.2-dir/fs/nfs/dir.c Thu Feb 22 12:34:41 2001 @@ -34,6 +34,7 @@ #define NFS_PARANOIA 1 /* #define NFS_DEBUG_VERBOSE 1 */ +static loff_t nfs_dir_llseek(struct file *, loff_t, int); static int nfs_readdir(struct file *, void *, filldir_t); static struct dentry *nfs_lookup(struct inode *, struct dentry *); static int nfs_create(struct inode *, struct dentry *, int); @@ -47,6 +48,7 @@ struct inode *, struct dentry *); struct file_operations nfs_dir_operations = { + llseek: nfs_dir_llseek, read: generic_read_dir, readdir: nfs_readdir, open: nfs_open, @@ -67,6 +69,25 @@ revalidate: nfs_revalidate, setattr: nfs_notify_change, }; + +static loff_t nfs_dir_llseek(struct file *file, loff_t offset, int origin) +{ + switch (origin) { + case 1: + if (offset == 0) { + offset = file->f_pos; + break; + } + case 2: + return -EINVAL; + } + if (offset != file->f_pos) { + file->f_pos = offset; + file->f_reada = 0; + file->f_version = ++event; + } + return (offset <= 0) ? 0 : offset; +} typedef u32 * (*decode_dirent_t)(u32 *, struct nfs_entry *, int); typedef struct { diff -u --recursive --new-file linux-2.4.2-fh_align/fs/nfs/nfs2xdr.c linux-2.4.2-dir/fs/nfs/nfs2xdr.c --- linux-2.4.2-fh_align/fs/nfs/nfs2xdr.c Fri Feb 9 20:29:44 2001 +++ linux-2.4.2-dir/fs/nfs/nfs2xdr.c Thu Feb 22 10:47:49 2001 @@ -419,7 +419,7 @@ bufsiz = bufsiz >> 2; p = xdr_encode_fhandle(p, args->fh); - *p++ = htonl(args->cookie); + *p++ = htonl(args->cookie & 0xFFFFFFFF); *p++ = htonl(bufsiz); /* see above */ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); @@ -504,7 +504,7 @@ entry->name = (const char *) p; p += XDR_QUADLEN(entry->len); entry->prev_cookie = entry->cookie; - entry->cookie = ntohl(*p++); + entry->cookie = (s64)((off_t)ntohl(*p++)); entry->eof = !p[0] && p[1]; return p; diff -u --recursive --new-file linux-2.4.2-fh_align/fs/nfs/nfs3xdr.c linux-2.4.2-dir/fs/nfs/nfs3xdr.c --- linux-2.4.2-fh_align/fs/nfs/nfs3xdr.c Fri Feb 9 20:29:44 2001 +++ linux-2.4.2-dir/fs/nfs/nfs3xdr.c Thu Feb 22 10:47:49 2001 @@ -523,6 +523,13 @@ return 0; } +/* Hack to sign-extending 32-bit cookies */ +static inline +u64 nfs_transform_cookie64(u64 cookie) +{ + return (cookie & 0x80000000) ? (cookie ^ 0xFFFFFFFF00000000) : cookie; +} + /* * Encode arguments to readdir call */ @@ -533,7 +540,7 @@ int buflen, replen; p = xdr_encode_fhandle(p, args->fh); - p = xdr_encode_hyper(p, args->cookie); + p = xdr_encode_hyper(p, nfs_transform_cookie64(args->cookie)); *p++ = args->verf[0]; *p++ = args->verf[1]; if (args->plus) { @@ -635,6 +642,7 @@ nfs3_decode_dirent(u32 *p, struct nfs_entry *entry, int plus) { struct nfs_entry old = *entry; + u64 cookie; if (!*p++) { if (!*p) @@ -648,7 +656,8 @@ entry->name = (const char *) p; p += XDR_QUADLEN(entry->len); entry->prev_cookie = entry->cookie; - p = xdr_decode_hyper(p, &entry->cookie); + p = xdr_decode_hyper(p, cookie); + entry->cookie = nfs_transform_cookie64(cookie); if (plus) { p = xdr_decode_post_op_attr(p, &entry->fattr); diff -u --recursive --new-file linux-2.4.2-fh_align/fs/readdir.c linux-2.4.2-dir/fs/readdir.c --- linux-2.4.2-fh_align/fs/readdir.c Mon Dec 11 22:45:42 2000 +++ linux-2.4.2-dir/fs/readdir.c Thu Feb 22 10:47:49 2001 @@ -315,7 +315,8 @@ lastdirent = buf.previous; if (lastdirent) { struct linux_dirent64 d; - d.d_off = file->f_pos; + /* get the sign extension right */ + d.d_off = (off_t)file->f_pos; copy_to_user(&lastdirent->d_off, &d.d_off, sizeof(d.d_off)); error = count - buf.count; }