lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <m1linawza5.fsf_-_@fess.ebiederm.org>
Date:	Thu, 08 Mar 2012 13:36:34 -0800
From:	ebiederm@...ssion.com (Eric W. Biederman)
To:	Greg Kroah-Hartman <gregkh@...uxfoundation.org>
Cc:	Jiri Slaby <jslaby@...e.cz>,
	Linus Torvalds <torvalds@...ux-foundation.org>,
	Jiri Slaby <jirislaby@...il.com>,
	Alan Cox <alan@...rguk.ukuu.org.uk>,
	LKML <linux-kernel@...r.kernel.org>,
	Al Viro <viro@...iv.linux.org.uk>,
	Maciej Rutecki <maciej.rutecki@...il.com>
Subject: [PATCH 2/3] sysfs: Maintain usable nlink directory counts.


When it is easy keep fully accurate nlink counts for sysfs directories,
when it is not easy set nlink to 1.  This maintains as much compatibility
with unix programs that expect directories to have a usable nlink count
as possible, without trying to do the impossible.

Directory nlink count overflows in sysfs are inevitable by design
so only bother to use a byte to store the directory nlink count.
A directory with 254 entries is larger than any sysfs directory
in a normal configruation but small enough we should see tools that
care about large sysfs directories should experience nlink == 1
during their testing.

This fixes libsensors and possibly other applications that get
confused if all sysfs directories return nlink == 1.  The lm_sensors
code that got confused was just wrong is fixed in the lm_sensors
trunk and a fixed version should be released sometime soon.

The nlink of all deleted directories is set to 0.  Returning for the
first time a correct nlink count for deleted sysfs directories.

Once a directory nlink count drops below 2 refuse to increment or decrement
it as there is not enough information available.  It is important that
this works for nlink == 0 as well as nlink == 1 because currently
sysfs supports deleting non-empty directories (the PCI layer requires this behavior).

For tagged directories set the nlink count == 1 because we currently
have one sysfs_dirent and multiple logical sysfs directories making
pre computing nlink impossible.

Signed-off-by: Eric W. Biederman <ebiederm@...ssion.com>
---
 fs/sysfs/dir.c   |   14 ++++++++++++++
 fs/sysfs/inode.c |    1 +
 fs/sysfs/sysfs.h |   16 ++++++++++++++++
 3 files changed, 31 insertions(+), 0 deletions(-)

diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index dd3779c..1526567 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -91,6 +91,9 @@ static int sysfs_link_sibling(struct sysfs_dirent *sd)
 	struct rb_node **node = &sd->s_parent->s_dir.children.rb_node;
 	struct rb_node *parent = NULL;
 
+	if (sysfs_type(sd) == SYSFS_DIR)
+		sysfs_inc_nlink(sd->s_parent);
+
 	while (*node) {
 		struct sysfs_dirent *pos;
 		int result;
@@ -123,6 +126,9 @@ static int sysfs_link_sibling(struct sysfs_dirent *sd)
  */
 static void sysfs_unlink_sibling(struct sysfs_dirent *sd)
 {
+	if (sysfs_type(sd) == SYSFS_DIR)
+		sysfs_dec_nlink(sd->s_parent);
+
 	rb_erase(&sd->s_rb, &sd->s_parent->s_dir.children);
 }
 
@@ -366,6 +372,9 @@ struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type)
 	sd->s_name = name;
 	sd->s_mode = mode;
 	sd->s_flags = type;
+	sd->s_nlink = 1;
+	if (sysfs_type(sd) == SYSFS_DIR)
+		sd->s_nlink = 2;
 
 	return sd;
 
@@ -536,6 +545,7 @@ void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
 		ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME;
 	}
 
+	sd->s_nlink = 0;
 	sd->s_flags |= SYSFS_FLAG_REMOVED;
 	sd->u.removed_list = acxt->removed;
 	acxt->removed = sd;
@@ -660,6 +670,10 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,
 	sd->s_ns = ns;
 	sd->s_dir.kobj = kobj;
 
+	/* Accurate nlink count impossible (one field mutiple dirs) */
+	if (sysfs_ns_type(sd))
+		sd->s_nlink = 1;
+
 	/* link in */
 	sysfs_addrm_start(&acxt, parent_sd);
 	rc = sysfs_add_one(&acxt, sd);
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c
index 4291fd1..f6ebda8 100644
--- a/fs/sysfs/inode.c
+++ b/fs/sysfs/inode.c
@@ -216,6 +216,7 @@ static void sysfs_refresh_inode(struct sysfs_dirent *sd, struct inode *inode)
 					    iattrs->ia_secdata,
 					    iattrs->ia_secdata_len);
 	}
+	set_nlink(inode, sd->s_nlink);
 }
 
 int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
index c76c932..71f9bf7 100644
--- a/fs/sysfs/sysfs.h
+++ b/fs/sysfs/sysfs.h
@@ -76,6 +76,7 @@ struct sysfs_dirent {
 		struct sysfs_elem_bin_attr	s_bin_attr;
 	};
 
+	unsigned char		s_nlink;
 	unsigned char		s_flags;
 	umode_t 		s_mode;
 	unsigned int		s_ino;
@@ -127,6 +128,21 @@ do {								\
 #define sysfs_dirent_init_lockdep(sd) do {} while(0)
 #endif
 
+static inline void sysfs_inc_nlink(struct sysfs_dirent *sd)
+{
+	if (sd->s_nlink <= 1)
+		return;
+	sd->s_nlink++;
+	if (sd->s_nlink == 0)
+		sd->s_nlink = 1;
+}
+static inline void sysfs_dec_nlink(struct sysfs_dirent *sd)
+{
+	if (sd->s_nlink <= 1)
+		return;
+	sd->s_nlink--;
+}
+
 /*
  * Context structure to be used while adding/removing nodes.
  */
-- 
1.7.2.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ