* [RFC 1/5] vfs, support pathconf(3) with _PC_LINK_MAX
2009-12-06 7:58 [RFC 0/5] pathconf(3) with _PC_LINK_MAX J. R. Okajima
@ 2009-12-06 7:58 ` J. R. Okajima
2009-12-06 7:59 ` [RFC 2/5] ext2, " J. R. Okajima
` (4 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: J. R. Okajima @ 2009-12-06 7:58 UTC (permalink / raw)
To: linux-kernel; +Cc: stewb, J. R. Okajima
The pathconf(_PC_LINK_MAX) cannot get the correct value, since linux
kernel doesn't provide such interface. And the current implementation in
GLibc issues statfs(2) first and then returns the predefined value
(EXT2_LINK_MAX, etc) based upoin the filesystem type. But GLibc doesn't
support all filesystem types. ie. when the target filesystem is unknown
to pathconf(3), it will return LINUX_LINK_MAX (127).
For GLibc, there is no way except implementing this poor method.
This patch makes statfs(2) return the correct value via struct
statfs.f_spare[0].
Signed-off-by: J. R. Okajima <hooanon05@yahoo.co.jp>
---
fs/compat.c | 5 +++--
fs/libfs.c | 1 +
fs/open.c | 9 +++++++--
include/linux/statfs.h | 6 ++++++
4 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/fs/compat.c b/fs/compat.c
index 6c19040..fd1d96b 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -246,7 +246,7 @@ static int put_compat_statfs(struct compat_statfs __user *ubuf, struct kstatfs *
__put_user(kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) ||
__put_user(kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]) ||
__put_user(kbuf->f_frsize, &ubuf->f_frsize) ||
- __put_user(0, &ubuf->f_spare[0]) ||
+ __put_user(kbuf->f_linkmax, &ubuf->f_linkmax) || /* f_spare[0] */
__put_user(0, &ubuf->f_spare[1]) ||
__put_user(0, &ubuf->f_spare[2]) ||
__put_user(0, &ubuf->f_spare[3]) ||
@@ -319,7 +319,8 @@ static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstat
__put_user(kbuf->f_namelen, &ubuf->f_namelen) ||
__put_user(kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]) ||
__put_user(kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]) ||
- __put_user(kbuf->f_frsize, &ubuf->f_frsize))
+ __put_user(kbuf->f_frsize, &ubuf->f_frsize) ||
+ __put_user(kbuf->f_linkmax, &ubuf->f_linkmax)) /* f_spare[0] */
return -EFAULT;
return 0;
}
diff --git a/fs/libfs.c b/fs/libfs.c
index 219576c..1a078dd 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -28,6 +28,7 @@ int simple_statfs(struct dentry *dentry, struct kstatfs *buf)
buf->f_type = dentry->d_sb->s_magic;
buf->f_bsize = PAGE_CACHE_SIZE;
buf->f_namelen = NAME_MAX;
+ buf->f_linkmax = LINK_MAX_UNLIMITED; /* cf. simple_link() */
return 0;
}
diff --git a/fs/open.c b/fs/open.c
index 4f01e06..4ca513f 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -39,6 +39,7 @@ int vfs_statfs(struct dentry *dentry, struct kstatfs *buf)
retval = -ENOSYS;
if (dentry->d_sb->s_op->statfs) {
memset(buf, 0, sizeof(*buf));
+ buf->f_linkmax = LINK_MAX_DEFAULT;
retval = security_sb_statfs(dentry);
if (retval)
return retval;
@@ -91,7 +92,9 @@ static int vfs_statfs_native(struct dentry *dentry, struct statfs *buf)
buf->f_fsid = st.f_fsid;
buf->f_namelen = st.f_namelen;
buf->f_frsize = st.f_frsize;
- memset(buf->f_spare, 0, sizeof(buf->f_spare));
+ /* contain f_linkmax */
+ BUILD_BUG_ON(sizeof(buf->f_spare) < sizeof(st.f_spare));
+ memcpy(buf->f_spare, st.f_spare, sizeof(st.f_spare));
}
return 0;
}
@@ -118,7 +121,9 @@ static int vfs_statfs64(struct dentry *dentry, struct statfs64 *buf)
buf->f_fsid = st.f_fsid;
buf->f_namelen = st.f_namelen;
buf->f_frsize = st.f_frsize;
- memset(buf->f_spare, 0, sizeof(buf->f_spare));
+ /* contain f_linkmax */
+ BUILD_BUG_ON(sizeof(buf->f_spare) < sizeof(st.f_spare));
+ memcpy(buf->f_spare, st.f_spare, sizeof(st.f_spare));
}
return 0;
}
diff --git a/include/linux/statfs.h b/include/linux/statfs.h
index b34cc82..958b837 100644
--- a/include/linux/statfs.h
+++ b/include/linux/statfs.h
@@ -19,4 +19,10 @@ struct kstatfs {
long f_spare[5];
};
+/* support pathconf(3) with _PC_LINK_MAX */
+#define f_linkmax f_spare[0]
+#define LINK_MAX_UNLIMITED (-1) /* link(2) does not check i_nlink */
+#define LINK_MAX_UNSUPPORTED 1 /* hardlink is unavailable */
+#define LINK_MAX_DEFAULT 127 /* compatibility to glibc */
+
#endif
--
1.6.1.284.g5dc13
^ permalink raw reply related [flat|nested] 9+ messages in thread* [RFC 2/5] ext2, support pathconf(3) with _PC_LINK_MAX
2009-12-06 7:58 [RFC 0/5] pathconf(3) with _PC_LINK_MAX J. R. Okajima
2009-12-06 7:58 ` [RFC 1/5] vfs, support " J. R. Okajima
@ 2009-12-06 7:59 ` J. R. Okajima
2009-12-06 7:59 ` [RFC 3/5] ext3, " J. R. Okajima
` (3 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: J. R. Okajima @ 2009-12-06 7:59 UTC (permalink / raw)
To: linux-kernel; +Cc: stewb, J. R. Okajima
Return the value via struct statfs.f_spare[0].
Signed-off-by: J. R. Okajima <hooanon05@yahoo.co.jp>
---
fs/ext2/super.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/fs/ext2/super.c b/fs/ext2/super.c
index 1a9ffee..720c5c1 100644
--- a/fs/ext2/super.c
+++ b/fs/ext2/super.c
@@ -1304,6 +1304,7 @@ static int ext2_statfs (struct dentry * dentry, struct kstatfs * buf)
le64_to_cpup((void *)es->s_uuid + sizeof(u64));
buf->f_fsid.val[0] = fsid & 0xFFFFFFFFUL;
buf->f_fsid.val[1] = (fsid >> 32) & 0xFFFFFFFFUL;
+ buf->f_linkmax = EXT2_LINK_MAX;
return 0;
}
--
1.6.1.284.g5dc13
^ permalink raw reply related [flat|nested] 9+ messages in thread* [RFC 3/5] ext3, support pathconf(3) with _PC_LINK_MAX
2009-12-06 7:58 [RFC 0/5] pathconf(3) with _PC_LINK_MAX J. R. Okajima
2009-12-06 7:58 ` [RFC 1/5] vfs, support " J. R. Okajima
2009-12-06 7:59 ` [RFC 2/5] ext2, " J. R. Okajima
@ 2009-12-06 7:59 ` J. R. Okajima
2009-12-06 7:59 ` [RFC 4/5] nfs, " J. R. Okajima
` (2 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: J. R. Okajima @ 2009-12-06 7:59 UTC (permalink / raw)
To: linux-kernel; +Cc: stewb, J. R. Okajima
Return the value via struct statfs.f_spare[0].
Signed-off-by: J. R. Okajima <hooanon05@yahoo.co.jp>
---
fs/ext3/super.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/fs/ext3/super.c b/fs/ext3/super.c
index 427496c..19cdc14 100644
--- a/fs/ext3/super.c
+++ b/fs/ext3/super.c
@@ -2698,6 +2698,7 @@ static int ext3_statfs (struct dentry * dentry, struct kstatfs * buf)
le64_to_cpup((void *)es->s_uuid + sizeof(u64));
buf->f_fsid.val[0] = fsid & 0xFFFFFFFFUL;
buf->f_fsid.val[1] = (fsid >> 32) & 0xFFFFFFFFUL;
+ buf->f_linkmax = EXT3_LINK_MAX;
return 0;
}
--
1.6.1.284.g5dc13
^ permalink raw reply related [flat|nested] 9+ messages in thread* [RFC 4/5] nfs, support pathconf(3) with _PC_LINK_MAX
2009-12-06 7:58 [RFC 0/5] pathconf(3) with _PC_LINK_MAX J. R. Okajima
` (2 preceding siblings ...)
2009-12-06 7:59 ` [RFC 3/5] ext3, " J. R. Okajima
@ 2009-12-06 7:59 ` J. R. Okajima
2009-12-06 7:59 ` [RFC 5/5] tmpfs, " J. R. Okajima
2009-12-06 8:39 ` [RFC 0/5] " Al Viro
5 siblings, 0 replies; 9+ messages in thread
From: J. R. Okajima @ 2009-12-06 7:59 UTC (permalink / raw)
To: linux-kernel; +Cc: stewb, J. R. Okajima
Return the value via struct statfs.f_spare[0], based upon PATHCONF
procedure. As namemax, it is set to a new member in struct nfs_server,
named linkmax.
Since NFSv2 (v4 too?) doesn't have PATHCONF procedure, it will be set to
LINK_MAX_DEFAULT which is a default value of GLibc implementation.
Signed-off-by: J. R. Okajima <hooanon05@yahoo.co.jp>
---
fs/nfs/client.c | 10 +++++++---
fs/nfs/super.c | 1 +
include/linux/nfs_fs_sb.h | 1 +
3 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 99ea196..ed3fddd 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -851,6 +851,7 @@ static int nfs_init_server(struct nfs_server *server,
server->mountd_protocol = data->mount_server.protocol;
server->namelen = data->namlen;
+ server->linkmax = LINK_MAX_DEFAULT;
/* Create a client RPC handle for the NFSv3 ACL management interface */
nfs_init_server_aclclient(server);
dprintk("<-- nfs_init_server() = 0 [new %p]\n", clp);
@@ -941,14 +942,17 @@ static int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, str
nfs_server_set_fsinfo(server, &fsinfo);
/* Get some general file system info */
- if (server->namelen == 0) {
+ {
struct nfs_pathconf pathinfo;
pathinfo.fattr = fattr;
nfs_fattr_init(fattr);
- if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0)
- server->namelen = pathinfo.max_namelen;
+ if (clp->rpc_ops->pathconf(server, mntfh, &pathinfo) >= 0) {
+ if (server->namelen == 0)
+ server->namelen = pathinfo.max_namelen;
+ server->linkmax = pathinfo.max_link;
+ }
}
dprintk("<-- nfs_probe_fsinfo() = 0\n");
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 90be551..ae0599c 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -453,6 +453,7 @@ static int nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
buf->f_ffree = res.afiles;
buf->f_namelen = server->namelen;
+ buf->f_linkmax = server->linkmax;
return 0;
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index 320569e..d66ab43 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -123,6 +123,7 @@ struct nfs_server {
unsigned int acdirmin;
unsigned int acdirmax;
unsigned int namelen;
+ unsigned int linkmax;
unsigned int options; /* extra options enabled by mount */
#define NFS_OPTION_FSCACHE 0x00000001 /* - local caching enabled */
--
1.6.1.284.g5dc13
^ permalink raw reply related [flat|nested] 9+ messages in thread* [RFC 5/5] tmpfs, support pathconf(3) with _PC_LINK_MAX
2009-12-06 7:58 [RFC 0/5] pathconf(3) with _PC_LINK_MAX J. R. Okajima
` (3 preceding siblings ...)
2009-12-06 7:59 ` [RFC 4/5] nfs, " J. R. Okajima
@ 2009-12-06 7:59 ` J. R. Okajima
2009-12-06 8:39 ` [RFC 0/5] " Al Viro
5 siblings, 0 replies; 9+ messages in thread
From: J. R. Okajima @ 2009-12-06 7:59 UTC (permalink / raw)
To: linux-kernel; +Cc: stewb, J. R. Okajima
Return the value via struct statfs.f_spare[0].
Actually tmpfs doesn't check the link count in link(2) and it can be
wrap around. Set LINK_MAX_UNLIMITED.
Signed-off-by: J. R. Okajima <hooanon05@yahoo.co.jp>
---
mm/shmem.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/mm/shmem.c b/mm/shmem.c
index 356dd99..78f7e21 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1784,6 +1784,7 @@ static int shmem_statfs(struct dentry *dentry, struct kstatfs *buf)
buf->f_type = TMPFS_MAGIC;
buf->f_bsize = PAGE_CACHE_SIZE;
buf->f_namelen = NAME_MAX;
+ buf->f_linkmax = LINK_MAX_UNLIMITED;
spin_lock(&sbinfo->stat_lock);
if (sbinfo->max_blocks) {
buf->f_blocks = sbinfo->max_blocks;
--
1.6.1.284.g5dc13
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [RFC 0/5] pathconf(3) with _PC_LINK_MAX
2009-12-06 7:58 [RFC 0/5] pathconf(3) with _PC_LINK_MAX J. R. Okajima
` (4 preceding siblings ...)
2009-12-06 7:59 ` [RFC 5/5] tmpfs, " J. R. Okajima
@ 2009-12-06 8:39 ` Al Viro
2009-12-06 9:09 ` hooanon05
2009-12-06 21:39 ` tytso
5 siblings, 2 replies; 9+ messages in thread
From: Al Viro @ 2009-12-06 8:39 UTC (permalink / raw)
To: J. R. Okajima; +Cc: linux-kernel, stewb
On Sun, Dec 06, 2009 at 04:58:58PM +0900, J. R. Okajima wrote:
> The pathconf(_PC_LINK_MAX) cannot get the correct value, since linux
> kernel doesn't provide such interface. And the current implementation in
> GLibc issues statfs(2) first and then returns the predefined value
> (EXT2_LINK_MAX, etc) based upoin the filesystem type. But GLibc doesn't
> support all filesystem types. ie. when the target filesystem is unknown
> to pathconf(3), it will return LINUX_LINK_MAX (127).
> For GLibc, there is no way except implementing this poor method.
>
> This patch makes statfs(2) return the correct value via struct
> statfs.f_spare[0].
Um... Why do we need that, again? Note that there is no way whatsoever
for predicting whether link(2) will fail due to having too many existing
links before you attempt the call - links can be created or removed between
stat(2) and link(2). So any uses of that value are heuristical.
Can you actually show any use cases of that thing? Preferably - in existing
code, but even a theoretical one would be interesting.
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [RFC 0/5] pathconf(3) with _PC_LINK_MAX
2009-12-06 8:39 ` [RFC 0/5] " Al Viro
@ 2009-12-06 9:09 ` hooanon05
2009-12-06 21:39 ` tytso
1 sibling, 0 replies; 9+ messages in thread
From: hooanon05 @ 2009-12-06 9:09 UTC (permalink / raw)
To: Al Viro; +Cc: linux-kernel, stewb
Al Viro:
> Um... Why do we need that, again? Note that there is no way whatsoever
> for predicting whether link(2) will fail due to having too many existing
> links before you attempt the call - links can be created or removed between
> stat(2) and link(2). So any uses of that value are heuristical.
>
> Can you actually show any use cases of that thing? Preferably - in existing
> code, but even a theoretical one would be interesting.
Thanx for quick reply.
To be honest, I am unsure how important this is in real world.
But I've met (reported, precisly) such test program. It seems to come
from old X/Open, though the actual reporter tried the LSB (Linux
Standard Base) runtime-test.
You can get the source code from
ftp.freestandards.org/pub/lsb/test_suites/released-3.2/source/runtime/lsb-test-core-3.2.0-2.src.rpm
+ lsb-test-core-3.2.0.tar.gz
+ lts_vsx-pcts-3.2.0.tgz
+ tset/POSIX.os/files/link/link.c
Here I quote just a part from tset/POSIX.os/files/link/link.c,
The function tblink() behaves as a wrapper for link(2) with the error
checking.
When the filesystem is unknown to pathconf(_PC_LINK_MAX), 'link_max' may
be incorrect and LSB cannot pass this test.
test15()
{
creat(t15a_file, MODEANY);
link_max = pathconf(t15a_file, _PC_LINK_MAX);
/* create lesser of LINK_MAX and PCTS_LINK_MAX links successfully */
/* i.e. make (testmax-1) link() calls, as there is 1 link already */
testmax = link_max;
for (i = 0; i < testmax-1; i++)
{
(void) sprintf(links[i], "L%ld", i+1);
if (tblink(t15a_file, links[i], SUCCEED, NOERROR) != SUCCEED)
break;
}
/* if LINK_MAX is testable, next link gives EMLINK */
(void) tblink(t15a_file, t15b_file, SYSERROR, EMLINK);
}
J. R. Okajima
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [RFC 0/5] pathconf(3) with _PC_LINK_MAX
2009-12-06 8:39 ` [RFC 0/5] " Al Viro
2009-12-06 9:09 ` hooanon05
@ 2009-12-06 21:39 ` tytso
1 sibling, 0 replies; 9+ messages in thread
From: tytso @ 2009-12-06 21:39 UTC (permalink / raw)
To: Al Viro; +Cc: J. R. Okajima, linux-kernel, stewb
On Sun, Dec 06, 2009 at 08:39:58AM +0000, Al Viro wrote:
>
> Um... Why do we need that, again? Note that there is no way whatsoever
> for predicting whether link(2) will fail due to having too many existing
> links before you attempt the call - links can be created or removed between
> stat(2) and link(2). So any uses of that value are heuristical.
>
> Can you actually show any use cases of that thing? Preferably - in existing
> code, but even a theoretical one would be interesting.
I think it's mainly a "if we're going to implement a POSIX interface,
it would be nice if it returned something based on reality instead of
a wild-assed guess". :-)
The "real life" use case I could think of is that backup programs that
use hard links everywhere would be able to determine ahead of time in
advance when it might need to create a new file instead of using a
hard link, without needing to do the link and getting the EMLINK
error. I agree that the only way you can know for sure is by actually
trying the link, so it's a pretty feeble use case.
I will note that without a functional, ext3 and ext4 (or ext3
filesystem with dir_nlink file system feature mounted with ext4) file
systems would be indistinguishable.
- Ted
^ permalink raw reply [flat|nested] 9+ messages in thread