Added missing SuSE-Xen-Patches.

This commit is contained in:
Ben Schweikert
2009-08-05 10:39:24 +02:00
parent 1e827a986b
commit 8f69975df1
184 changed files with 154619 additions and 0 deletions

View File

@@ -0,0 +1,201 @@
From: Jeff Mahoney <jeffm@suse.com>
Subject: [PATCH] vfs: introduce path_permission()
2.6.27 eliminated the nameidata parameter from permission and replaced
several call sites with inode_permission. This keeps the information
required by AppArmor from reaching it.
The following patch factors out the permission assessment part of
inode_permission into __inode_permission and adds a path_permission
function that takes a struct path instead of a struct inode and passes
it to security_path_permission instead of security_inode_permission.
All of the call sites that had access to a struct path whether by
itself or via a file or nameidata (and used it) in 2.6.26 are changed
to use the path_permission call.
Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
fs/inotify_user.c | 2 +-
fs/namei.c | 32 ++++++++++++++++++++++++--------
fs/open.c | 10 +++++-----
include/linux/fs.h | 5 +++++
4 files changed, 35 insertions(+), 14 deletions(-)
--- a/fs/inotify_user.c
+++ b/fs/inotify_user.c
@@ -372,7 +372,7 @@ static int find_inode(const char __user
if (error)
return error;
/* you can only watch an inode if you have read permissions on it */
- error = inode_permission(path->dentry->d_inode, MAY_READ);
+ error = path_permission(path, MAY_READ);
if (error)
path_put(path);
return error;
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -227,7 +227,7 @@ int generic_permission(struct inode *ino
return -EACCES;
}
-int inode_permission(struct inode *inode, int mask)
+static int __inode_permission(struct inode *inode, int mask)
{
int retval;
int submask = mask;
@@ -273,7 +273,12 @@ int inode_permission(struct inode *inode
if (retval)
return retval;
- retval = devcgroup_inode_permission(inode, mask);
+ return devcgroup_inode_permission(inode, mask);
+}
+
+int inode_permission(struct inode *inode, int mask)
+{
+ int retval = __inode_permission(inode, mask);
if (retval)
return retval;
@@ -281,6 +286,15 @@ int inode_permission(struct inode *inode
mask & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND));
}
+int path_permission(struct path *path, int mask)
+{
+ int retval = __inode_permission(path->dentry->d_inode, mask);
+ if (retval)
+ return retval;
+ return security_path_permission(path,
+ mask & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND));
+}
+
/**
* vfs_permission - check for access rights to a given path
* @nd: lookup result that describes the path
@@ -293,7 +307,7 @@ int inode_permission(struct inode *inode
*/
int vfs_permission(struct nameidata *nd, int mask)
{
- return inode_permission(nd->path.dentry->d_inode, mask);
+ return path_permission(&nd->path, mask);
}
/**
@@ -310,7 +324,7 @@ int vfs_permission(struct nameidata *nd,
*/
int file_permission(struct file *file, int mask)
{
- return inode_permission(file->f_path.dentry->d_inode, mask);
+ return path_permission(&file->f_path, mask);
}
/*
@@ -452,8 +466,9 @@ static struct dentry * cached_lookup(str
* short-cut DAC fails, then call permission() to do more
* complete permission check.
*/
-static int exec_permission_lite(struct inode *inode)
+static int exec_permission_lite(struct path *path)
{
+ struct inode *inode = path->dentry->d_inode;
umode_t mode = inode->i_mode;
if (inode->i_op && inode->i_op->permission)
@@ -478,7 +493,7 @@ static int exec_permission_lite(struct i
return -EACCES;
ok:
- return security_inode_permission(inode, MAY_EXEC);
+ return security_path_permission(path, MAY_EXEC);
}
/*
@@ -875,7 +890,7 @@ static int __link_path_walk(const char *
unsigned int c;
nd->flags |= LOOKUP_CONTINUE;
- err = exec_permission_lite(inode);
+ err = exec_permission_lite(&nd->path);
if (err == -EAGAIN)
err = vfs_permission(nd, MAY_EXEC);
if (err)
@@ -1250,7 +1265,7 @@ static struct dentry *lookup_hash(struct
{
int err;
- err = inode_permission(nd->path.dentry->d_inode, MAY_EXEC);
+ err = path_permission(&nd->path, MAY_EXEC);
if (err)
return ERR_PTR(err);
return __lookup_hash(&nd->last, nd->path.dentry, nd);
@@ -2907,6 +2922,7 @@ EXPORT_SYMBOL(page_symlink_inode_operati
EXPORT_SYMBOL(path_lookup);
EXPORT_SYMBOL(vfs_path_lookup);
EXPORT_SYMBOL(inode_permission);
+EXPORT_SYMBOL(path_permission);
EXPORT_SYMBOL(vfs_permission);
EXPORT_SYMBOL(file_permission);
EXPORT_SYMBOL(unlock_rename);
--- a/fs/open.c
+++ b/fs/open.c
@@ -248,7 +248,7 @@ static long do_sys_truncate(const char _
if (error)
goto dput_and_out;
- error = inode_permission(inode, MAY_WRITE);
+ error = path_permission(&path, MAY_WRITE);
if (error)
goto mnt_drop_write_and_out;
@@ -493,7 +493,7 @@ SYSCALL_DEFINE3(faccessat, int, dfd, con
goto out_path_release;
}
- res = inode_permission(inode, mode | MAY_ACCESS);
+ res = path_permission(&path, mode | MAY_ACCESS);
/* SuS v2 requires we report a read only fs too */
if (res || !(mode & S_IWOTH) || special_file(inode->i_mode))
goto out_path_release;
@@ -536,7 +536,7 @@ SYSCALL_DEFINE1(chdir, const char __user
if (error)
goto out;
- error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS);
+ error = path_permission(&path, MAY_EXEC | MAY_ACCESS);
if (error)
goto dput_and_out;
@@ -565,7 +565,7 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd
if (!S_ISDIR(inode->i_mode))
goto out_putf;
- error = inode_permission(inode, MAY_EXEC | MAY_ACCESS);
+ error = path_permission(&file->f_path, MAY_EXEC | MAY_ACCESS);
if (!error)
set_fs_pwd(current->fs, &file->f_path);
out_putf:
@@ -583,7 +583,7 @@ SYSCALL_DEFINE1(chroot, const char __use
if (error)
goto out;
- error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS);
+ error = path_permission(&path, MAY_EXEC | MAY_ACCESS);
if (error)
goto dput_and_out;
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1201,6 +1201,11 @@ extern void dentry_unhash(struct dentry
extern int file_permission(struct file *, int);
/*
+ * VFS path helper functions.
+ */
+extern int path_permission(struct path *, int);
+
+/*
* File types
*
* NOTE! These match bits 12..15 of stat.st_mode

View File

@@ -0,0 +1,47 @@
From: John Johansen <jjohansen@suse.de>
Subject: AppArmor: Patch AppArmor for 2.6.25 kernel
Add 64 bit capabilities support to AppArmor.
Signed-off-by: John Johansen <jjohansen@suse.de>
---
security/apparmor/module_interface.c | 22 ++++++++++++++++++----
1 file changed, 18 insertions(+), 4 deletions(-)
--- a/security/apparmor/module_interface.c
+++ b/security/apparmor/module_interface.c
@@ -395,15 +395,29 @@ static struct aa_profile *aa_unpack_prof
if (!aa_is_nameX(e, AA_STRUCTEND, NULL))
goto fail;
- if (!aa_is_u32(e, &(profile->capabilities), NULL))
+ if (!aa_is_u32(e, &(profile->capabilities.cap[0]), NULL))
goto fail;
- if (!aa_is_u32(e, &(profile->audit_caps), NULL))
+ if (!aa_is_u32(e, &(profile->audit_caps.cap[0]), NULL))
goto fail;
- if (!aa_is_u32(e, &(profile->quiet_caps), NULL))
+ if (!aa_is_u32(e, &(profile->quiet_caps.cap[0]), NULL))
goto fail;
- if (!aa_is_u32(e, &(profile->set_caps), NULL))
+ if (!aa_is_u32(e, &(profile->set_caps.cap[0]), NULL))
goto fail;
+ if (aa_is_nameX(e, AA_STRUCT, "caps64")) {
+ /* optional upper half of 64 bit caps */
+ if (!aa_is_u32(e, &(profile->capabilities.cap[1]), NULL))
+ goto fail;
+ if (!aa_is_u32(e, &(profile->audit_caps.cap[1]), NULL))
+ goto fail;
+ if (!aa_is_u32(e, &(profile->quiet_caps.cap[1]), NULL))
+ goto fail;
+ if (!aa_is_u32(e, &(profile->set_caps.cap[1]), NULL))
+ goto fail;
+ if (!aa_is_nameX(e, AA_STRUCTEND, NULL))
+ goto fail;
+ }
+
if (!aa_unpack_rlimits(e, profile))
goto fail;

View File

@@ -0,0 +1,72 @@
From: Tony Jones <tonyj@suse.de>
Subject: Export audit subsystem for use by modules
Update kenel audit range comments to show AppArmor's registered range of
1500-1599. This range used to be reserved for LSPP but LSPP uses the
SE Linux range and the range was given to AppArmor.
Adds necessary export symbols for audit subsystem routines.
Changes audit_log_vformat to be externally visible (analagous to vprintf)
Patch is not in mainline -- pending AppArmor code submission to lkml
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
include/linux/audit.h | 12 +++++++++++-
kernel/audit.c | 6 ++++--
2 files changed, 15 insertions(+), 3 deletions(-)
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -33,7 +33,7 @@
* 1200 - 1299 messages internal to the audit daemon
* 1300 - 1399 audit event messages
* 1400 - 1499 SE Linux use
- * 1500 - 1599 kernel LSPP events
+ * 1500 - 1599 AppArmor use
* 1600 - 1699 kernel crypto events
* 1700 - 1799 kernel anomaly records
* 1800 - 1999 future kernel use (maybe integrity labels and related events)
@@ -119,6 +119,13 @@
#define AUDIT_MAC_UNLBL_STCADD 1416 /* NetLabel: add a static label */
#define AUDIT_MAC_UNLBL_STCDEL 1417 /* NetLabel: del a static label */
+#define AUDIT_APPARMOR_AUDIT 1501 /* AppArmor audited grants */
+#define AUDIT_APPARMOR_ALLOWED 1502 /* Allowed Access for learning */
+#define AUDIT_APPARMOR_DENIED 1503
+#define AUDIT_APPARMOR_HINT 1504 /* Process Tracking information */
+#define AUDIT_APPARMOR_STATUS 1505 /* Changes in config */
+#define AUDIT_APPARMOR_ERROR 1506 /* Internal AppArmor Errors */
+
#define AUDIT_FIRST_KERN_ANOM_MSG 1700
#define AUDIT_LAST_KERN_ANOM_MSG 1799
#define AUDIT_ANOM_PROMISCUOUS 1700 /* Device changed promiscuous mode */
@@ -545,6 +552,9 @@ extern void audit_log(struct audit_
__attribute__((format(printf,4,5)));
extern struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type);
+extern void audit_log_vformat(struct audit_buffer *ab,
+ const char *fmt, va_list args)
+ __attribute__((format(printf,2,0)));
extern void audit_log_format(struct audit_buffer *ab,
const char *fmt, ...)
__attribute__((format(printf,2,3)));
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -1231,8 +1231,7 @@ static inline int audit_expand(struct au
* will be called a second time. Currently, we assume that a printk
* can't format message larger than 1024 bytes, so we don't either.
*/
-static void audit_log_vformat(struct audit_buffer *ab, const char *fmt,
- va_list args)
+void audit_log_vformat(struct audit_buffer *ab, const char *fmt, va_list args)
{
int len, avail;
struct sk_buff *skb;
@@ -1506,3 +1505,6 @@ EXPORT_SYMBOL(audit_log_start);
EXPORT_SYMBOL(audit_log_end);
EXPORT_SYMBOL(audit_log_format);
EXPORT_SYMBOL(audit_log);
+EXPORT_SYMBOL_GPL(audit_log_vformat);
+EXPORT_SYMBOL_GPL(audit_log_untrustedstring);
+EXPORT_SYMBOL_GPL(audit_log_d_path);

View File

@@ -0,0 +1,31 @@
From: John Johansen <jjohansen@suse.de>
Subject: Add AppArmor LSM to security/Makefile
Signed-off-by: John Johansen <jjohansen@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
---
security/Kconfig | 1 +
security/Makefile | 3 ++-
2 files changed, 3 insertions(+), 1 deletion(-)
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -117,6 +117,7 @@ config SECURITY_DEFAULT_MMAP_MIN_ADDR
source security/selinux/Kconfig
source security/smack/Kconfig
+source security/apparmor/Kconfig
endmenu
--- a/security/Makefile
+++ b/security/Makefile
@@ -14,5 +14,6 @@ obj-$(CONFIG_SECURITY) += security.o c
# Must precede capability.o in order to stack properly.
obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o
-obj-$(CONFIG_SECURITY_ROOTPLUG) += root_plug.o
+obj-$(CONFIG_SECURITY_APPARMOR) += commoncap.o apparmor/
+ obj-$(CONFIG_SECURITY_ROOTPLUG) += root_plug.o
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o

View File

@@ -0,0 +1,910 @@
From: John Johansen <jjohansen@suse.de>
Subject: AppArmor: Module and LSM hooks
Module parameters, LSM hooks, initialization and teardown.
Signed-off-by: John Johansen <jjohansen@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
---
security/apparmor/lsm.c | 895 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 895 insertions(+)
--- /dev/null
+++ b/security/apparmor/lsm.c
@@ -0,0 +1,895 @@
+/*
+ * Copyright (C) 1998-2007 Novell/SUSE
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * AppArmor LSM interface
+ */
+
+#include <linux/security.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/ctype.h>
+#include <linux/sysctl.h>
+#include <linux/audit.h>
+
+#include "apparmor.h"
+#include "inline.h"
+
+/* Flag indicating whether initialization completed */
+int apparmor_initialized = 0;
+
+static int param_set_aabool(const char *val, struct kernel_param *kp);
+static int param_get_aabool(char *buffer, struct kernel_param *kp);
+#define param_check_aabool(name, p) __param_check(name, p, int)
+
+static int param_set_aauint(const char *val, struct kernel_param *kp);
+static int param_get_aauint(char *buffer, struct kernel_param *kp);
+#define param_check_aauint(name, p) __param_check(name, p, int)
+
+/* Flag values, also controllable via /sys/module/apparmor/parameters
+ * We define special types as we want to do additional mediation.
+ *
+ * Complain mode -- in complain mode access failures result in auditing only
+ * and task is allowed access. audit events are processed by userspace to
+ * generate policy. Default is 'enforce' (0).
+ * Value is also togglable per profile and referenced when global value is
+ * enforce.
+ */
+int apparmor_complain = 0;
+module_param_named(complain, apparmor_complain, aabool, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(apparmor_complain, "Toggle AppArmor complain mode");
+
+/* Debug mode */
+int apparmor_debug = 0;
+module_param_named(debug, apparmor_debug, aabool, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(apparmor_debug, "Toggle AppArmor debug mode");
+
+/* Audit mode */
+int apparmor_audit = 0;
+module_param_named(audit, apparmor_audit, aabool, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(apparmor_audit, "Toggle AppArmor audit mode");
+
+/* Syscall logging mode */
+int apparmor_logsyscall = 0;
+module_param_named(logsyscall, apparmor_logsyscall, aabool, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(apparmor_logsyscall, "Toggle AppArmor logsyscall mode");
+
+/* Maximum pathname length before accesses will start getting rejected */
+unsigned int apparmor_path_max = 2 * PATH_MAX;
+module_param_named(path_max, apparmor_path_max, aauint, S_IRUSR | S_IWUSR);
+MODULE_PARM_DESC(apparmor_path_max, "Maximum pathname length allowed");
+
+/* Boot time disable flag */
+#ifdef CONFIG_SECURITY_APPARMOR_DISABLE
+#define AA_ENABLED_PERMS 0600
+#else
+#define AA_ENABLED_PERMS 0400
+#endif
+static int param_set_aa_enabled(const char *val, struct kernel_param *kp);
+unsigned int apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE;
+module_param_call(enabled, param_set_aa_enabled, param_get_aauint,
+ &apparmor_enabled, AA_ENABLED_PERMS);
+MODULE_PARM_DESC(apparmor_enabled, "Enable/Disable Apparmor on boot");
+
+static int __init apparmor_enabled_setup(char *str)
+{
+ apparmor_enabled = simple_strtol(str, NULL, 0);
+ return 1;
+}
+__setup("apparmor=", apparmor_enabled_setup);
+
+static int param_set_aabool(const char *val, struct kernel_param *kp)
+{
+ if (aa_task_context(current))
+ return -EPERM;
+ return param_set_bool(val, kp);
+}
+
+static int param_get_aabool(char *buffer, struct kernel_param *kp)
+{
+ if (aa_task_context(current))
+ return -EPERM;
+ return param_get_bool(buffer, kp);
+}
+
+static int param_set_aauint(const char *val, struct kernel_param *kp)
+{
+ if (aa_task_context(current))
+ return -EPERM;
+ return param_set_uint(val, kp);
+}
+
+static int param_get_aauint(char *buffer, struct kernel_param *kp)
+{
+ if (aa_task_context(current))
+ return -EPERM;
+ return param_get_uint(buffer, kp);
+}
+
+/* allow run time disabling of apparmor */
+static int param_set_aa_enabled(const char *val, struct kernel_param *kp)
+{
+ char *endp;
+ unsigned long l;
+
+ if (!apparmor_initialized) {
+ apparmor_enabled = 0;
+ return 0;
+ }
+
+ if (aa_task_context(current))
+ return -EPERM;
+
+ if (!apparmor_enabled)
+ return -EINVAL;
+
+ if (!val)
+ return -EINVAL;
+
+ l = simple_strtoul(val, &endp, 0);
+ if (endp == val || l != 0)
+ return -EINVAL;
+
+ apparmor_enabled = 0;
+ apparmor_disable();
+ return 0;
+}
+
+static int aa_reject_syscall(struct task_struct *task, gfp_t flags,
+ const char *name)
+{
+ struct aa_profile *profile = aa_get_profile(task);
+ int error = 0;
+
+ if (profile) {
+ error = aa_audit_syscallreject(profile, flags, name);
+ aa_put_profile(profile);
+ }
+
+ return error;
+}
+
+static int apparmor_ptrace(struct task_struct *parent,
+ struct task_struct *child, unsigned int mode)
+{
+ struct aa_task_context *cxt;
+ int error = 0;
+
+ /*
+ * parent can ptrace child when
+ * - parent is unconfined
+ * - parent & child are in the same namespace &&
+ * - parent is in complain mode
+ * - parent and child are confined by the same profile
+ * - parent profile has CAP_SYS_PTRACE
+ */
+
+ rcu_read_lock();
+ cxt = aa_task_context(parent);
+ if (cxt) {
+ if (parent->nsproxy != child->nsproxy) {
+ struct aa_audit sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = "ptrace";
+ sa.gfp_mask = GFP_ATOMIC;
+ sa.parent = parent->pid;
+ sa.task = child->pid;
+ sa.info = "different namespaces";
+ aa_audit_reject(cxt->profile, &sa);
+ error = -EPERM;
+ } else {
+ struct aa_task_context *child_cxt =
+ aa_task_context(child);
+
+ error = aa_may_ptrace(cxt, child_cxt ?
+ child_cxt->profile : NULL);
+ if (PROFILE_COMPLAIN(cxt->profile)) {
+ struct aa_audit sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = "ptrace";
+ sa.gfp_mask = GFP_ATOMIC;
+ sa.parent = parent->pid;
+ sa.task = child->pid;
+ aa_audit_hint(cxt->profile, &sa);
+ }
+ }
+ }
+ rcu_read_unlock();
+
+ return error;
+}
+
+static int apparmor_capable(struct task_struct *task, int cap)
+{
+ int error;
+ struct aa_task_context *cxt;
+
+ /* cap_capable returns 0 on success, else -EPERM */
+ error = cap_capable(task, cap);
+
+ rcu_read_lock();
+ cxt = aa_task_context(task);
+ if (cxt && (!error || cap_raised(cxt->profile->set_caps, cap)))
+ error = aa_capability(cxt, cap);
+ rcu_read_unlock();
+
+ return error;
+}
+
+static int apparmor_sysctl(struct ctl_table *table, int op)
+{
+ struct aa_profile *profile = aa_get_profile(current);
+ int error = 0;
+
+ if (profile) {
+ char *buffer, *name;
+ int mask;
+
+ mask = 0;
+ if (op & 4)
+ mask |= MAY_READ;
+ if (op & 2)
+ mask |= MAY_WRITE;
+
+ error = -ENOMEM;
+ buffer = (char*)__get_free_page(GFP_KERNEL);
+ if (!buffer)
+ goto out;
+ name = sysctl_pathname(table, buffer, PAGE_SIZE);
+ if (name && name - buffer >= 5) {
+ name -= 5;
+ memcpy(name, "/proc", 5);
+ error = aa_perm_path(profile, "sysctl", name, mask, 0);
+ }
+ free_page((unsigned long)buffer);
+ }
+
+out:
+ aa_put_profile(profile);
+ return error;
+}
+
+static int apparmor_bprm_set_security(struct linux_binprm *bprm)
+{
+ /* handle capability bits with setuid, etc */
+ cap_bprm_set_security(bprm);
+ /* already set based on script name */
+ if (bprm->sh_bang)
+ return 0;
+ return aa_register(bprm);
+}
+
+static int apparmor_bprm_secureexec(struct linux_binprm *bprm)
+{
+ int ret = cap_bprm_secureexec(bprm);
+
+ if (!ret && (unsigned long)bprm->security & AA_SECURE_EXEC_NEEDED) {
+ AA_DEBUG("%s: secureexec required for %s\n",
+ __FUNCTION__, bprm->filename);
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static int apparmor_sb_mount(char *dev_name, struct path *path, char *type,
+ unsigned long flags, void *data)
+{
+ return aa_reject_syscall(current, GFP_KERNEL, "mount");
+}
+
+static int apparmor_umount(struct vfsmount *mnt, int flags)
+{
+ return aa_reject_syscall(current, GFP_KERNEL, "umount");
+}
+
+static int apparmor_inode_mkdir(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, int mask)
+{
+ struct aa_profile *profile;
+ int error = 0;
+
+ if (!mnt || !mediated_filesystem(dir))
+ goto out;
+
+ profile = aa_get_profile(current);
+
+ if (profile)
+ error = aa_perm_dir(profile, "inode_mkdir", dentry, mnt,
+ MAY_WRITE);
+
+ aa_put_profile(profile);
+
+out:
+ return error;
+}
+
+static int apparmor_inode_rmdir(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt)
+{
+ struct aa_profile *profile;
+ int error = 0;
+
+ if (!mnt || !mediated_filesystem(dir))
+ goto out;
+
+ profile = aa_get_profile(current);
+
+ if (profile)
+ error = aa_perm_dir(profile, "inode_rmdir", dentry, mnt,
+ MAY_WRITE);
+
+ aa_put_profile(profile);
+
+out:
+ return error;
+}
+
+static int aa_permission(const char *operation, struct inode *inode,
+ struct dentry *dentry, struct vfsmount *mnt,
+ int mask, int check)
+{
+ int error = 0;
+
+ if (mnt && mediated_filesystem(inode)) {
+ struct aa_profile *profile;
+
+ profile = aa_get_profile(current);
+ if (profile)
+ error = aa_perm(profile, operation, dentry, mnt, mask,
+ check);
+ aa_put_profile(profile);
+ }
+ return error;
+}
+
+static inline int aa_mask_permissions(int mask)
+{
+ if (mask & MAY_APPEND)
+ mask &= (MAY_READ | MAY_APPEND | MAY_EXEC);
+ else
+ mask &= (MAY_READ | MAY_WRITE | MAY_EXEC);
+ return mask;
+}
+
+static int apparmor_inode_create(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, int mask)
+{
+ return aa_permission("inode_create", dir, dentry, mnt, MAY_APPEND, 0);
+}
+
+static int apparmor_inode_link(struct dentry *old_dentry,
+ struct vfsmount *old_mnt, struct inode *dir,
+ struct dentry *new_dentry,
+ struct vfsmount *new_mnt)
+{
+ int error = 0;
+ struct aa_profile *profile;
+
+ if (!old_mnt || !new_mnt || !mediated_filesystem(dir))
+ goto out;
+
+ profile = aa_get_profile(current);
+
+ if (profile)
+ error = aa_link(profile, new_dentry, new_mnt,
+ old_dentry, old_mnt);
+
+ aa_put_profile(profile);
+
+out:
+ return error;
+}
+
+static int apparmor_inode_unlink(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt)
+{
+ int check = 0;
+
+ if (S_ISDIR(dentry->d_inode->i_mode))
+ check |= AA_CHECK_DIR;
+ return aa_permission("inode_unlink", dir, dentry, mnt, MAY_WRITE,
+ check);
+}
+
+static int apparmor_inode_symlink(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, const char *old_name)
+{
+ return aa_permission("inode_symlink", dir, dentry, mnt, MAY_WRITE, 0);
+}
+
+static int apparmor_inode_mknod(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, int mode, dev_t dev)
+{
+ return aa_permission("inode_mknod", dir, dentry, mnt, MAY_WRITE, 0);
+}
+
+static int apparmor_inode_rename(struct inode *old_dir,
+ struct dentry *old_dentry,
+ struct vfsmount *old_mnt,
+ struct inode *new_dir,
+ struct dentry *new_dentry,
+ struct vfsmount *new_mnt)
+{
+ struct aa_profile *profile;
+ int error = 0;
+
+ if ((!old_mnt && !new_mnt) || !mediated_filesystem(old_dir))
+ goto out;
+
+ profile = aa_get_profile(current);
+
+ if (profile) {
+ struct inode *inode = old_dentry->d_inode;
+ int check = 0;
+
+ if (inode && S_ISDIR(inode->i_mode))
+ check |= AA_CHECK_DIR;
+ if (old_mnt)
+ error = aa_perm(profile, "inode_rename", old_dentry,
+ old_mnt, MAY_READ | MAY_WRITE, check);
+
+ if (!error && new_mnt) {
+ error = aa_perm(profile, "inode_rename", new_dentry,
+ new_mnt, MAY_WRITE, check);
+ }
+ }
+
+ aa_put_profile(profile);
+
+out:
+ return error;
+}
+
+static int apparmor_inode_permission(struct inode *inode, int mask,
+ struct nameidata *nd)
+{
+ int check = 0;
+
+ if (!nd || nd->flags & (LOOKUP_PARENT | LOOKUP_CONTINUE))
+ return 0;
+ mask = aa_mask_permissions(mask);
+ if (S_ISDIR(inode->i_mode)) {
+ check |= AA_CHECK_DIR;
+ /* allow traverse accesses to directories */
+ mask &= ~MAY_EXEC;
+ }
+ return aa_permission("inode_permission", inode, nd->dentry, nd->mnt,
+ mask, check);
+}
+
+static int apparmor_inode_setattr(struct dentry *dentry, struct vfsmount *mnt,
+ struct iattr *iattr)
+{
+ int error = 0;
+
+ if (!mnt)
+ goto out;
+
+ if (mediated_filesystem(dentry->d_inode)) {
+ struct aa_profile *profile;
+
+ profile = aa_get_profile(current);
+ /*
+ * Mediate any attempt to change attributes of a file
+ * (chmod, chown, chgrp, etc)
+ */
+ if (profile)
+ error = aa_attr(profile, dentry, mnt, iattr);
+
+ aa_put_profile(profile);
+ }
+
+out:
+ return error;
+}
+
+static int aa_xattr_permission(struct dentry *dentry, struct vfsmount *mnt,
+ const char *operation, int mask,
+ struct file *file)
+{
+ int error = 0;
+
+ if (mnt && mediated_filesystem(dentry->d_inode)) {
+ struct aa_profile *profile = aa_get_profile(current);
+ int check = file ? AA_CHECK_FD : 0;
+
+ if (profile)
+ error = aa_perm_xattr(profile, operation, dentry, mnt,
+ mask, check);
+ aa_put_profile(profile);
+ }
+
+ return error;
+}
+
+static int apparmor_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
+ const char *name, const void *value,
+ size_t size, int flags, struct file *file)
+{
+ int error = cap_inode_setxattr(dentry, mnt, name, value, size, flags,
+ file);
+
+ if (!error)
+ error = aa_xattr_permission(dentry, mnt, "xattr set",
+ MAY_WRITE, file);
+ return error;
+}
+
+static int apparmor_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
+ const char *name, struct file *file)
+{
+ return aa_xattr_permission(dentry, mnt, "xattr get", MAY_READ, file);
+}
+
+static int apparmor_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt,
+ struct file *file)
+{
+ return aa_xattr_permission(dentry, mnt, "xattr list", MAY_READ, file);
+}
+
+static int apparmor_inode_removexattr(struct dentry *dentry,
+ struct vfsmount *mnt, const char *name,
+ struct file *file)
+{
+ return aa_xattr_permission(dentry, mnt, "xattr remove", MAY_WRITE,
+ file);
+}
+
+static int aa_file_permission(const char *op, struct file *file, int mask)
+{
+ struct aa_profile *profile;
+ struct aa_profile *file_profile = (struct aa_profile*)file->f_security;
+ int error = 0;
+
+ if (!file_profile)
+ goto out;
+
+ /*
+ * If this file was opened under a different profile, we
+ * revalidate the access against the current profile.
+ */
+ profile = aa_get_profile(current);
+ if (profile && (file_profile != profile || mask & AA_MAY_LOCK)) {
+ struct dentry *dentry = file->f_dentry;
+ struct vfsmount *mnt = file->f_vfsmnt;
+ struct inode *inode = dentry->d_inode;
+ int check = AA_CHECK_FD;
+
+ /*
+ * FIXME: We should remember which profiles we revalidated
+ * against.
+ */
+ if (S_ISDIR(inode->i_mode))
+ check |= AA_CHECK_DIR;
+ error = aa_permission(op, inode, dentry, mnt, mask, check);
+ }
+ aa_put_profile(profile);
+
+out:
+ return error;
+}
+
+static int apparmor_file_permission(struct file *file, int mask)
+{
+ return aa_file_permission("file_permission", file,
+ aa_mask_permissions(mask));
+}
+
+static inline int apparmor_file_lock (struct file *file, unsigned int cmd)
+{
+ int mask = AA_MAY_LOCK;
+ if (cmd == F_WRLCK)
+ mask |= MAY_WRITE;
+ return aa_file_permission("file_lock", file, mask);
+}
+
+static int apparmor_file_alloc_security(struct file *file)
+{
+ struct aa_profile *profile;
+
+ profile = aa_get_profile(current);
+ if (profile)
+ file->f_security = profile;
+
+ return 0;
+}
+
+static void apparmor_file_free_security(struct file *file)
+{
+ struct aa_profile *file_profile = (struct aa_profile*)file->f_security;
+
+ aa_put_profile(file_profile);
+}
+
+static inline int aa_mmap(struct file *file, const char *operation,
+ unsigned long prot, unsigned long flags)
+{
+ struct dentry *dentry;
+ int mask = 0;
+
+ if (!file || !file->f_security)
+ return 0;
+
+ if (prot & PROT_READ)
+ mask |= MAY_READ;
+ /* Private mappings don't require write perms since they don't
+ * write back to the files */
+ if ((prot & PROT_WRITE) && !(flags & MAP_PRIVATE))
+ mask |= MAY_WRITE;
+ if (prot & PROT_EXEC)
+ mask |= AA_EXEC_MMAP;
+
+ dentry = file->f_dentry;
+ return aa_permission(operation, dentry->d_inode, dentry,
+ file->f_vfsmnt, mask, AA_CHECK_FD);
+}
+
+static int apparmor_file_mmap(struct file *file, unsigned long reqprot,
+ unsigned long prot, unsigned long flags,
+ unsigned long addr, unsigned long addr_only)
+{
+ if ((addr < mmap_min_addr) && !capable(CAP_SYS_RAWIO)) {
+ struct aa_profile *profile = aa_get_profile(current);
+ if (profile)
+ /* future control check here */
+ return -EACCES;
+ else
+ return -EACCES;
+ aa_put_profile(profile);
+ }
+
+ return aa_mmap(file, "file_mmap", prot, flags);
+}
+
+static int apparmor_file_mprotect(struct vm_area_struct *vma,
+ unsigned long reqprot, unsigned long prot)
+{
+ return aa_mmap(vma->vm_file, "file_mprotect", prot,
+ !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0);
+}
+
+static int apparmor_task_alloc_security(struct task_struct *task)
+{
+ return aa_clone(task);
+}
+
+/*
+ * Called from IRQ context from RCU callback.
+ */
+static void apparmor_task_free_security(struct task_struct *task)
+{
+ aa_release(task);
+}
+
+static int apparmor_getprocattr(struct task_struct *task, char *name,
+ char **value)
+{
+ unsigned len;
+ int error;
+ struct aa_profile *profile;
+
+ /* AppArmor only supports the "current" process attribute */
+ if (strcmp(name, "current") != 0)
+ return -EINVAL;
+
+ /* must be task querying itself or admin */
+ if (current != task && !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ profile = aa_get_profile(task);
+ error = aa_getprocattr(profile, value, &len);
+ aa_put_profile(profile);
+ if (!error)
+ error = len;
+
+ return error;
+}
+
+static int apparmor_setprocattr(struct task_struct *task, char *name,
+ void *value, size_t size)
+{
+ char *command, *args;
+ int error;
+
+ if (strcmp(name, "current") != 0 || size == 0 || size >= PAGE_SIZE)
+ return -EINVAL;
+ args = value;
+ args[size] = '\0';
+ args = strstrip(args);
+ command = strsep(&args, " ");
+ if (!args)
+ return -EINVAL;
+ while (isspace(*args))
+ args++;
+ if (!*args)
+ return -EINVAL;
+
+ if (strcmp(command, "changehat") == 0) {
+ if (current != task)
+ return -EACCES;
+ error = aa_setprocattr_changehat(args);
+ } else if (strcmp(command, "changeprofile") == 0) {
+ if (current != task)
+ return -EACCES;
+ error = aa_setprocattr_changeprofile(args);
+ } else if (strcmp(command, "setprofile") == 0) {
+ struct aa_profile *profile;
+
+ /* Only an unconfined process with admin capabilities
+ * may change the profile of another task.
+ */
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ profile = aa_get_profile(current);
+ if (profile) {
+ struct aa_audit sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = "profile_set";
+ sa.gfp_mask = GFP_KERNEL;
+ sa.task = task->pid;
+ sa.info = "from confined process";
+ aa_audit_reject(profile, &sa);
+ aa_put_profile(profile);
+ return -EACCES;
+ }
+ error = aa_setprocattr_setprofile(task, args);
+ } else {
+ struct aa_audit sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = "setprocattr";
+ sa.gfp_mask = GFP_KERNEL;
+ sa.info = "invalid command";
+ sa.name = command;
+ sa.task = task->pid;
+ aa_audit_reject(NULL, &sa);
+ return -EINVAL;
+ }
+
+ if (!error)
+ error = size;
+ return error;
+}
+
+struct security_operations apparmor_ops = {
+ .ptrace = apparmor_ptrace,
+ .capget = cap_capget,
+ .capset_check = cap_capset_check,
+ .capset_set = cap_capset_set,
+ .sysctl = apparmor_sysctl,
+ .capable = apparmor_capable,
+ .syslog = cap_syslog,
+
+ .netlink_send = cap_netlink_send,
+ .netlink_recv = cap_netlink_recv,
+
+ .bprm_apply_creds = cap_bprm_apply_creds,
+ .bprm_set_security = apparmor_bprm_set_security,
+ .bprm_secureexec = apparmor_bprm_secureexec,
+
+ .sb_mount = apparmor_sb_mount,
+ .sb_umount = apparmor_umount,
+
+ .inode_mkdir = apparmor_inode_mkdir,
+ .inode_rmdir = apparmor_inode_rmdir,
+ .inode_create = apparmor_inode_create,
+ .inode_link = apparmor_inode_link,
+ .inode_unlink = apparmor_inode_unlink,
+ .inode_symlink = apparmor_inode_symlink,
+ .inode_mknod = apparmor_inode_mknod,
+ .inode_rename = apparmor_inode_rename,
+ .inode_permission = apparmor_inode_permission,
+ .inode_setattr = apparmor_inode_setattr,
+ .inode_setxattr = apparmor_inode_setxattr,
+ .inode_getxattr = apparmor_inode_getxattr,
+ .inode_listxattr = apparmor_inode_listxattr,
+ .inode_removexattr = apparmor_inode_removexattr,
+ .file_permission = apparmor_file_permission,
+ .file_alloc_security = apparmor_file_alloc_security,
+ .file_free_security = apparmor_file_free_security,
+ .file_mmap = apparmor_file_mmap,
+ .file_mprotect = apparmor_file_mprotect,
+ .file_lock = apparmor_file_lock,
+
+ .task_alloc_security = apparmor_task_alloc_security,
+ .task_free_security = apparmor_task_free_security,
+ .task_post_setuid = cap_task_post_setuid,
+ .task_reparent_to_init = cap_task_reparent_to_init,
+
+ .getprocattr = apparmor_getprocattr,
+ .setprocattr = apparmor_setprocattr,
+};
+
+void info_message(const char *str)
+{
+ struct aa_audit sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.gfp_mask = GFP_KERNEL;
+ sa.info = str;
+ printk(KERN_INFO "AppArmor: %s\n", str);
+ if (audit_enabled)
+ aa_audit_message(NULL, &sa, AUDIT_APPARMOR_STATUS);
+}
+
+static int __init apparmor_init(void)
+{
+ int error;
+
+ if (!apparmor_enabled) {
+ info_message("AppArmor disabled by boottime parameter\n");
+ return 0;
+ }
+
+ if ((error = create_apparmorfs())) {
+ AA_ERROR("Unable to activate AppArmor filesystem\n");
+ goto createfs_out;
+ }
+
+ if ((error = alloc_default_namespace())){
+ AA_ERROR("Unable to allocate default profile namespace\n");
+ goto alloc_out;
+ }
+
+ if ((error = register_security(&apparmor_ops))) {
+ AA_ERROR("Unable to register AppArmor\n");
+ goto register_security_out;
+ }
+
+ /* Report that AppArmor successfully initialized */
+ apparmor_initialized = 1;
+ if (apparmor_complain)
+ info_message("AppArmor initialized: complainmode enabled");
+ else
+ info_message("AppArmor initialized");
+
+ return error;
+
+register_security_out:
+ free_default_namespace();
+
+alloc_out:
+ destroy_apparmorfs();
+
+createfs_out:
+ return error;
+
+}
+
+security_initcall(apparmor_init);
+
+void apparmor_disable(void)
+{
+ /* Remove and release all the profiles on the profile list. */
+ mutex_lock(&aa_interface_lock);
+ aa_profile_ns_list_release();
+
+ /* FIXME: cleanup profiles references on files */
+ free_default_namespace();
+
+ /*
+ * Delay for an rcu cycle to make sure that all active task
+ * context readers have finished, and all profiles have been
+ * freed by their rcu callbacks.
+ */
+ synchronize_rcu();
+
+ destroy_apparmorfs();
+ mutex_unlock(&aa_interface_lock);
+
+ apparmor_initialized = 0;
+
+ info_message("AppArmor protection removed");
+}
+
+MODULE_DESCRIPTION("AppArmor process confinement");
+MODULE_AUTHOR("Novell/Immunix, http://bugs.opensuse.org");
+MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,408 @@
From: John Johansen <jjohansen@suse.de>
Subject: AppArmor: Simplified network controls for AppArmor
Simple network control determining which network families a confined
application has access to.
Signed-off-by: John Johansen <jjohansen@suse.de>
---
security/apparmor/Makefile | 7 +
security/apparmor/apparmor.h | 9 ++
security/apparmor/lsm.c | 129 ++++++++++++++++++++++++++++++++++-
security/apparmor/main.c | 107 ++++++++++++++++++++++++++++-
security/apparmor/module_interface.c | 26 ++++++-
5 files changed, 271 insertions(+), 7 deletions(-)
--- a/security/apparmor/Makefile
+++ b/security/apparmor/Makefile
@@ -8,6 +8,11 @@ apparmor-y := main.o list.o procattr.o l
quiet_cmd_make-caps = GEN $@
cmd_make-caps = sed -n -e "/CAP_FS_MASK/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z > $@
-$(obj)/main.o : $(obj)/capability_names.h
+quiet_cmd_make-af = GEN $@
+cmd_make-af = sed -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "s/^\#define[ \\t]\\+AF_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z > $@
+
+$(obj)/main.o : $(obj)/capability_names.h $(obj)/af_names.h
$(obj)/capability_names.h : $(srctree)/include/linux/capability.h
$(call cmd,make-caps)
+$(obj)/af_names.h : $(srctree)/include/linux/socket.h
+ $(call cmd,make-af)
--- a/security/apparmor/apparmor.h
+++ b/security/apparmor/apparmor.h
@@ -16,6 +16,8 @@
#include <linux/fs.h>
#include <linux/binfmts.h>
#include <linux/rcupdate.h>
+#include <linux/socket.h>
+#include <net/sock.h>
/*
* We use MAY_READ, MAY_WRITE, MAY_EXEC, MAY_APPEND and the following flags
@@ -212,6 +214,9 @@ struct aa_profile {
struct list_head task_contexts;
spinlock_t lock;
unsigned long int_flags;
+ u16 network_families[AF_MAX];
+ u16 audit_network[AF_MAX];
+ u16 quiet_network[AF_MAX];
};
extern struct list_head profile_ns_list;
@@ -258,6 +263,7 @@ struct aa_audit {
int request_mask, denied_mask, audit_mask;
struct iattr *iattr;
pid_t task, parent;
+ int family, type, protocol;
int error_code;
};
@@ -319,6 +325,9 @@ extern void aa_change_task_context(struc
struct aa_profile *previous_profile);
extern int aa_may_ptrace(struct aa_task_context *cxt,
struct aa_profile *tracee);
+extern int aa_net_perm(struct aa_profile *profile, char *operation,
+ int family, int type, int protocol);
+extern int aa_revalidate_sk(struct sock *sk, char *operation);
/* lsm.c */
extern int apparmor_initialized;
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -18,6 +18,7 @@
#include <linux/ctype.h>
#include <linux/sysctl.h>
#include <linux/audit.h>
+#include <net/sock.h>
#include "apparmor.h"
#include "inline.h"
@@ -680,6 +681,117 @@ static void apparmor_task_free_security(
aa_release(task);
}
+static int apparmor_socket_create(int family, int type, int protocol, int kern)
+{
+ struct aa_profile *profile;
+ int error = 0;
+
+ if (kern)
+ return 0;
+
+ profile = aa_get_profile(current);
+ if (profile)
+ error = aa_net_perm(profile, "socket_create", family,
+ type, protocol);
+ aa_put_profile(profile);
+
+ return error;
+}
+
+static int apparmor_socket_post_create(struct socket *sock, int family,
+ int type, int protocol, int kern)
+{
+ struct sock *sk = sock->sk;
+
+ if (kern)
+ return 0;
+
+ return aa_revalidate_sk(sk, "socket_post_create");
+}
+
+static int apparmor_socket_bind(struct socket *sock,
+ struct sockaddr *address, int addrlen)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(sk, "socket_bind");
+}
+
+static int apparmor_socket_connect(struct socket *sock,
+ struct sockaddr *address, int addrlen)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(sk, "socket_connect");
+}
+
+static int apparmor_socket_listen(struct socket *sock, int backlog)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(sk, "socket_listen");
+}
+
+static int apparmor_socket_accept(struct socket *sock, struct socket *newsock)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(sk, "socket_accept");
+}
+
+static int apparmor_socket_sendmsg(struct socket *sock,
+ struct msghdr *msg, int size)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(sk, "socket_sendmsg");
+}
+
+static int apparmor_socket_recvmsg(struct socket *sock,
+ struct msghdr *msg, int size, int flags)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(sk, "socket_recvmsg");
+}
+
+static int apparmor_socket_getsockname(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(sk, "socket_getsockname");
+}
+
+static int apparmor_socket_getpeername(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(sk, "socket_getpeername");
+}
+
+static int apparmor_socket_getsockopt(struct socket *sock, int level,
+ int optname)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(sk, "socket_getsockopt");
+}
+
+static int apparmor_socket_setsockopt(struct socket *sock, int level,
+ int optname)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(sk, "socket_setsockopt");
+}
+
+static int apparmor_socket_shutdown(struct socket *sock, int how)
+{
+ struct sock *sk = sock->sk;
+
+ return aa_revalidate_sk(sk, "socket_shutdown");
+}
+
static int apparmor_getprocattr(struct task_struct *task, char *name,
char **value)
{
@@ -780,9 +892,6 @@ struct security_operations apparmor_ops
.capable = apparmor_capable,
.syslog = cap_syslog,
- .netlink_send = cap_netlink_send,
- .netlink_recv = cap_netlink_recv,
-
.bprm_apply_creds = cap_bprm_apply_creds,
.bprm_set_security = apparmor_bprm_set_security,
.bprm_secureexec = apparmor_bprm_secureexec,
@@ -820,6 +929,20 @@ struct security_operations apparmor_ops
.getprocattr = apparmor_getprocattr,
.setprocattr = apparmor_setprocattr,
+
+ .socket_create = apparmor_socket_create,
+ .socket_post_create = apparmor_socket_post_create,
+ .socket_bind = apparmor_socket_bind,
+ .socket_connect = apparmor_socket_connect,
+ .socket_listen = apparmor_socket_listen,
+ .socket_accept = apparmor_socket_accept,
+ .socket_sendmsg = apparmor_socket_sendmsg,
+ .socket_recvmsg = apparmor_socket_recvmsg,
+ .socket_getsockname = apparmor_socket_getsockname,
+ .socket_getpeername = apparmor_socket_getpeername,
+ .socket_getsockopt = apparmor_socket_getsockopt,
+ .socket_setsockopt = apparmor_socket_setsockopt,
+ .socket_shutdown = apparmor_socket_shutdown,
};
void info_message(const char *str)
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -14,6 +14,9 @@
#include <linux/audit.h>
#include <linux/mount.h>
#include <linux/ptrace.h>
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <net/sock.h>
#include "apparmor.h"
@@ -116,6 +119,24 @@ static void aa_audit_file_mask(struct au
audit_log_format(ab, " %s=\"%s::%s\"", name, user, other);
}
+static const char *address_families[] = {
+#include "af_names.h"
+};
+
+static const char *sock_types[] = {
+ "unknown(0)",
+ "stream",
+ "dgram",
+ "raw",
+ "rdm",
+ "seqpacket",
+ "dccp",
+ "unknown(7)",
+ "unknown(8)",
+ "unknown(9)",
+ "packet",
+};
+
/**
* aa_audit - Log an audit event to the audit subsystem
* @profile: profile to check against
@@ -187,7 +208,25 @@ static int aa_audit_base(struct aa_profi
audit_log_untrustedstring(ab, sa->name2);
}
- audit_log_format(ab, " pid=%d", current->pid);
+ if (sa->family || sa->type) {
+ if (address_families[sa->family])
+ audit_log_format(ab, " family=\"%s\"",
+ address_families[sa->family]);
+ else
+ audit_log_format(ab, " family=\"unknown(%d)\"",
+ sa->family);
+
+ if (sock_types[sa->type])
+ audit_log_format(ab, " sock_type=\"%s\"",
+ sock_types[sa->type]);
+ else
+ audit_log_format(ab, " sock_type=\"unknown(%d)\"",
+ sa->type);
+
+ audit_log_format(ab, " protocol=%d", sa->protocol);
+ }
+
+ audit_log_format(ab, " pid=%d", current->pid);
if (profile) {
audit_log_format(ab, " profile=");
@@ -767,6 +806,72 @@ int aa_link(struct aa_profile *profile,
return error;
}
+
+int aa_net_perm(struct aa_profile *profile, char *operation,
+ int family, int type, int protocol)
+{
+ struct aa_audit sa;
+ int error = 0;
+ u16 family_mask, audit_mask, quiet_mask;
+
+ if ((family < 0) || (family >= AF_MAX))
+ return -EINVAL;
+
+ if ((type < 0) || (type >= SOCK_MAX))
+ return -EINVAL;
+
+ /* unix domain and netlink sockets are handled by ipc */
+ if (family == AF_UNIX || family == AF_NETLINK)
+ return 0;
+
+ family_mask = profile->network_families[family];
+ audit_mask = profile->audit_network[family];
+ quiet_mask = profile->quiet_network[family];
+
+ error = (family_mask & (1 << type)) ? 0 : -EACCES;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = operation;
+ sa.gfp_mask = GFP_KERNEL;
+ sa.family = family;
+ sa.type = type;
+ sa.protocol = protocol;
+ sa.error_code = error;
+
+ if (likely(!error)) {
+ if (!PROFILE_AUDIT(profile) && !(family_mask & audit_mask))
+ return 0;
+ } else if (!((1 << type) & ~quiet_mask)) {
+ return error;
+ }
+
+ error = aa_audit(profile, &sa);
+
+ return error;
+}
+
+int aa_revalidate_sk(struct sock *sk, char *operation)
+{
+ struct aa_profile *profile;
+ int error = 0;
+
+ /* this is some debugging code to flush out the network hooks that
+ that are called in interrupt context */
+ if (in_interrupt()) {
+ printk("AppArmor Debug: Hook being called from interrupt context\n");
+ dump_stack();
+ return 0;
+ }
+
+ profile = aa_get_profile(current);
+ if (profile)
+ error = aa_net_perm(profile, operation,
+ sk->sk_family, sk->sk_type,
+ sk->sk_protocol);
+ aa_put_profile(profile);
+
+ return error;
+}
/*******************************
* Global task related functions
--- a/security/apparmor/module_interface.c
+++ b/security/apparmor/module_interface.c
@@ -321,8 +321,8 @@ static struct aa_profile *aa_unpack_prof
struct aa_audit *sa)
{
struct aa_profile *profile = NULL;
-
- int error = -EPROTO;
+ size_t size = 0;
+ int i, error = -EPROTO;
profile = alloc_aa_profile();
if (!profile)
@@ -355,6 +355,28 @@ static struct aa_profile *aa_unpack_prof
if (!aa_is_u32(e, &(profile->set_caps), NULL))
goto fail;
+ size = aa_is_array(e, "net_allowed_af");
+ if (size) {
+ if (size > AF_MAX)
+ goto fail;
+
+ for (i = 0; i < size; i++) {
+ if (!aa_is_u16(e, &profile->network_families[i], NULL))
+ goto fail;
+ if (!aa_is_u16(e, &profile->audit_network[i], NULL))
+ goto fail;
+ if (!aa_is_u16(e, &profile->quiet_network[i], NULL))
+ goto fail;
+ }
+ if (!aa_is_nameX(e, AA_ARRAYEND, NULL))
+ goto fail;
+ /* allow unix domain and netlink sockets they are handled
+ * by IPC
+ */
+ }
+ profile->network_families[AF_UNIX] = 0xffff;
+ profile->network_families[AF_NETLINK] = 0xffff;
+
/* get file rules */
profile->file_rules = aa_unpack_dfa(e);
if (IS_ERR(profile->file_rules)) {

View File

@@ -0,0 +1,55 @@
From: Jeff Mahoney <jeffm@suse.com>
Subject: apparmor: use new ptrace security_operations
This patch implements the new ptrace security_operations members.
->ptrace was changed to ->ptrace_may_access and ->ptrace_traceme.
The apparmor versions are really just wrappers for the old function.
Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
security/apparmor/lsm.c | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -158,7 +158,7 @@ static int aa_reject_syscall(struct task
}
static int apparmor_ptrace(struct task_struct *parent,
- struct task_struct *child, unsigned int mode)
+ struct task_struct *child)
{
struct aa_task_context *cxt;
int error = 0;
@@ -207,6 +207,18 @@ static int apparmor_ptrace(struct task_s
return error;
}
+static int apparmor_ptrace_may_access(struct task_struct *child,
+ unsigned int mode)
+{
+ return apparmor_ptrace(child->parent, child);
+}
+
+
+static int apparmor_ptrace_traceme(struct task_struct *parent)
+{
+ return apparmor_ptrace(parent, current);
+}
+
static int apparmor_capable(struct task_struct *task, int cap)
{
int error;
@@ -899,7 +911,8 @@ static int apparmor_task_setrlimit(unsig
}
struct security_operations apparmor_ops = {
- .ptrace = apparmor_ptrace,
+ .ptrace_may_access = apparmor_ptrace_may_access,
+ .ptrace_traceme = apparmor_ptrace_traceme,
.capget = cap_capget,
.capset_check = cap_capset_check,
.capset_set = cap_capset_set,

View File

@@ -0,0 +1,461 @@
From: John Johansen <jjohansen@suse.de>
Subject: AppArmor: per profile controls for system rlimits
Provide contol of rlimits on a per profile basis. Each profile provides
a per limit contol and corresponding hard limit value, such that when a
profile becomes attached to a task it sets the tasks limits to be <= to
the profiles specified limits. Note: the profile limit value will not
raise a tasks limit if it is already less than the profile mandates.
In addition to setting a tasks limits, the ability to set limits on
a confined task are controlled. AppArmor only controls the raising
of a tasks limits Tasks with CAP_SYS_RESOURCE can have their hard limits
raised up to the value specified by the profile. AppArmor does not
prevent a task for lowering its hard limits, nor does it provide
additional control on soft limits.
AppArmor only controls the limits specified in a profile so that
any limit not specified is free to be modified subject to standard
linux limitations.
---
security/apparmor/apparmor.h | 23 ++++++
security/apparmor/apparmorfs.c | 2
security/apparmor/lsm.c | 16 ++++
security/apparmor/main.c | 132 +++++++++++++++++++++++++++++++----
security/apparmor/module_interface.c | 56 ++++++++++++++
5 files changed, 215 insertions(+), 14 deletions(-)
--- a/security/apparmor/apparmor.h
+++ b/security/apparmor/apparmor.h
@@ -16,6 +16,7 @@
#include <linux/fs.h>
#include <linux/binfmts.h>
#include <linux/rcupdate.h>
+#include <linux/resource.h>
#include <linux/socket.h>
#include <net/sock.h>
@@ -139,6 +140,18 @@ extern unsigned int apparmor_path_max;
#define AA_ERROR(fmt, args...) printk(KERN_ERR "AppArmor: " fmt, ##args)
+/* struct aa_rlimit - rlimits settings for the profile
+ * @mask: which hard limits to set
+ * @limits: rlimit values that override task limits
+ *
+ * AppArmor rlimits are used to set confined task rlimits. Only the
+ * limits specified in @mask will be controlled by apparmor.
+ */
+struct aa_rlimit {
+ unsigned int mask;
+ struct rlimit limits[RLIM_NLIMITS];
+};
+
struct aa_profile;
/* struct aa_namespace - namespace for a set of profiles
@@ -173,6 +186,8 @@ struct aa_namespace {
* @audit_caps: caps that are to be audited
* @quiet_caps: caps that should not be audited
* @capabilities: capabilities granted by the process
+ * @rlimits: rlimits for the profile
+ * @task_count: how many tasks the profile is attached to
* @count: reference count of the profile
* @task_contexts: list of tasks confined by profile
* @lock: lock for the task_contexts list
@@ -210,6 +225,9 @@ struct aa_profile {
kernel_cap_t audit_caps;
kernel_cap_t quiet_caps;
+ struct aa_rlimit rlimits;
+ unsigned int task_count;
+
struct kref count;
struct list_head task_contexts;
spinlock_t lock;
@@ -261,6 +279,7 @@ struct aa_audit {
const char *name2;
const char *name3;
int request_mask, denied_mask, audit_mask;
+ int rlimit;
struct iattr *iattr;
pid_t task, parent;
int family, type, protocol;
@@ -328,6 +347,10 @@ extern int aa_may_ptrace(struct aa_task_
extern int aa_net_perm(struct aa_profile *profile, char *operation,
int family, int type, int protocol);
extern int aa_revalidate_sk(struct sock *sk, char *operation);
+extern int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource,
+ struct rlimit *new_rlim);
+extern void aa_set_rlimits(struct task_struct *task, struct aa_profile *profile);
+
/* lsm.c */
extern int apparmor_initialized;
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -106,7 +106,7 @@ static ssize_t aa_features_read(struct f
{
const char *features = "file=3.0 capability=2.0 network=1.0 "
"change_hat=1.5 change_profile=1.0 "
- "aanamespaces=1.0";
+ "aanamespaces=1.0 rlimit=1.0";
return simple_read_from_buffer(buf, size, ppos, features,
strlen(features));
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -883,6 +883,21 @@ static int apparmor_setprocattr(struct t
return error;
}
+static int apparmor_task_setrlimit(unsigned int resource,
+ struct rlimit *new_rlim)
+{
+ struct aa_profile *profile;
+ int error = 0;
+
+ profile = aa_get_profile(current);
+ if (profile) {
+ error = aa_task_setrlimit(profile, resource, new_rlim);
+ }
+ aa_put_profile(profile);
+
+ return error;
+}
+
struct security_operations apparmor_ops = {
.ptrace = apparmor_ptrace,
.capget = cap_capget,
@@ -926,6 +941,7 @@ struct security_operations apparmor_ops
.task_free_security = apparmor_task_free_security,
.task_post_setuid = cap_task_post_setuid,
.task_reparent_to_init = cap_task_reparent_to_init,
+ .task_setrlimit = apparmor_task_setrlimit,
.getprocattr = apparmor_getprocattr,
.setprocattr = apparmor_setprocattr,
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -177,6 +177,9 @@ static int aa_audit_base(struct aa_profi
if (sa->request_mask)
audit_log_format(ab, " fsuid=%d", current->fsuid);
+ if (sa->rlimit)
+ audit_log_format(ab, " rlimit=%d", sa->rlimit - 1);
+
if (sa->iattr) {
struct iattr *iattr = sa->iattr;
@@ -872,6 +875,79 @@ int aa_revalidate_sk(struct sock *sk, ch
return error;
}
+/**
+ * aa_task_setrlimit - test permission to set an rlimit
+ * @profile - profile confining the task
+ * @resource - the resource being set
+ * @new_rlim - the new resource limit
+ *
+ * Control raising the processes hard limit.
+ */
+int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource,
+ struct rlimit *new_rlim)
+{
+ struct aa_audit sa;
+ int error = 0;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = "setrlimit";
+ sa.gfp_mask = GFP_KERNEL;
+ sa.rlimit = resource + 1;
+
+ if (profile->rlimits.mask & (1 << resource) &&
+ new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max) {
+ sa.error_code = -EACCES;
+
+ error = aa_audit(profile, &sa);
+ }
+
+ return error;
+}
+
+static int aa_rlimit_nproc(struct aa_profile *profile) {
+ if (profile && (profile->rlimits.mask & (1 << RLIMIT_NPROC)) &&
+ profile->task_count >= profile->rlimits.limits[RLIMIT_NPROC].rlim_max)
+ return -EAGAIN;
+ return 0;
+}
+
+void aa_set_rlimits(struct task_struct *task, struct aa_profile *profile)
+{
+ int i, mask;
+
+ if (!profile)
+ return;
+
+ if (!profile->rlimits.mask)
+ return;
+
+ task_lock(task->group_leader);
+ mask = 1;
+ for (i = 0; i < RLIM_NLIMITS; i++, mask <<= 1) {
+ struct rlimit new_rlim, *old_rlim;
+
+ /* check to see if NPROC which is per profile and handled
+ * in clone/exec or whether this is a limit to be set
+ * can't set cpu limit either right now
+ */
+ if (i == RLIMIT_NPROC || i == RLIMIT_CPU)
+ continue;
+
+ old_rlim = task->signal->rlim + i;
+ new_rlim = *old_rlim;
+
+ if (mask & profile->rlimits.mask &&
+ profile->rlimits.limits[i].rlim_max < new_rlim.rlim_max) {
+ new_rlim.rlim_max = profile->rlimits.limits[i].rlim_max;
+ /* soft limit should not exceed hard limit */
+ if (new_rlim.rlim_cur > new_rlim.rlim_max)
+ new_rlim.rlim_cur = new_rlim.rlim_max;
+ }
+
+ *old_rlim = new_rlim;
+ }
+ task_unlock(task->group_leader);
+}
/*******************************
* Global task related functions
@@ -885,6 +961,7 @@ int aa_revalidate_sk(struct sock *sk, ch
*/
int aa_clone(struct task_struct *child)
{
+ struct aa_audit sa;
struct aa_task_context *cxt, *child_cxt;
struct aa_profile *profile;
@@ -894,6 +971,11 @@ int aa_clone(struct task_struct *child)
if (!child_cxt)
return -ENOMEM;
+ memset(&sa, 0, sizeof(sa));
+ sa.operation = "clone";
+ sa.task = child->pid;
+ sa.gfp_mask = GFP_KERNEL;
+
repeat:
profile = aa_get_profile(current);
if (profile) {
@@ -910,18 +992,22 @@ repeat:
goto repeat;
}
+ if (aa_rlimit_nproc(profile)) {
+ sa.info = "rlimit nproc limit exceeded";
+ unlock_profile(profile);
+ aa_audit_reject(profile, &sa);
+ aa_put_profile(profile);
+ return -EAGAIN;
+ }
+
/* No need to grab the child's task lock here. */
aa_change_task_context(child, child_cxt, profile,
cxt->cookie, cxt->previous_profile);
+
unlock_profile(profile);
if (APPARMOR_COMPLAIN(child_cxt) &&
profile == profile->ns->null_complain_profile) {
- struct aa_audit sa;
- memset(&sa, 0, sizeof(sa));
- sa.operation = "clone";
- sa.gfp_mask = GFP_KERNEL;
- sa.task = child->pid;
aa_audit_hint(profile, &sa);
}
aa_put_profile(profile);
@@ -1156,6 +1242,10 @@ repeat:
sa.task = current->parent->pid;
aa_audit_reject(profile, &sa);
}
+ if (PTR_ERR(old_profile) == -EAGAIN) {
+ sa.info = "rlimit nproc limit exceeded";
+ aa_audit_reject(profile, &sa);
+ }
new_profile = old_profile;
goto cleanup;
}
@@ -1303,6 +1393,12 @@ static int do_change_profile(struct aa_p
goto out;
}
+ if ((error = aa_rlimit_nproc(new_profile))) {
+ sa->info = "rlimit nproc limit exceeded";
+ aa_audit_reject(cxt->profile, sa);
+ goto out;
+ }
+
if (new_profile == ns->null_complain_profile)
aa_audit_hint(cxt->profile, sa);
@@ -1481,17 +1577,18 @@ struct aa_profile *__aa_replace_profile(
cxt = lock_task_and_profiles(task, profile);
if (unlikely(profile && profile->isstale)) {
- task_unlock(task);
- unlock_both_profiles(profile, cxt ? cxt->profile : NULL);
- aa_free_task_context(new_cxt);
- return ERR_PTR(-ESTALE);
+ old_profile = ERR_PTR(-ESTALE);
+ goto error;
}
if ((current->ptrace & PT_PTRACED) && aa_may_ptrace(cxt, profile)) {
- task_unlock(task);
- unlock_both_profiles(profile, cxt ? cxt->profile : NULL);
- aa_free_task_context(new_cxt);
- return ERR_PTR(-EPERM);
+ old_profile = ERR_PTR(-EPERM);
+ goto error;
+ }
+
+ if (aa_rlimit_nproc(profile)) {
+ old_profile = ERR_PTR(-EAGAIN);
+ goto error;
}
if (cxt)
@@ -1499,8 +1596,15 @@ struct aa_profile *__aa_replace_profile(
aa_change_task_context(task, new_cxt, profile, 0, NULL);
task_unlock(task);
+ aa_set_rlimits(task, profile);
unlock_both_profiles(profile, old_profile);
return old_profile;
+
+error:
+ task_unlock(task);
+ unlock_both_profiles(profile, cxt ? cxt->profile : NULL);
+ aa_free_task_context(new_cxt);
+ return old_profile;
}
/**
@@ -1565,6 +1669,7 @@ void aa_change_task_context(struct task_
if (old_cxt) {
list_del_init(&old_cxt->list);
+ old_cxt->profile->task_count--;
call_rcu(&old_cxt->rcu, free_aa_task_context_rcu_callback);
}
if (new_cxt) {
@@ -1576,6 +1681,7 @@ void aa_change_task_context(struct task_
new_cxt->cookie = cookie;
new_cxt->task = task;
new_cxt->profile = aa_dup_profile(profile);
+ profile->task_count++;
new_cxt->previous_profile = aa_dup_profile(previous_profile);
list_move(&new_cxt->list, &profile->task_contexts);
}
--- a/security/apparmor/module_interface.c
+++ b/security/apparmor/module_interface.c
@@ -177,6 +177,22 @@ fail:
return 0;
}
+static int aa_is_u64(struct aa_ext *e, u64 *data, const char *name)
+{
+ void *pos = e->pos;
+ if (aa_is_nameX(e, AA_U64, name)) {
+ if (!aa_inbounds(e, sizeof(u64)))
+ goto fail;
+ if (data)
+ *data = le64_to_cpu(get_unaligned((u64 *)e->pos));
+ e->pos += sizeof(u64);
+ return 1;
+ }
+fail:
+ e->pos = pos;
+ return 0;
+}
+
static size_t aa_is_array(struct aa_ext *e, const char *name)
{
void *pos = e->pos;
@@ -312,6 +328,39 @@ fail:
return 0;
}
+int aa_unpack_rlimits(struct aa_ext *e, struct aa_profile *profile)
+{
+ void *pos = e->pos;
+
+ /* rlimits are optional */
+ if (aa_is_nameX(e, AA_STRUCT, "rlimits")) {
+ int i, size;
+ u32 tmp = 0;
+ if (!aa_is_u32(e, &tmp, NULL))
+ goto fail;
+ profile->rlimits.mask = tmp;
+
+ size = aa_is_array(e, NULL);
+ if (size > RLIM_NLIMITS)
+ goto fail;
+ for (i = 0; i < size; i++) {
+ u64 tmp = 0;
+ if (!aa_is_u64(e, &tmp, NULL))
+ goto fail;
+ profile->rlimits.limits[i].rlim_max = tmp;
+ }
+ if (!aa_is_nameX(e, AA_ARRAYEND, NULL))
+ goto fail;
+ if (!aa_is_nameX(e, AA_STRUCTEND, NULL))
+ goto fail;
+ }
+ return 1;
+
+fail:
+ e->pos = pos;
+ return 0;
+}
+
/**
* aa_unpack_profile - unpack a serialized profile
* @e: serialized data extent information
@@ -355,6 +404,9 @@ static struct aa_profile *aa_unpack_prof
if (!aa_is_u32(e, &(profile->set_caps), NULL))
goto fail;
+ if (!aa_unpack_rlimits(e, profile))
+ goto fail;
+
size = aa_is_array(e, "net_allowed_af");
if (size) {
if (size > AF_MAX)
@@ -614,6 +666,8 @@ ssize_t aa_replace_profile(void *udata,
sa.operation = "profile_load";
goto out;
}
+ /* do not fail replacement based off of profile's NPROC rlimit */
+
/*
* Replacement needs to allocate a new aa_task_context for each
* task confined by old_profile. To do this the profile locks
@@ -634,6 +688,7 @@ ssize_t aa_replace_profile(void *udata,
task_lock(task);
task_replace(task, new_cxt, new_profile);
task_unlock(task);
+ aa_set_rlimits(task, new_profile);
new_cxt = NULL;
}
unlock_both_profiles(old_profile, new_profile);
@@ -656,6 +711,7 @@ out:
*
* remove a profile from the profile list and all aa_task_context references
* to said profile.
+ * NOTE: removing confinement does not restore rlimits to preconfinemnet values
*/
ssize_t aa_remove_profile(char *name, size_t size)
{

View File

@@ -0,0 +1,60 @@
From: Andreas Gruenbacher <agruen@suse.de>
Subject: Add d_namespace_path() to compute namespace relative pathnames
In AppArmor, we are interested in pathnames relative to the namespace root.
This is the same as d_path() except for the root where the search ends. Add
a function for computing the namespace-relative path.
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/namespace.c | 30 ++++++++++++++++++++++++++++++
include/linux/mount.h | 2 ++
2 files changed, 32 insertions(+)
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -2299,3 +2299,33 @@ void __put_mnt_ns(struct mnt_namespace *
release_mounts(&umount_list);
kfree(ns);
}
+
+char *d_namespace_path(struct dentry *dentry, struct vfsmount *vfsmnt,
+ char *buf, int buflen)
+{
+ struct path root, tmp, ns_root = { };
+ struct path path = { .mnt = vfsmnt, .dentry = dentry };
+ char *res;
+
+ read_lock(&current->fs->lock);
+ root = current->fs->root;
+ path_get(&current->fs->root);
+ read_unlock(&current->fs->lock);
+ spin_lock(&vfsmount_lock);
+ if (root.mnt)
+ ns_root.mnt = mntget(root.mnt->mnt_ns->root);
+ if (ns_root.mnt)
+ ns_root.dentry = dget(ns_root.mnt->mnt_root);
+ spin_unlock(&vfsmount_lock);
+ tmp = ns_root;
+ res = __d_path(&path, &tmp, buf, buflen,
+ D_PATH_FAIL_DELETED | D_PATH_DISCONNECT);
+ path_put(&root);
+ path_put(&ns_root);
+
+ /* Prevent empty path for lazily unmounted filesystems. */
+ if (!IS_ERR(res) && *res == '\0')
+ *--res = '.';
+ return res;
+}
+EXPORT_SYMBOL(d_namespace_path);
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -134,4 +134,6 @@ extern void mark_mounts_for_expiry(struc
extern spinlock_t vfsmount_lock;
extern dev_t name_to_dev_t(char *name);
+extern char *d_namespace_path(struct dentry *, struct vfsmount *, char *, int);
+
#endif /* _LINUX_MOUNT_H */

View File

@@ -0,0 +1,25 @@
From: Miklos Szeredi <mszeredi@suse.cz>
Subject: fix oops in d_namespace_path
Patch-mainline: no
References: bnc#433504
d_namespace_path uses the current->fs->root to get the current
namespace. If root is detached root.mnt->mnt_ns will be NULL, causing
an Oops. Fix by checking this before dereferencing the mnt_ns.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
---
fs/namespace.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -2312,7 +2312,7 @@ char *d_namespace_path(struct dentry *de
path_get(&current->fs->root);
read_unlock(&current->fs->lock);
spin_lock(&vfsmount_lock);
- if (root.mnt)
+ if (root.mnt && root.mnt->mnt_ns)
ns_root.mnt = mntget(root.mnt->mnt_ns->root);
if (ns_root.mnt)
ns_root.dentry = dget(ns_root.mnt->mnt_root);

View File

@@ -0,0 +1,42 @@
From: Andreas Gruenbacher <agruen@suse.de>
Subject: Switch to vfs_permission() in do_path_lookup()
Switch from file_permission() to vfs_permission() in do_path_lookup():
this avoids calling permission() with a NULL nameidata here.
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/namei.c | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1085,24 +1085,21 @@ static int do_path_lookup(int dfd, const
path_get(&fs->pwd);
read_unlock(&fs->lock);
} else {
- struct dentry *dentry;
-
file = fget_light(dfd, &fput_needed);
retval = -EBADF;
if (!file)
goto out_fail;
- dentry = file->f_path.dentry;
+ nd->path = file->f_path;
retval = -ENOTDIR;
- if (!S_ISDIR(dentry->d_inode->i_mode))
+ if (!S_ISDIR(nd->path.dentry->d_inode->i_mode))
goto fput_fail;
retval = file_permission(file, MAY_EXEC);
if (retval)
goto fput_fail;
- nd->path = file->f_path;
path_get(&file->f_path);
fput_light(file, fput_needed);

View File

@@ -0,0 +1,84 @@
From: Andreas Gruenbacher <agruen@suse.de>
Subject: Enable LSM hooks to distinguish operations on file descriptors from operations on pathnames
Struct iattr already contains ia_file since commit cc4e69de from
Miklos (which is related to commit befc649c). Use this to pass
struct file down the setattr hooks. This allows LSMs to distinguish
operations on file descriptors from operations on paths.
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
Cc: Miklos Szeredi <mszeredi@suse.cz>
---
fs/nfsd/vfs.c | 12 +++++++-----
fs/open.c | 5 ++++-
2 files changed, 11 insertions(+), 6 deletions(-)
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -425,7 +425,7 @@ static ssize_t nfsd_getxattr(struct dent
{
ssize_t buflen;
- buflen = vfs_getxattr(dentry, mnt, key, NULL, 0);
+ buflen = vfs_getxattr(dentry, mnt, key, NULL, 0, NULL);
if (buflen <= 0)
return buflen;
@@ -433,7 +433,7 @@ static ssize_t nfsd_getxattr(struct dent
if (!*buf)
return -ENOMEM;
- return vfs_getxattr(dentry, mnt, key, *buf, buflen);
+ return vfs_getxattr(dentry, mnt, key, *buf, buflen, NULL);
}
#endif
@@ -459,7 +459,7 @@ set_nfsv4_acl_one(struct dentry *dentry,
goto out;
}
- error = vfs_setxattr(dentry, mnt, key, buf, len, 0);
+ error = vfs_setxattr(dentry, mnt, key, buf, len, 0, NULL);
out:
kfree(buf);
return error;
@@ -2133,12 +2133,14 @@ nfsd_set_posix_acl(struct svc_fh *fhp, i
if (error)
goto getout;
if (size)
- error = vfs_setxattr(fhp->fh_dentry, mnt, name, value, size,0);
+ error = vfs_setxattr(fhp->fh_dentry, mnt, name, value, size, 0,
+ NULL);
else {
if (!S_ISDIR(inode->i_mode) && type == ACL_TYPE_DEFAULT)
error = 0;
else {
- error = vfs_removexattr(fhp->fh_dentry, mnt, name);
+ error = vfs_removexattr(fhp->fh_dentry, mnt, name,
+ NULL);
if (error == -ENODATA)
error = 0;
}
--- a/fs/open.c
+++ b/fs/open.c
@@ -623,7 +623,7 @@ SYSCALL_DEFINE2(fchmod, unsigned int, fd
if (mode == (mode_t) -1)
mode = inode->i_mode;
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
- newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
+ newattrs.ia_valid = ATTR_MODE | ATTR_CTIME | ATTR_FILE;
err = fnotify_change(dentry, file->f_path.mnt, &newattrs, file);
mutex_unlock(&inode->i_mutex);
mnt_drop_write(file->f_path.mnt);
@@ -686,6 +686,9 @@ static int chown_common(struct dentry *
if (!S_ISDIR(inode->i_mode))
newattrs.ia_valid |=
ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
+ if (file)
+ newattrs.ia_valid |= ATTR_FILE;
+
mutex_lock(&inode->i_mutex);
error = fnotify_change(dentry, mnt, &newattrs, file);
mutex_unlock(&inode->i_mutex);

View File

@@ -0,0 +1,26 @@
From: John Johansen <jjohansen@suse.de>
Subject: fix enforcement of deny rules in complain mode
Patch-mainline: no
References: bnc#426159
Fix enforcement of deny rules so that they are not enforced in complain
mode. This is necessary so that application behavior is not changed by
the presence of the deny rule.
Signed-off-by: John Johansen <jjohansen@suse.de>
---
security/apparmor/main.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -325,7 +325,7 @@ static int aa_audit_file(struct aa_profi
} else {
int mask = AUDIT_QUIET_MASK(sa->audit_mask);
- if (!(sa->denied_mask & ~mask))
+ if (!(sa->denied_mask & ~mask) && !PROFILE_COMPLAIN(profile))
return sa->error_code;
/* mask off perms whose denial is being silenced */

View File

@@ -0,0 +1,25 @@
From: John Johansen <jrjohansen@verizon.net>
Subject: [PATCH] AppArmor: Fix leak of filename for deleted files
This patch fixes a memory leak where the name doesn't get freed when
a file has been deleted.
Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
security/apparmor/main.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -500,10 +500,10 @@ static char *aa_get_name(struct dentry *
*buffer = buf;
return name;
}
+ kfree(buf);
if (PTR_ERR(name) != -ENAMETOOLONG)
return name;
- kfree(buf);
size <<= 1;
if (size > apparmor_path_max)
return ERR_PTR(-ENAMETOOLONG);

View File

@@ -0,0 +1,66 @@
From: John Johansen <jjohansen@suse.de>
Subject: fix recognition of security= boot parameter
Patch-mainline: no
References: bnc#442668
Fix AppArmor to respect the kernel boot parameter security=, so that if a
different lsm is choosen apparmor does not try to register its lsm hooks.
Signed-off-by: John Johansen <jjohansen@suse.de>
---
security/Kconfig | 9 +++++++++
security/apparmor/lsm.c | 5 +++--
security/security.c | 2 +-
3 files changed, 13 insertions(+), 3 deletions(-)
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -51,6 +51,15 @@ config SECURITY
If you are unsure how to answer this question, answer N.
+config SECURITY_DEFAULT
+ string "Default security module"
+ depends on SECURITY
+ default ""
+ help
+ This determines the security module used if the security=
+ boot parmater is not provided. If a security module is not
+ specified the first module to register will be used.
+
config SECURITY_NETWORK
bool "Socket and Networking Security Hooks"
depends on SECURITY
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -911,6 +911,7 @@ static int apparmor_task_setrlimit(unsig
}
struct security_operations apparmor_ops = {
+ .name = "apparmor",
.ptrace_may_access = apparmor_ptrace_may_access,
.ptrace_traceme = apparmor_ptrace_traceme,
.capget = cap_capget,
@@ -989,8 +990,8 @@ static int __init apparmor_init(void)
{
int error;
- if (!apparmor_enabled) {
- info_message("AppArmor disabled by boottime parameter\n");
+ if (!apparmor_enabled || !security_module_enable(&apparmor_ops)) {
+ info_message("AppArmor disabled by boot time parameter\n");
return 0;
}
--- a/security/security.c
+++ b/security/security.c
@@ -18,7 +18,7 @@
#include <linux/security.h>
/* Boot-time LSM user choice */
-static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1];
+static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] = CONFIG_SECURITY_DEFAULT;
/* things that live in capability.c */
extern struct security_operations default_security_ops;

View File

@@ -0,0 +1,44 @@
From: John Johansen <jjohansen@suse.de>
Subject: Call lsm hook before unhashing dentry in vfs_rmdir()
If we unhash the dentry before calling the security_inode_rmdir hook,
we cannot compute the file's pathname in the hook anymore. AppArmor
needs to know the filename in order to decide whether a file may be
deleted, though.
Signed-off-by: John Johansen <jjohansen@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
---
fs/namei.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2177,6 +2177,10 @@ int vfs_rmdir(struct inode *dir, struct
if (!dir->i_op || !dir->i_op->rmdir)
return -EPERM;
+ error = security_inode_rmdir(dir, dentry, mnt);
+ if (error)
+ return error;
+
DQUOT_INIT(dir);
mutex_lock(&dentry->d_inode->i_mutex);
@@ -2184,12 +2188,9 @@ int vfs_rmdir(struct inode *dir, struct
if (d_mountpoint(dentry))
error = -EBUSY;
else {
- error = security_inode_rmdir(dir, dentry, mnt);
- if (!error) {
- error = dir->i_op->rmdir(dir, dentry);
- if (!error)
- dentry->d_inode->i_flags |= S_DEAD;
- }
+ error = dir->i_op->rmdir(dir, dentry);
+ if (!error)
+ dentry->d_inode->i_flags |= S_DEAD;
}
mutex_unlock(&dentry->d_inode->i_mutex);
if (!error) {

View File

@@ -0,0 +1,108 @@
From: John Johansen <jjohansen@suse.de>
Subject: fix log messages to enable tools profile learning
Patch-mainline: no
References: bnc#447564
The allocation of the child pid is done after the LSM clone hook, which
breaks the AppArmor tools fork tracking, for profiles learning. Output
the parent pid with each log message to enable the tools to handle fork
tracking.
Signed-off-by: John Johansen <jjohansen@suse.de>
---
security/apparmor/lsm.c | 28 ----------------------------
security/apparmor/main.c | 10 +++++-----
security/apparmor/module_interface.c | 2 +-
3 files changed, 6 insertions(+), 34 deletions(-)
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -143,20 +143,6 @@ static int param_set_aa_enabled(const ch
return 0;
}
-static int aa_reject_syscall(struct task_struct *task, gfp_t flags,
- const char *name)
-{
- struct aa_profile *profile = aa_get_profile(task);
- int error = 0;
-
- if (profile) {
- error = aa_audit_syscallreject(profile, flags, name);
- aa_put_profile(profile);
- }
-
- return error;
-}
-
static int apparmor_ptrace(struct task_struct *parent,
struct task_struct *child)
{
@@ -292,17 +278,6 @@ static int apparmor_bprm_secureexec(stru
return ret;
}
-static int apparmor_sb_mount(char *dev_name, struct path *path, char *type,
- unsigned long flags, void *data)
-{
- return aa_reject_syscall(current, GFP_KERNEL, "mount");
-}
-
-static int apparmor_umount(struct vfsmount *mnt, int flags)
-{
- return aa_reject_syscall(current, GFP_KERNEL, "umount");
-}
-
static int apparmor_inode_mkdir(struct inode *dir, struct dentry *dentry,
struct vfsmount *mnt, int mask)
{
@@ -925,9 +900,6 @@ struct security_operations apparmor_ops
.bprm_set_security = apparmor_bprm_set_security,
.bprm_secureexec = apparmor_bprm_secureexec,
- .sb_mount = apparmor_sb_mount,
- .sb_umount = apparmor_umount,
-
.inode_mkdir = apparmor_inode_mkdir,
.inode_rmdir = apparmor_inode_rmdir,
.inode_create = apparmor_inode_create,
--- a/security/apparmor/main.c
+++ b/security/apparmor/main.c
@@ -229,9 +229,13 @@ static int aa_audit_base(struct aa_profi
audit_log_format(ab, " protocol=%d", sa->protocol);
}
- audit_log_format(ab, " pid=%d", current->pid);
+ audit_log_format(ab, " pid=%d", current->pid);
if (profile) {
+ if (!sa->parent)
+ audit_log_format(ab, " parent=%d",
+ current->real_parent->pid);
+
audit_log_format(ab, " profile=");
audit_log_untrustedstring(ab, profile->name);
@@ -1006,10 +1010,6 @@ repeat:
unlock_profile(profile);
- if (APPARMOR_COMPLAIN(child_cxt) &&
- profile == profile->ns->null_complain_profile) {
- aa_audit_hint(profile, &sa);
- }
aa_put_profile(profile);
} else
aa_free_task_context(child_cxt);
--- a/security/apparmor/module_interface.c
+++ b/security/apparmor/module_interface.c
@@ -126,7 +126,7 @@ static int aa_is_nameX(struct aa_ext *e,
* AA_NAME tag value is a u16.
*/
if (aa_is_X(e, AA_NAME)) {
- char *tag;
+ char *tag = NULL;
size_t size = aa_is_u16_chunk(e, &tag);
/* if a name is specified it must match. otherwise skip tag */
if (name && (!size || strcmp(name, tag)))

View File

@@ -0,0 +1,28 @@
From: John Johansen <jjohansen@suse.de>
Subject: AppArmor: reintroduce ATTR_FILE
The fsetattr patch removed ATTR_FILE but AppArmor needs it to distinguish
file based writes.
Note: Now that LSMs must be static, it would be better to add a file
pointer argument to security_operations->inode_setattr() instead. Then
move the fs.h chunk to patches.apparmor/fsetattr-restore-ia_file. -jeffm
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/open.c | 3 +++
1 file changed, 3 insertions(+)
--- a/fs/open.c
+++ b/fs/open.c
@@ -208,6 +208,9 @@ int do_truncate(struct dentry *dentry, s
newattrs.ia_size = length;
newattrs.ia_valid = ATTR_SIZE | time_attrs;
+ if (filp)
+ newattrs.ia_valid |= ATTR_FILE;
+
/* Remove suid/sgid on truncate too */
newattrs.ia_valid |= should_remove_suid(dentry);

View File

@@ -0,0 +1,58 @@
From: Jeff Mahoney <jeffm@suse.com>
Subject: [PATCH] vfs: restore ia_file for compatibility with external modules
References: bnc#381259
patches.apparmor/fsetattr.diff eliminated ia_file and ATTR_FILE in favor
of providing a ->fsetattr call that used a file pointer. Until this
patch is accepted into mainline, this patch provides the backward
compatibility for external file system modules.
Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
fs/attr.c | 13 ++++++++++++-
include/linux/fs.h | 11 +++++++++++
2 files changed, 23 insertions(+), 1 deletion(-)
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -168,8 +168,19 @@ int fnotify_change(struct dentry *dentry
if (!error) {
if (file && file->f_op && file->f_op->fsetattr)
error = file->f_op->fsetattr(file, attr);
- else
+ else {
+ /* External file system still expect to be
+ * passed a file pointer via ia_file and
+ * have it announced via ATTR_FILE. This
+ * just makes it so they don't need to
+ * change their API just for us. External
+ * callers will have set these themselves. */
+ if (file) {
+ attr->ia_valid |= ATTR_FILE;
+ attr->ia_file = file;
+ }
error = inode->i_op->setattr(dentry, attr);
+ }
}
} else {
error = inode_change_ok(inode, attr);
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -367,6 +367,17 @@ struct iattr {
struct timespec ia_atime;
struct timespec ia_mtime;
struct timespec ia_ctime;
+
+ /*
+ * Not an attribute, but an auxilary info for filesystems wanting to
+ * implement an ftruncate() like method. NOTE: filesystem should
+ * check for (ia_valid & ATTR_FILE), and not for (ia_file != NULL).
+ *
+ * NOTE: With patches.apparmor/fsetattr.diff applied, this is
+ * for compatibility with external file system modules only. There
+ * should not be any in-kernel users left.
+ */
+ struct file *ia_file;
};
/*

View File

@@ -0,0 +1,414 @@
Subject: VFS: new fsetattr() file operation
From: Miklos Szeredi <mszeredi@suse.cz>
Add a new file operation: f_op->fsetattr(), that is invoked by
ftruncate, fchmod, fchown and utimensat. Fall back to i_op->setattr()
if it is not defined.
For the reasons why we need this, see patch adding fgetattr().
ftruncate() already passed the open file to the filesystem via the
ia_file member of struct iattr. However it is cleaner to have a
separate file operation for this, so remove ia_file, ATTR_FILE and
convert existing users: fuse and AFS.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> ---
Signed-off-by: John Johansen <jjohansen@suse.de> ---
---
fs/afs/dir.c | 1 +
fs/afs/file.c | 1 +
fs/afs/inode.c | 19 +++++++++++++++----
fs/afs/internal.h | 1 +
fs/attr.c | 19 +++++++++++++++----
fs/fuse/dir.c | 20 +++++++++-----------
fs/fuse/file.c | 7 +++++++
fs/fuse/fuse_i.h | 4 ++++
fs/open.c | 20 ++++++++------------
fs/utimes.c | 9 +++++----
include/linux/fs.h | 9 ++-------
11 files changed, 68 insertions(+), 42 deletions(-)
--- a/fs/afs/dir.c
+++ b/fs/afs/dir.c
@@ -45,6 +45,7 @@ const struct file_operations afs_dir_fil
.release = afs_release,
.readdir = afs_readdir,
.lock = afs_lock,
+ .fsetattr = afs_fsetattr,
};
const struct inode_operations afs_dir_inode_operations = {
--- a/fs/afs/file.c
+++ b/fs/afs/file.c
@@ -36,6 +36,7 @@ const struct file_operations afs_file_op
.fsync = afs_fsync,
.lock = afs_lock,
.flock = afs_flock,
+ .fsetattr = afs_fsetattr,
};
const struct inode_operations afs_file_inode_operations = {
--- a/fs/afs/inode.c
+++ b/fs/afs/inode.c
@@ -358,7 +358,8 @@ void afs_clear_inode(struct inode *inode
/*
* set the attributes of an inode
*/
-int afs_setattr(struct dentry *dentry, struct iattr *attr)
+static int afs_do_setattr(struct dentry *dentry, struct iattr *attr,
+ struct file *file)
{
struct afs_vnode *vnode = AFS_FS_I(dentry->d_inode);
struct key *key;
@@ -380,8 +381,8 @@ int afs_setattr(struct dentry *dentry, s
afs_writeback_all(vnode);
}
- if (attr->ia_valid & ATTR_FILE) {
- key = attr->ia_file->private_data;
+ if (file) {
+ key = file->private_data;
} else {
key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key)) {
@@ -391,10 +392,20 @@ int afs_setattr(struct dentry *dentry, s
}
ret = afs_vnode_setattr(vnode, key, attr);
- if (!(attr->ia_valid & ATTR_FILE))
+ if (!file)
key_put(key);
error:
_leave(" = %d", ret);
return ret;
}
+
+int afs_setattr(struct dentry *dentry, struct iattr *attr)
+{
+ return afs_do_setattr(dentry, attr, NULL);
+}
+
+int afs_fsetattr(struct file *file, struct iattr *attr)
+{
+ return afs_do_setattr(file->f_path.dentry, attr, file);
+}
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -548,6 +548,7 @@ extern void afs_zap_data(struct afs_vnod
extern int afs_validate(struct afs_vnode *, struct key *);
extern int afs_getattr(struct vfsmount *, struct dentry *, struct kstat *);
extern int afs_setattr(struct dentry *, struct iattr *);
+extern int afs_fsetattr(struct file *, struct iattr *);
extern void afs_clear_inode(struct inode *);
/*
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -100,8 +100,8 @@ int inode_setattr(struct inode * inode,
}
EXPORT_SYMBOL(inode_setattr);
-int notify_change(struct dentry *dentry, struct vfsmount *mnt,
- struct iattr *attr)
+int fnotify_change(struct dentry *dentry, struct vfsmount *mnt,
+ struct iattr *attr, struct file *file)
{
struct inode *inode = dentry->d_inode;
mode_t mode = inode->i_mode;
@@ -165,8 +165,12 @@ int notify_change(struct dentry *dentry,
if (inode->i_op && inode->i_op->setattr) {
error = security_inode_setattr(dentry, mnt, attr);
- if (!error)
- error = inode->i_op->setattr(dentry, attr);
+ if (!error) {
+ if (file && file->f_op && file->f_op->fsetattr)
+ error = file->f_op->fsetattr(file, attr);
+ else
+ error = inode->i_op->setattr(dentry, attr);
+ }
} else {
error = inode_change_ok(inode, attr);
if (!error)
@@ -188,5 +192,12 @@ int notify_change(struct dentry *dentry,
return error;
}
+EXPORT_SYMBOL_GPL(fnotify_change);
+
+int notify_change(struct dentry *dentry, struct vfsmount *mnt,
+ struct iattr *attr)
+{
+ return fnotify_change(dentry, mnt, attr, NULL);
+}
EXPORT_SYMBOL(notify_change);
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1105,21 +1105,22 @@ static int fuse_dir_fsync(struct file *f
return file ? fuse_fsync_common(file, de, datasync, 1) : 0;
}
-static bool update_mtime(unsigned ivalid)
+static bool update_mtime(unsigned ivalid, bool have_file)
{
/* Always update if mtime is explicitly set */
if (ivalid & ATTR_MTIME_SET)
return true;
/* If it's an open(O_TRUNC) or an ftruncate(), don't update */
- if ((ivalid & ATTR_SIZE) && (ivalid & (ATTR_OPEN | ATTR_FILE)))
+ if ((ivalid & ATTR_SIZE) && ((ivalid & ATTR_OPEN) || have_file))
return false;
/* In all other cases update */
return true;
}
-static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg)
+static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg,
+ bool have_file)
{
unsigned ivalid = iattr->ia_valid;
@@ -1138,7 +1139,7 @@ static void iattr_to_fattr(struct iattr
if (!(ivalid & ATTR_ATIME_SET))
arg->valid |= FATTR_ATIME_NOW;
}
- if ((ivalid & ATTR_MTIME) && update_mtime(ivalid)) {
+ if ((ivalid & ATTR_MTIME) && update_mtime(ivalid, have_file)) {
arg->valid |= FATTR_MTIME;
arg->mtime = iattr->ia_mtime.tv_sec;
arg->mtimensec = iattr->ia_mtime.tv_nsec;
@@ -1199,8 +1200,8 @@ void fuse_release_nowrite(struct inode *
* vmtruncate() doesn't allow for this case, so do the rlimit checking
* and the actual truncation by hand.
*/
-static int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
- struct file *file)
+int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
+ struct file *file)
{
struct inode *inode = entry->d_inode;
struct fuse_conn *fc = get_fuse_conn(inode);
@@ -1244,7 +1245,7 @@ static int fuse_do_setattr(struct dentry
memset(&inarg, 0, sizeof(inarg));
memset(&outarg, 0, sizeof(outarg));
- iattr_to_fattr(attr, &inarg);
+ iattr_to_fattr(attr, &inarg, file != NULL);
if (file) {
struct fuse_file *ff = file->private_data;
inarg.valid |= FATTR_FH;
@@ -1314,10 +1315,7 @@ error:
static int fuse_setattr(struct dentry *entry, struct iattr *attr)
{
- if (attr->ia_valid & ATTR_FILE)
- return fuse_do_setattr(entry, attr, attr->ia_file);
- else
- return fuse_do_setattr(entry, attr, NULL);
+ return fuse_do_setattr(entry, attr, NULL);
}
static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry,
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -1466,6 +1466,11 @@ static loff_t fuse_file_llseek(struct fi
return retval;
}
+static int fuse_fsetattr(struct file *file, struct iattr *attr)
+{
+ return fuse_do_setattr(file->f_path.dentry, attr, file);
+}
+
static const struct file_operations fuse_file_operations = {
.llseek = fuse_file_llseek,
.read = do_sync_read,
@@ -1479,6 +1484,7 @@ static const struct file_operations fuse
.fsync = fuse_fsync,
.lock = fuse_file_lock,
.flock = fuse_file_flock,
+ .fsetattr = fuse_fsetattr,
.splice_read = generic_file_splice_read,
};
@@ -1492,6 +1498,7 @@ static const struct file_operations fuse
.fsync = fuse_fsync,
.lock = fuse_file_lock,
.flock = fuse_file_flock,
+ .fsetattr = fuse_fsetattr,
/* no mmap and splice_read */
};
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -551,6 +551,10 @@ void fuse_truncate(struct address_space
*/
int fuse_dev_init(void);
+
+int fuse_do_setattr(struct dentry *entry, struct iattr *attr,
+ struct file *file);
+
/**
* Cleanup the client device
*/
--- a/fs/open.c
+++ b/fs/open.c
@@ -207,16 +207,12 @@ int do_truncate(struct dentry *dentry, s
newattrs.ia_size = length;
newattrs.ia_valid = ATTR_SIZE | time_attrs;
- if (filp) {
- newattrs.ia_file = filp;
- newattrs.ia_valid |= ATTR_FILE;
- }
/* Remove suid/sgid on truncate too */
newattrs.ia_valid |= should_remove_suid(dentry);
mutex_lock(&dentry->d_inode->i_mutex);
- err = notify_change(dentry, mnt, &newattrs);
+ err = fnotify_change(dentry, mnt, &newattrs, filp);
mutex_unlock(&dentry->d_inode->i_mutex);
return err;
}
@@ -625,7 +621,7 @@ SYSCALL_DEFINE2(fchmod, unsigned int, fd
mode = inode->i_mode;
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
- err = notify_change(dentry, file->f_path.mnt, &newattrs);
+ err = fnotify_change(dentry, file->f_path.mnt, &newattrs, file);
mutex_unlock(&inode->i_mutex);
mnt_drop_write(file->f_path.mnt);
out_putf:
@@ -669,7 +665,7 @@ SYSCALL_DEFINE2(chmod, const char __user
}
static int chown_common(struct dentry * dentry, struct vfsmount *mnt,
- uid_t user, gid_t group)
+ uid_t user, gid_t group, struct file *file)
{
struct inode *inode = dentry->d_inode;
int error;
@@ -688,7 +684,7 @@ static int chown_common(struct dentry *
newattrs.ia_valid |=
ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
mutex_lock(&inode->i_mutex);
- error = notify_change(dentry, mnt, &newattrs);
+ error = fnotify_change(dentry, mnt, &newattrs, file);
mutex_unlock(&inode->i_mutex);
return error;
@@ -705,7 +701,7 @@ SYSCALL_DEFINE3(chown, const char __user
error = mnt_want_write(path.mnt);
if (error)
goto out_release;
- error = chown_common(path.dentry, path.mnt, user, group);
+ error = chown_common(path.dentry, path.mnt, user, group, NULL);
mnt_drop_write(path.mnt);
out_release:
path_put(&path);
@@ -730,7 +726,7 @@ SYSCALL_DEFINE5(fchownat, int, dfd, cons
error = mnt_want_write(path.mnt);
if (error)
goto out_release;
- error = chown_common(path.dentry, path.mnt, user, group);
+ error = chown_common(path.dentry, path.mnt, user, group, NULL);
mnt_drop_write(path.mnt);
out_release:
path_put(&path);
@@ -749,7 +745,7 @@ SYSCALL_DEFINE3(lchown, const char __use
error = mnt_want_write(path.mnt);
if (error)
goto out_release;
- error = chown_common(path.dentry, path.mnt, user, group);
+ error = chown_common(path.dentry, path.mnt, user, group, NULL);
mnt_drop_write(path.mnt);
out_release:
path_put(&path);
@@ -772,7 +768,7 @@ SYSCALL_DEFINE3(fchown, unsigned int, fd
goto out_fput;
dentry = file->f_path.dentry;
audit_inode(NULL, dentry);
- error = chown_common(dentry, file->f_path.mnt, user, group);
+ error = chown_common(dentry, file->f_path.mnt, user, group, file);
mnt_drop_write(file->f_path.mnt);
out_fput:
fput(file);
--- a/fs/utimes.c
+++ b/fs/utimes.c
@@ -48,7 +48,8 @@ static bool nsec_valid(long nsec)
return nsec >= 0 && nsec <= 999999999;
}
-static int utimes_common(struct path *path, struct timespec *times)
+static int utimes_common(struct path *path, struct timespec *times,
+ struct file *f)
{
int error;
struct iattr newattrs;
@@ -102,7 +103,7 @@ static int utimes_common(struct path *pa
}
}
mutex_lock(&inode->i_mutex);
- error = notify_change(path->dentry, path->mnt, &newattrs);
+ error = fnotify_change(path->dentry, path->mnt, &newattrs, f);
mutex_unlock(&inode->i_mutex);
mnt_drop_write_and_out:
@@ -149,7 +150,7 @@ long do_utimes(int dfd, char __user *fil
if (!file)
goto out;
- error = utimes_common(&file->f_path, times);
+ error = utimes_common(&file->f_path, times, file);
fput(file);
} else {
struct path path;
@@ -162,7 +163,7 @@ long do_utimes(int dfd, char __user *fil
if (error)
goto out;
- error = utimes_common(&path, times);
+ error = utimes_common(&path, times, NULL);
path_put(&path);
}
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -367,13 +367,6 @@ struct iattr {
struct timespec ia_atime;
struct timespec ia_mtime;
struct timespec ia_ctime;
-
- /*
- * Not an attribute, but an auxilary info for filesystems wanting to
- * implement an ftruncate() like method. NOTE: filesystem should
- * check for (ia_valid & ATTR_FILE), and not for (ia_file != NULL).
- */
- struct file *ia_file;
};
/*
@@ -1280,6 +1273,7 @@ struct file_operations {
#define HAVE_FOP_OPEN_EXEC
int (*open_exec) (struct inode *);
int (*setlease)(struct file *, long, struct file_lock **);
+ int (*fsetattr)(struct file *, struct iattr *);
};
struct inode_operations {
@@ -1799,6 +1793,7 @@ extern int do_remount_sb(struct super_bl
extern sector_t bmap(struct inode *, sector_t);
#endif
extern int notify_change(struct dentry *, struct vfsmount *, struct iattr *);
+extern int fnotify_change(struct dentry *, struct vfsmount *, struct iattr *, struct file *);
extern int inode_permission(struct inode *, int);
extern int generic_permission(struct inode *, int,
int (*check_acl)(struct inode *, int));

View File

@@ -0,0 +1,41 @@
From: Andreas Gruenbacher <agruen@suse.de>
Subject: Pass struct path down to remove_suid and children
Required by a later patch that adds a struct vfsmount parameter to
notify_change().
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
mm/filemap.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -1826,12 +1826,12 @@ int should_remove_suid(struct dentry *de
}
EXPORT_SYMBOL(should_remove_suid);
-static int __remove_suid(struct dentry *dentry, int kill)
+static int __remove_suid(struct path *path, int kill)
{
struct iattr newattrs;
newattrs.ia_valid = ATTR_FORCE | kill;
- return notify_change(dentry, &newattrs);
+ return notify_change(path->dentry, &newattrs);
}
int file_remove_suid(struct file *file)
@@ -1846,7 +1846,7 @@ int file_remove_suid(struct file *file)
if (killpriv)
error = security_inode_killpriv(dentry);
if (!error && killsuid)
- error = __remove_suid(dentry, killsuid);
+ error = __remove_suid(&file->f_path, killsuid);
return error;
}

View File

@@ -0,0 +1,107 @@
From: Tony Jones <tonyj@suse.de>
Subject: Pass struct vfsmount to the inode_create LSM hook
This is needed for computing pathnames in the AppArmor LSM.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/namei.c | 2 +-
include/linux/security.h | 9 ++++++---
security/capability.c | 2 +-
security/security.c | 5 +++--
security/selinux/hooks.c | 3 ++-
5 files changed, 13 insertions(+), 8 deletions(-)
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1543,7 +1543,7 @@ int vfs_create(struct inode *dir, struct
return -EACCES; /* shouldn't it be ENOSYS? */
mode &= S_IALLUGO;
mode |= S_IFREG;
- error = security_inode_create(dir, dentry, mode);
+ error = security_inode_create(dir, dentry, nd ? nd->path.mnt : NULL, mode);
if (error)
return error;
DQUOT_INIT(dir);
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -337,6 +337,7 @@ static inline void security_free_mnt_opt
* Check permission to create a regular file.
* @dir contains inode structure of the parent of the new file.
* @dentry contains the dentry structure for the file to be created.
+ * @mnt is the vfsmount corresponding to @dentry (may be NULL).
* @mode contains the file mode of the file to be created.
* Return 0 if permission is granted.
* @inode_link:
@@ -1354,8 +1355,8 @@ struct security_operations {
void (*inode_free_security) (struct inode *inode);
int (*inode_init_security) (struct inode *inode, struct inode *dir,
char **name, void **value, size_t *len);
- int (*inode_create) (struct inode *dir,
- struct dentry *dentry, int mode);
+ int (*inode_create) (struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, int mode);
int (*inode_link) (struct dentry *old_dentry,
struct inode *dir, struct dentry *new_dentry);
int (*inode_unlink) (struct inode *dir, struct dentry *dentry);
@@ -1622,7 +1623,8 @@ int security_inode_alloc(struct inode *i
void security_inode_free(struct inode *inode);
int security_inode_init_security(struct inode *inode, struct inode *dir,
char **name, void **value, size_t *len);
-int security_inode_create(struct inode *dir, struct dentry *dentry, int mode);
+int security_inode_create(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, int mode);
int security_inode_link(struct dentry *old_dentry, struct inode *dir,
struct dentry *new_dentry);
int security_inode_unlink(struct inode *dir, struct dentry *dentry);
@@ -1968,6 +1970,7 @@ static inline int security_inode_init_se
static inline int security_inode_create(struct inode *dir,
struct dentry *dentry,
+ struct vfsmount *mnt,
int mode)
{
return 0;
--- a/security/capability.c
+++ b/security/capability.c
@@ -155,7 +155,7 @@ static int cap_inode_init_security(struc
}
static int cap_inode_create(struct inode *inode, struct dentry *dentry,
- int mask)
+ struct vfsmount *mnt, int mask)
{
return 0;
}
--- a/security/security.c
+++ b/security/security.c
@@ -358,11 +358,12 @@ int security_inode_init_security(struct
}
EXPORT_SYMBOL(security_inode_init_security);
-int security_inode_create(struct inode *dir, struct dentry *dentry, int mode)
+int security_inode_create(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, int mode)
{
if (unlikely(IS_PRIVATE(dir)))
return 0;
- return security_ops->inode_create(dir, dentry, mode);
+ return security_ops->inode_create(dir, dentry, mnt, mode);
}
int security_inode_link(struct dentry *old_dentry, struct inode *dir,
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2566,7 +2566,8 @@ static int selinux_inode_init_security(s
return 0;
}
-static int selinux_inode_create(struct inode *dir, struct dentry *dentry, int mask)
+static int selinux_inode_create(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, int mask)
{
return may_create(dir, dentry, SECCLASS_FILE);
}

View File

@@ -0,0 +1,128 @@
From: Tony Jones <tonyj@suse.de>
Subject: Pass struct vfsmount to the inode_getxattr LSM hook
This is needed for computing pathnames in the AppArmor LSM.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/xattr.c | 2 +-
include/linux/security.h | 11 +++++++----
security/capability.c | 3 ++-
security/security.c | 5 +++--
security/selinux/hooks.c | 3 ++-
security/smack/smack_lsm.c | 4 +++-
6 files changed, 18 insertions(+), 10 deletions(-)
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -141,7 +141,7 @@ vfs_getxattr(struct dentry *dentry, stru
if (error)
return error;
- error = security_inode_getxattr(dentry, name);
+ error = security_inode_getxattr(dentry, mnt, name);
if (error)
return error;
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -446,7 +446,7 @@ static inline void security_free_mnt_opt
* @value identified by @name for @dentry and @mnt.
* @inode_getxattr:
* Check permission before obtaining the extended attributes
- * identified by @name for @dentry.
+ * identified by @name for @dentry and @mnt.
* Return 0 if permission is granted.
* @inode_listxattr:
* Check permission before obtaining the list of extended attribute
@@ -1400,7 +1400,8 @@ struct security_operations {
struct vfsmount *mnt,
const char *name, const void *value,
size_t size, int flags);
- int (*inode_getxattr) (struct dentry *dentry, const char *name);
+ int (*inode_getxattr) (struct dentry *dentry, struct vfsmount *mnt,
+ const char *name);
int (*inode_listxattr) (struct dentry *dentry);
int (*inode_removexattr) (struct dentry *dentry, const char *name);
int (*inode_need_killpriv) (struct dentry *dentry);
@@ -1676,7 +1677,8 @@ int security_inode_setxattr(struct dentr
void security_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt,
const char *name, const void *value,
size_t size, int flags);
-int security_inode_getxattr(struct dentry *dentry, const char *name);
+int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
+ const char *name);
int security_inode_listxattr(struct dentry *dentry);
int security_inode_removexattr(struct dentry *dentry, const char *name);
int security_inode_need_killpriv(struct dentry *dentry);
@@ -2113,7 +2115,8 @@ static inline void security_inode_post_s
{ }
static inline int security_inode_getxattr(struct dentry *dentry,
- const char *name)
+ struct vfsmount *mnt,
+ const char *name)
{
return 0;
}
--- a/security/capability.c
+++ b/security/capability.c
@@ -241,7 +241,8 @@ static void cap_inode_post_setxattr(stru
{
}
-static int cap_inode_getxattr(struct dentry *dentry, const char *name)
+static int cap_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
+ const char *name)
{
return 0;
}
--- a/security/security.c
+++ b/security/security.c
@@ -491,11 +491,12 @@ void security_inode_post_setxattr(struct
flags);
}
-int security_inode_getxattr(struct dentry *dentry, const char *name)
+int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
+ const char *name)
{
if (unlikely(IS_PRIVATE(dentry->d_inode)))
return 0;
- return security_ops->inode_getxattr(dentry, name);
+ return security_ops->inode_getxattr(dentry, mnt, name);
}
int security_inode_listxattr(struct dentry *dentry)
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2796,7 +2796,8 @@ static void selinux_inode_post_setxattr(
return;
}
-static int selinux_inode_getxattr(struct dentry *dentry, const char *name)
+static int selinux_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
+ const char *name)
{
return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
}
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -673,11 +673,13 @@ static void smack_inode_post_setxattr(st
/*
* smack_inode_getxattr - Smack check on getxattr
* @dentry: the object
+ * @mnt: unused
* @name: unused
*
* Returns 0 if access is permitted, an error code otherwise
*/
-static int smack_inode_getxattr(struct dentry *dentry, const char *name)
+static int smack_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
+ const char *name)
{
return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ);
}

View File

@@ -0,0 +1,149 @@
From: Tony Jones <tonyj@suse.de>
Subject: Pass the struct vfsmounts to the inode_link LSM hook
This is needed for computing pathnames in the AppArmor LSM.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/namei.c | 3 ++-
include/linux/security.h | 18 ++++++++++++------
security/capability.c | 5 +++--
security/security.c | 8 +++++---
security/selinux/hooks.c | 9 +++++++--
security/smack/smack_lsm.c | 5 +++--
6 files changed, 32 insertions(+), 16 deletions(-)
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2437,7 +2437,8 @@ int vfs_link(struct dentry *old_dentry,
if (S_ISDIR(inode->i_mode))
return -EPERM;
- error = security_inode_link(old_dentry, dir, new_dentry);
+ error = security_inode_link(old_dentry, old_mnt, dir, new_dentry,
+ new_mnt);
if (error)
return error;
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -343,8 +343,10 @@ static inline void security_free_mnt_opt
* @inode_link:
* Check permission before creating a new hard link to a file.
* @old_dentry contains the dentry structure for an existing link to the file.
+ * @old_mnt is the vfsmount corresponding to @old_dentry (may be NULL).
* @dir contains the inode structure of the parent directory of the new link.
* @new_dentry contains the dentry structure for the new link.
+ * @new_mnt is the vfsmount corresponding to @new_dentry (may be NULL).
* Return 0 if permission is granted.
* @inode_unlink:
* Check the permission to remove a hard link to a file.
@@ -1362,8 +1364,9 @@ struct security_operations {
char **name, void **value, size_t *len);
int (*inode_create) (struct inode *dir, struct dentry *dentry,
struct vfsmount *mnt, int mode);
- int (*inode_link) (struct dentry *old_dentry,
- struct inode *dir, struct dentry *new_dentry);
+ int (*inode_link) (struct dentry *old_dentry, struct vfsmount *old_mnt,
+ struct inode *dir, struct dentry *new_dentry,
+ struct vfsmount *new_mnt);
int (*inode_unlink) (struct inode *dir, struct dentry *dentry);
int (*inode_symlink) (struct inode *dir, struct dentry *dentry,
struct vfsmount *mnt, const char *old_name);
@@ -1632,8 +1635,9 @@ int security_inode_init_security(struct
char **name, void **value, size_t *len);
int security_inode_create(struct inode *dir, struct dentry *dentry,
struct vfsmount *mnt, int mode);
-int security_inode_link(struct dentry *old_dentry, struct inode *dir,
- struct dentry *new_dentry);
+int security_inode_link(struct dentry *old_dentry, struct vfsmount *old_mnt,
+ struct inode *dir, struct dentry *new_dentry,
+ struct vfsmount *new_mnt);
int security_inode_unlink(struct inode *dir, struct dentry *dentry);
int security_inode_symlink(struct inode *dir, struct dentry *dentry,
struct vfsmount *mnt, const char *old_name);
@@ -1987,8 +1991,10 @@ static inline int security_inode_create(
}
static inline int security_inode_link(struct dentry *old_dentry,
- struct inode *dir,
- struct dentry *new_dentry)
+ struct vfsmount *old_mnt,
+ struct inode *dir,
+ struct dentry *new_dentry,
+ struct vfsmount *new_mnt)
{
return 0;
}
--- a/security/capability.c
+++ b/security/capability.c
@@ -160,8 +160,9 @@ static int cap_inode_create(struct inode
return 0;
}
-static int cap_inode_link(struct dentry *old_dentry, struct inode *inode,
- struct dentry *new_dentry)
+static int cap_inode_link(struct dentry *old_dentry, struct vfsmount *old_mnt,
+ struct inode *inode,
+ struct dentry *new_dentry, struct vfsmount *new_mnt)
{
return 0;
}
--- a/security/security.c
+++ b/security/security.c
@@ -366,12 +366,14 @@ int security_inode_create(struct inode *
return security_ops->inode_create(dir, dentry, mnt, mode);
}
-int security_inode_link(struct dentry *old_dentry, struct inode *dir,
- struct dentry *new_dentry)
+int security_inode_link(struct dentry *old_dentry, struct vfsmount *old_mnt,
+ struct inode *dir, struct dentry *new_dentry,
+ struct vfsmount *new_mnt)
{
if (unlikely(IS_PRIVATE(old_dentry->d_inode)))
return 0;
- return security_ops->inode_link(old_dentry, dir, new_dentry);
+ return security_ops->inode_link(old_dentry, old_mnt, dir,
+ new_dentry, new_mnt);
}
int security_inode_unlink(struct inode *dir, struct dentry *dentry)
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2572,11 +2572,16 @@ static int selinux_inode_create(struct i
return may_create(dir, dentry, SECCLASS_FILE);
}
-static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
+static int selinux_inode_link(struct dentry *old_dentry,
+ struct vfsmount *old_mnt,
+ struct inode *dir,
+ struct dentry *new_dentry,
+ struct vfsmount *new_mnt)
{
int rc;
- rc = secondary_ops->inode_link(old_dentry, dir, new_dentry);
+ rc = secondary_ops->inode_link(old_dentry, old_mnt, dir, new_dentry,
+ new_mnt);
if (rc)
return rc;
return may_link(dir, old_dentry, MAY_LINK);
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -432,8 +432,9 @@ static int smack_inode_init_security(str
*
* Returns 0 if access is permitted, an error code otherwise
*/
-static int smack_inode_link(struct dentry *old_dentry, struct inode *dir,
- struct dentry *new_dentry)
+static int smack_inode_link(struct dentry *old_dentry, struct vfsmount *old_mnt,
+ struct inode *dir,
+ struct dentry *new_dentry, struct vfsmount *new_mnt)
{
int rc;
char *isp;

View File

@@ -0,0 +1,105 @@
From: Tony Jones <tonyj@suse.de>
Subject: Pass struct vfsmount to the inode_listxattr LSM hook
This is needed for computing pathnames in the AppArmor LSM.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/xattr.c | 2 +-
include/linux/security.h | 9 +++++----
security/capability.c | 2 +-
security/security.c | 4 ++--
security/selinux/hooks.c | 2 +-
5 files changed, 10 insertions(+), 9 deletions(-)
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -174,7 +174,7 @@ vfs_listxattr(struct dentry *dentry, str
struct inode *inode = dentry->d_inode;
ssize_t error;
- error = security_inode_listxattr(dentry);
+ error = security_inode_listxattr(dentry, mnt);
if (error)
return error;
error = -EOPNOTSUPP;
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -450,7 +450,7 @@ static inline void security_free_mnt_opt
* Return 0 if permission is granted.
* @inode_listxattr:
* Check permission before obtaining the list of extended attribute
- * names for @dentry.
+ * names for @dentry and @mnt.
* Return 0 if permission is granted.
* @inode_removexattr:
* Check permission before removing the extended attribute
@@ -1402,7 +1402,7 @@ struct security_operations {
size_t size, int flags);
int (*inode_getxattr) (struct dentry *dentry, struct vfsmount *mnt,
const char *name);
- int (*inode_listxattr) (struct dentry *dentry);
+ int (*inode_listxattr) (struct dentry *dentry, struct vfsmount *mnt);
int (*inode_removexattr) (struct dentry *dentry, const char *name);
int (*inode_need_killpriv) (struct dentry *dentry);
int (*inode_killpriv) (struct dentry *dentry);
@@ -1679,7 +1679,7 @@ void security_inode_post_setxattr(struct
size_t size, int flags);
int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
const char *name);
-int security_inode_listxattr(struct dentry *dentry);
+int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt);
int security_inode_removexattr(struct dentry *dentry, const char *name);
int security_inode_need_killpriv(struct dentry *dentry);
int security_inode_killpriv(struct dentry *dentry);
@@ -2121,7 +2121,8 @@ static inline int security_inode_getxatt
return 0;
}
-static inline int security_inode_listxattr(struct dentry *dentry)
+static inline int security_inode_listxattr(struct dentry *dentry,
+ struct vfsmount *mnt)
{
return 0;
}
--- a/security/capability.c
+++ b/security/capability.c
@@ -247,7 +247,7 @@ static int cap_inode_getxattr(struct den
return 0;
}
-static int cap_inode_listxattr(struct dentry *dentry)
+static int cap_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt)
{
return 0;
}
--- a/security/security.c
+++ b/security/security.c
@@ -499,11 +499,11 @@ int security_inode_getxattr(struct dentr
return security_ops->inode_getxattr(dentry, mnt, name);
}
-int security_inode_listxattr(struct dentry *dentry)
+int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt)
{
if (unlikely(IS_PRIVATE(dentry->d_inode)))
return 0;
- return security_ops->inode_listxattr(dentry);
+ return security_ops->inode_listxattr(dentry, mnt);
}
int security_inode_removexattr(struct dentry *dentry, const char *name)
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2802,7 +2802,7 @@ static int selinux_inode_getxattr(struct
return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
}
-static int selinux_inode_listxattr(struct dentry *dentry)
+static int selinux_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt)
{
return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
}

View File

@@ -0,0 +1,106 @@
From: Tony Jones <tonyj@suse.de>
Subject: Pass struct vfsmount to the inode_mkdir LSM hook
This is needed for computing pathnames in the AppArmor LSM.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/namei.c | 2 +-
include/linux/security.h | 8 ++++++--
security/capability.c | 2 +-
security/security.c | 5 +++--
security/selinux/hooks.c | 3 ++-
5 files changed, 13 insertions(+), 7 deletions(-)
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2089,7 +2089,7 @@ int vfs_mkdir(struct inode *dir, struct
return -EPERM;
mode &= (S_IRWXUGO|S_ISVTX);
- error = security_inode_mkdir(dir, dentry, mode);
+ error = security_inode_mkdir(dir, dentry, mnt, mode);
if (error)
return error;
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -362,6 +362,7 @@ static inline void security_free_mnt_opt
* associated with inode strcture @dir.
* @dir containst the inode structure of parent of the directory to be created.
* @dentry contains the dentry structure of new directory.
+ * @mnt is the vfsmount corresponding to @dentry (may be NULL).
* @mode contains the mode of new directory.
* Return 0 if permission is granted.
* @inode_rmdir:
@@ -1363,7 +1364,8 @@ struct security_operations {
int (*inode_unlink) (struct inode *dir, struct dentry *dentry);
int (*inode_symlink) (struct inode *dir,
struct dentry *dentry, const char *old_name);
- int (*inode_mkdir) (struct inode *dir, struct dentry *dentry, int mode);
+ int (*inode_mkdir) (struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, int mode);
int (*inode_rmdir) (struct inode *dir, struct dentry *dentry);
int (*inode_mknod) (struct inode *dir, struct dentry *dentry,
int mode, dev_t dev);
@@ -1632,7 +1634,8 @@ int security_inode_link(struct dentry *o
int security_inode_unlink(struct inode *dir, struct dentry *dentry);
int security_inode_symlink(struct inode *dir, struct dentry *dentry,
const char *old_name);
-int security_inode_mkdir(struct inode *dir, struct dentry *dentry, int mode);
+int security_inode_mkdir(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, int mode);
int security_inode_rmdir(struct inode *dir, struct dentry *dentry);
int security_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev);
int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
@@ -2001,6 +2004,7 @@ static inline int security_inode_symlink
static inline int security_inode_mkdir(struct inode *dir,
struct dentry *dentry,
+ struct vfsmount *mnt,
int mode)
{
return 0;
--- a/security/capability.c
+++ b/security/capability.c
@@ -178,7 +178,7 @@ static int cap_inode_symlink(struct inod
}
static int cap_inode_mkdir(struct inode *inode, struct dentry *dentry,
- int mask)
+ struct vfsmount *mnt, int mask)
{
return 0;
}
--- a/security/security.c
+++ b/security/security.c
@@ -389,11 +389,12 @@ int security_inode_symlink(struct inode
return security_ops->inode_symlink(dir, dentry, old_name);
}
-int security_inode_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+int security_inode_mkdir(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, int mode)
{
if (unlikely(IS_PRIVATE(dir)))
return 0;
- return security_ops->inode_mkdir(dir, dentry, mode);
+ return security_ops->inode_mkdir(dir, dentry, mnt, mode);
}
int security_inode_rmdir(struct inode *dir, struct dentry *dentry)
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2597,7 +2597,8 @@ static int selinux_inode_symlink(struct
return may_create(dir, dentry, SECCLASS_LNK_FILE);
}
-static int selinux_inode_mkdir(struct inode *dir, struct dentry *dentry, int mask)
+static int selinux_inode_mkdir(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, int mask)
{
return may_create(dir, dentry, SECCLASS_DIR);
}

View File

@@ -0,0 +1,124 @@
From: Tony Jones <tonyj@suse.de>
Subject: Pass struct vfsmount to the inode_mknod LSM hook
This is needed for computing pathnames in the AppArmor LSM.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/namei.c | 6 +++---
include/linux/security.h | 7 +++++--
security/capability.c | 2 +-
security/security.c | 5 +++--
security/selinux/hooks.c | 5 +++--
5 files changed, 15 insertions(+), 10 deletions(-)
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1994,7 +1994,7 @@ int vfs_mknod(struct inode *dir, struct
if (error)
return error;
- error = security_inode_mknod(dir, dentry, mode, dev);
+ error = security_inode_mknod(dir, dentry, mnt, mode, dev);
if (error)
return error;
@@ -2056,11 +2056,11 @@ asmlinkage long sys_mknodat(int dfd, con
break;
case S_IFCHR: case S_IFBLK:
error = vfs_mknod(nd.path.dentry->d_inode, dentry,
- nd.path, mode, new_decode_dev(dev));
+ nd.path.mnt, mode, new_decode_dev(dev));
break;
case S_IFIFO: case S_IFSOCK:
error = vfs_mknod(nd.path.dentry->d_inode, dentry,
- nd.path, mode, 0);
+ nd.path.mnt, mode, 0);
break;
}
mnt_drop_write(nd.path.mnt);
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -377,6 +377,7 @@ static inline void security_free_mnt_opt
* and not this hook.
* @dir contains the inode structure of parent of the new file.
* @dentry contains the dentry structure of the new file.
+ * @mnt is the vfsmount corresponding to @dentry (may be NULL).
* @mode contains the mode of the new file.
* @dev contains the device number.
* Return 0 if permission is granted.
@@ -1368,7 +1369,7 @@ struct security_operations {
struct vfsmount *mnt, int mode);
int (*inode_rmdir) (struct inode *dir, struct dentry *dentry);
int (*inode_mknod) (struct inode *dir, struct dentry *dentry,
- int mode, dev_t dev);
+ struct vfsmount *mnt, int mode, dev_t dev);
int (*inode_rename) (struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry);
int (*inode_readlink) (struct dentry *dentry);
@@ -1637,7 +1638,8 @@ int security_inode_symlink(struct inode
int security_inode_mkdir(struct inode *dir, struct dentry *dentry,
struct vfsmount *mnt, int mode);
int security_inode_rmdir(struct inode *dir, struct dentry *dentry);
-int security_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev);
+int security_inode_mknod(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, int mode, dev_t dev);
int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry);
int security_inode_readlink(struct dentry *dentry);
@@ -2018,6 +2020,7 @@ static inline int security_inode_rmdir(s
static inline int security_inode_mknod(struct inode *dir,
struct dentry *dentry,
+ struct vfsmount *mnt,
int mode, dev_t dev)
{
return 0;
--- a/security/capability.c
+++ b/security/capability.c
@@ -189,7 +189,7 @@ static int cap_inode_rmdir(struct inode
}
static int cap_inode_mknod(struct inode *inode, struct dentry *dentry,
- int mode, dev_t dev)
+ struct vfsmount *mnt, int mode, dev_t dev)
{
return 0;
}
--- a/security/security.c
+++ b/security/security.c
@@ -404,11 +404,12 @@ int security_inode_rmdir(struct inode *d
return security_ops->inode_rmdir(dir, dentry);
}
-int security_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+int security_inode_mknod(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, int mode, dev_t dev)
{
if (unlikely(IS_PRIVATE(dir)))
return 0;
- return security_ops->inode_mknod(dir, dentry, mode, dev);
+ return security_ops->inode_mknod(dir, dentry, mnt, mode, dev);
}
int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2608,11 +2608,12 @@ static int selinux_inode_rmdir(struct in
return may_link(dir, dentry, MAY_RMDIR);
}
-static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, int mode, dev_t dev)
{
int rc;
- rc = secondary_ops->inode_mknod(dir, dentry, mode, dev);
+ rc = secondary_ops->inode_mknod(dir, dentry, mnt, mode, dev);
if (rc)
return rc;

View File

@@ -0,0 +1,104 @@
From: Tony Jones <tonyj@suse.de>
Subject: Pass struct vfsmount to the inode_readlink LSM hook
This is needed for computing pathnames in the AppArmor LSM.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/stat.c | 2 +-
include/linux/security.h | 8 +++++---
security/capability.c | 2 +-
security/security.c | 4 ++--
security/selinux/hooks.c | 2 +-
5 files changed, 10 insertions(+), 8 deletions(-)
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -308,7 +308,7 @@ SYSCALL_DEFINE4(readlinkat, int, dfd, co
error = -EINVAL;
if (inode->i_op && inode->i_op->readlink) {
- error = security_inode_readlink(path.dentry);
+ error = security_inode_readlink(path.dentry, path.mnt);
if (!error) {
touch_atime(path.mnt, path.dentry);
error = inode->i_op->readlink(path.dentry,
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -392,6 +392,7 @@ static inline void security_free_mnt_opt
* @inode_readlink:
* Check the permission to read the symbolic link.
* @dentry contains the dentry structure for the file link.
+ * @mnt is the vfsmount corresponding to @dentry (may be NULL).
* Return 0 if permission is granted.
* @inode_follow_link:
* Check permission to follow a symbolic link when looking up a pathname.
@@ -1373,7 +1374,7 @@ struct security_operations {
struct vfsmount *mnt, int mode, dev_t dev);
int (*inode_rename) (struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry);
- int (*inode_readlink) (struct dentry *dentry);
+ int (*inode_readlink) (struct dentry *dentry, struct vfsmount *mnt);
int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd);
int (*inode_permission) (struct inode *inode, int mask);
int (*inode_setattr) (struct dentry *dentry, struct vfsmount *,
@@ -1643,7 +1644,7 @@ int security_inode_mknod(struct inode *d
struct vfsmount *mnt, int mode, dev_t dev);
int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry);
-int security_inode_readlink(struct dentry *dentry);
+int security_inode_readlink(struct dentry *dentry, struct vfsmount *mnt);
int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd);
int security_inode_permission(struct inode *inode, int mask);
int security_inode_setattr(struct dentry *dentry, struct vfsmount *mnt,
@@ -2036,7 +2037,8 @@ static inline int security_inode_rename(
return 0;
}
-static inline int security_inode_readlink(struct dentry *dentry)
+static inline int security_inode_readlink(struct dentry *dentry,
+ struct vfsmount *mnt)
{
return 0;
}
--- a/security/capability.c
+++ b/security/capability.c
@@ -200,7 +200,7 @@ static int cap_inode_rename(struct inode
return 0;
}
-static int cap_inode_readlink(struct dentry *dentry)
+static int cap_inode_readlink(struct dentry *dentry, struct vfsmount *mnt)
{
return 0;
}
--- a/security/security.c
+++ b/security/security.c
@@ -422,11 +422,11 @@ int security_inode_rename(struct inode *
new_dir, new_dentry);
}
-int security_inode_readlink(struct dentry *dentry)
+int security_inode_readlink(struct dentry *dentry, struct vfsmount *mnt)
{
if (unlikely(IS_PRIVATE(dentry->d_inode)))
return 0;
- return security_ops->inode_readlink(dentry);
+ return security_ops->inode_readlink(dentry, mnt);
}
int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd)
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2627,7 +2627,7 @@ static int selinux_inode_rename(struct i
return may_rename(old_inode, old_dentry, new_inode, new_dentry);
}
-static int selinux_inode_readlink(struct dentry *dentry)
+static int selinux_inode_readlink(struct dentry *dentry, struct vfsmount *mnt)
{
return dentry_has_perm(current, NULL, dentry, FILE__READ);
}

View File

@@ -0,0 +1,143 @@
From: Tony Jones <tonyj@suse.de>
Subject: Pass struct vfsmount to the inode_removexattr LSM hook
This is needed for computing pathnames in the AppArmor LSM.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/xattr.c | 2 +-
include/linux/security.h | 14 +++++++++-----
security/commoncap.c | 3 ++-
security/security.c | 5 +++--
security/selinux/hooks.c | 3 ++-
security/smack/smack_lsm.c | 6 ++++--
6 files changed, 21 insertions(+), 12 deletions(-)
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -202,7 +202,7 @@ vfs_removexattr(struct dentry *dentry, s
if (error)
return error;
- error = security_inode_removexattr(dentry, name);
+ error = security_inode_removexattr(dentry, mnt, name);
if (error)
return error;
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -57,7 +57,8 @@ extern int cap_bprm_secureexec(struct li
extern int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
const char *name, const void *value, size_t size,
int flags);
-extern int cap_inode_removexattr(struct dentry *dentry, const char *name);
+extern int cap_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt,
+ const char *name);
extern int cap_inode_need_killpriv(struct dentry *dentry);
extern int cap_inode_killpriv(struct dentry *dentry);
extern int cap_task_post_setuid(uid_t old_ruid, uid_t old_euid, uid_t old_suid, int flags);
@@ -1403,7 +1404,8 @@ struct security_operations {
int (*inode_getxattr) (struct dentry *dentry, struct vfsmount *mnt,
const char *name);
int (*inode_listxattr) (struct dentry *dentry, struct vfsmount *mnt);
- int (*inode_removexattr) (struct dentry *dentry, const char *name);
+ int (*inode_removexattr) (struct dentry *dentry, struct vfsmount *mnt,
+ const char *name);
int (*inode_need_killpriv) (struct dentry *dentry);
int (*inode_killpriv) (struct dentry *dentry);
int (*inode_getsecurity) (const struct inode *inode, const char *name, void **buffer, bool alloc);
@@ -1680,7 +1682,8 @@ void security_inode_post_setxattr(struct
int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
const char *name);
int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt);
-int security_inode_removexattr(struct dentry *dentry, const char *name);
+int security_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt,
+ const char *name);
int security_inode_need_killpriv(struct dentry *dentry);
int security_inode_killpriv(struct dentry *dentry);
int security_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc);
@@ -2128,9 +2131,10 @@ static inline int security_inode_listxat
}
static inline int security_inode_removexattr(struct dentry *dentry,
- const char *name)
+ struct vfsmount *mnt,
+ const char *name)
{
- return cap_inode_removexattr(dentry, name);
+ return cap_inode_removexattr(dentry, mnt, name);
}
static inline int security_inode_need_killpriv(struct dentry *dentry)
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -429,7 +429,8 @@ int cap_inode_setxattr(struct dentry *de
return 0;
}
-int cap_inode_removexattr(struct dentry *dentry, const char *name)
+int cap_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt,
+ const char *name)
{
if (!strcmp(name, XATTR_NAME_CAPS)) {
if (!capable(CAP_SETFCAP))
--- a/security/security.c
+++ b/security/security.c
@@ -506,11 +506,12 @@ int security_inode_listxattr(struct dent
return security_ops->inode_listxattr(dentry, mnt);
}
-int security_inode_removexattr(struct dentry *dentry, const char *name)
+int security_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt,
+ const char *name)
{
if (unlikely(IS_PRIVATE(dentry->d_inode)))
return 0;
- return security_ops->inode_removexattr(dentry, name);
+ return security_ops->inode_removexattr(dentry, mnt, name);
}
int security_inode_need_killpriv(struct dentry *dentry)
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2807,7 +2807,8 @@ static int selinux_inode_listxattr(struc
return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
}
-static int selinux_inode_removexattr(struct dentry *dentry, const char *name)
+static int selinux_inode_removexattr(struct dentry *dentry,
+ struct vfsmount *mnt, const char *name)
{
if (strcmp(name, XATTR_NAME_SELINUX))
return selinux_inode_setotherxattr(dentry, name);
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -687,13 +687,15 @@ static int smack_inode_getxattr(struct d
/*
* smack_inode_removexattr - Smack check on removexattr
* @dentry: the object
+ * @mnt: unused
* @name: name of the attribute
*
* Removing the Smack attribute requires CAP_MAC_ADMIN
*
* Returns 0 if access is permitted, an error code otherwise
*/
-static int smack_inode_removexattr(struct dentry *dentry, const char *name)
+static int smack_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt,
+ const char *name)
{
int rc = 0;
@@ -703,7 +705,7 @@ static int smack_inode_removexattr(struc
if (!capable(CAP_MAC_ADMIN))
rc = -EPERM;
} else
- rc = cap_inode_removexattr(dentry, name);
+ rc = cap_inode_removexattr(dentry, mnt, name);
if (rc == 0)
rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE);

View File

@@ -0,0 +1,160 @@
From: Tony Jones <tonyj@suse.de>
Subject: Pass struct vfsmount to the inode_rename LSM hook
This is needed for computing pathnames in the AppArmor LSM.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/namei.c | 6 ++++--
include/linux/security.h | 13 ++++++++++---
security/capability.c | 3 ++-
security/security.c | 7 ++++---
security/selinux/hooks.c | 8 ++++++--
security/smack/smack_lsm.c | 6 +++++-
6 files changed, 31 insertions(+), 12 deletions(-)
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2563,7 +2563,8 @@ static int vfs_rename_dir(struct inode *
return error;
}
- error = security_inode_rename(old_dir, old_dentry, new_dir, new_dentry);
+ error = security_inode_rename(old_dir, old_dentry, old_mnt,
+ new_dir, new_dentry, new_mnt);
if (error)
return error;
@@ -2597,7 +2598,8 @@ static int vfs_rename_other(struct inode
struct inode *target;
int error;
- error = security_inode_rename(old_dir, old_dentry, new_dir, new_dentry);
+ error = security_inode_rename(old_dir, old_dentry, old_mnt,
+ new_dir, new_dentry, new_mnt);
if (error)
return error;
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -390,8 +390,10 @@ static inline void security_free_mnt_opt
* Check for permission to rename a file or directory.
* @old_dir contains the inode structure for parent of the old link.
* @old_dentry contains the dentry structure of the old link.
+ * @old_mnt is the vfsmount corresponding to @old_dentry (may be NULL).
* @new_dir contains the inode structure for parent of the new link.
* @new_dentry contains the dentry structure of the new link.
+ * @new_mnt is the vfsmount corresponding to @new_dentry (may be NULL).
* Return 0 if permission is granted.
* @inode_readlink:
* Check the permission to read the symbolic link.
@@ -1380,7 +1382,9 @@ struct security_operations {
int (*inode_mknod) (struct inode *dir, struct dentry *dentry,
struct vfsmount *mnt, int mode, dev_t dev);
int (*inode_rename) (struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry);
+ struct vfsmount *old_mnt,
+ struct inode *new_dir, struct dentry *new_dentry,
+ struct vfsmount *new_mnt);
int (*inode_readlink) (struct dentry *dentry, struct vfsmount *mnt);
int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd);
int (*inode_permission) (struct inode *inode, int mask);
@@ -1653,7 +1657,8 @@ int security_inode_rmdir(struct inode *d
int security_inode_mknod(struct inode *dir, struct dentry *dentry,
struct vfsmount *mnt, int mode, dev_t dev);
int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry);
+ struct vfsmount *old_mnt, struct inode *new_dir,
+ struct dentry *new_dentry, struct vfsmount *new_mnt);
int security_inode_readlink(struct dentry *dentry, struct vfsmount *mnt);
int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd);
int security_inode_permission(struct inode *inode, int mask);
@@ -2045,8 +2050,10 @@ static inline int security_inode_mknod(s
static inline int security_inode_rename(struct inode *old_dir,
struct dentry *old_dentry,
+ struct vfsmount *old_mnt,
struct inode *new_dir,
- struct dentry *new_dentry)
+ struct dentry *new_dentry,
+ struct vfsmount *new_mnt)
{
return 0;
}
--- a/security/capability.c
+++ b/security/capability.c
@@ -198,7 +198,8 @@ static int cap_inode_mknod(struct inode
}
static int cap_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
- struct inode *new_inode, struct dentry *new_dentry)
+ struct vfsmount *old_mnt, struct inode *new_inode,
+ struct dentry *new_dentry, struct vfsmount *new_mnt)
{
return 0;
}
--- a/security/security.c
+++ b/security/security.c
@@ -417,13 +417,14 @@ int security_inode_mknod(struct inode *d
}
int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry)
+ struct vfsmount *old_mnt, struct inode *new_dir,
+ struct dentry *new_dentry, struct vfsmount *new_mnt)
{
if (unlikely(IS_PRIVATE(old_dentry->d_inode) ||
(new_dentry->d_inode && IS_PRIVATE(new_dentry->d_inode))))
return 0;
- return security_ops->inode_rename(old_dir, old_dentry,
- new_dir, new_dentry);
+ return security_ops->inode_rename(old_dir, old_dentry, old_mnt,
+ new_dir, new_dentry, new_mnt);
}
int security_inode_readlink(struct dentry *dentry, struct vfsmount *mnt)
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2628,8 +2628,12 @@ static int selinux_inode_mknod(struct in
return may_create(dir, dentry, inode_mode_to_security_class(mode));
}
-static int selinux_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
- struct inode *new_inode, struct dentry *new_dentry)
+static int selinux_inode_rename(struct inode *old_inode,
+ struct dentry *old_dentry,
+ struct vfsmount *old_mnt,
+ struct inode *new_inode,
+ struct dentry *new_dentry,
+ struct vfsmount *new_mnt)
{
return may_rename(old_inode, old_dentry, new_inode, new_dentry);
}
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -509,8 +509,10 @@ static int smack_inode_rmdir(struct inod
* smack_inode_rename - Smack check on rename
* @old_inode: the old directory
* @old_dentry: unused
+ * @old_mnt: unused
* @new_inode: the new directory
* @new_dentry: unused
+ * @new_mnt: unused
*
* Read and write access is required on both the old and
* new directories.
@@ -519,8 +521,10 @@ static int smack_inode_rmdir(struct inod
*/
static int smack_inode_rename(struct inode *old_inode,
struct dentry *old_dentry,
+ struct vfsmount *old_mnt,
struct inode *new_inode,
- struct dentry *new_dentry)
+ struct dentry *new_dentry,
+ struct vfsmount *new_mnt)
{
int rc;
char *isp;

View File

@@ -0,0 +1,127 @@
From: Tony Jones <tonyj@suse.de>
Subject: Pass struct vfsmount to the inode_rmdir LSM hook
This is needed for computing pathnames in the AppArmor LSM.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/namei.c | 2 +-
include/linux/security.h | 10 +++++++---
security/capability.c | 3 ++-
security/security.c | 5 +++--
security/selinux/hooks.c | 3 ++-
security/smack/smack_lsm.c | 4 +++-
6 files changed, 18 insertions(+), 9 deletions(-)
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2184,7 +2184,7 @@ int vfs_rmdir(struct inode *dir, struct
if (d_mountpoint(dentry))
error = -EBUSY;
else {
- error = security_inode_rmdir(dir, dentry);
+ error = security_inode_rmdir(dir, dentry, mnt);
if (!error) {
error = dir->i_op->rmdir(dir, dentry);
if (!error)
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -372,6 +372,7 @@ static inline void security_free_mnt_opt
* Check the permission to remove a directory.
* @dir contains the inode structure of parent of the directory to be removed.
* @dentry contains the dentry structure of directory to be removed.
+ * @mnt is the vfsmount corresponding to @dentry (may be NULL).
* Return 0 if permission is granted.
* @inode_mknod:
* Check permissions when creating a special file (or a socket or a fifo
@@ -1372,7 +1373,8 @@ struct security_operations {
struct vfsmount *mnt, const char *old_name);
int (*inode_mkdir) (struct inode *dir, struct dentry *dentry,
struct vfsmount *mnt, int mode);
- int (*inode_rmdir) (struct inode *dir, struct dentry *dentry);
+ int (*inode_rmdir) (struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt);
int (*inode_mknod) (struct inode *dir, struct dentry *dentry,
struct vfsmount *mnt, int mode, dev_t dev);
int (*inode_rename) (struct inode *old_dir, struct dentry *old_dentry,
@@ -1643,7 +1645,8 @@ int security_inode_symlink(struct inode
struct vfsmount *mnt, const char *old_name);
int security_inode_mkdir(struct inode *dir, struct dentry *dentry,
struct vfsmount *mnt, int mode);
-int security_inode_rmdir(struct inode *dir, struct dentry *dentry);
+int security_inode_rmdir(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt);
int security_inode_mknod(struct inode *dir, struct dentry *dentry,
struct vfsmount *mnt, int mode, dev_t dev);
int security_inode_rename(struct inode *old_dir, struct dentry *old_dentry,
@@ -2022,7 +2025,8 @@ static inline int security_inode_mkdir(s
}
static inline int security_inode_rmdir(struct inode *dir,
- struct dentry *dentry)
+ struct dentry *dentry,
+ struct vfsmount *mnt)
{
return 0;
}
--- a/security/capability.c
+++ b/security/capability.c
@@ -184,7 +184,8 @@ static int cap_inode_mkdir(struct inode
return 0;
}
-static int cap_inode_rmdir(struct inode *inode, struct dentry *dentry)
+static int cap_inode_rmdir(struct inode *inode, struct dentry *dentry,
+ struct vfsmount *mnt)
{
return 0;
}
--- a/security/security.c
+++ b/security/security.c
@@ -399,11 +399,12 @@ int security_inode_mkdir(struct inode *d
return security_ops->inode_mkdir(dir, dentry, mnt, mode);
}
-int security_inode_rmdir(struct inode *dir, struct dentry *dentry)
+int security_inode_rmdir(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt)
{
if (unlikely(IS_PRIVATE(dentry->d_inode)))
return 0;
- return security_ops->inode_rmdir(dir, dentry);
+ return security_ops->inode_rmdir(dir, dentry, mnt);
}
int security_inode_mknod(struct inode *dir, struct dentry *dentry,
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2609,7 +2609,8 @@ static int selinux_inode_mkdir(struct in
return may_create(dir, dentry, SECCLASS_DIR);
}
-static int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry)
+static int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt)
{
return may_link(dir, dentry, MAY_RMDIR);
}
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -480,11 +480,13 @@ static int smack_inode_unlink(struct ino
* smack_inode_rmdir - Smack check on directory deletion
* @dir: containing directory object
* @dentry: directory to unlink
+ * @mnt: vfsmount @dentry to unlink
*
* Returns 0 if current can write the containing directory
* and the directory, error code otherwise
*/
-static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry)
+static int smack_inode_rmdir(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt)
{
int rc;

View File

@@ -0,0 +1,146 @@
From: Tony Jones <tonyj@suse.de>
Subject: Pass struct vfsmount to the inode_setattr LSM hook
This is needed for computing pathnames in the AppArmor LSM.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/attr.c | 4 ++--
fs/fat/file.c | 2 +-
include/linux/security.h | 10 +++++++---
security/capability.c | 3 ++-
security/security.c | 5 +++--
security/selinux/hooks.c | 5 +++--
security/smack/smack_lsm.c | 3 ++-
7 files changed, 20 insertions(+), 12 deletions(-)
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -164,13 +164,13 @@ int notify_change(struct dentry *dentry,
down_write(&dentry->d_inode->i_alloc_sem);
if (inode->i_op && inode->i_op->setattr) {
- error = security_inode_setattr(dentry, attr);
+ error = security_inode_setattr(dentry, mnt, attr);
if (!error)
error = inode->i_op->setattr(dentry, attr);
} else {
error = inode_change_ok(inode, attr);
if (!error)
- error = security_inode_setattr(dentry, attr);
+ error = security_inode_setattr(dentry, mnt, attr);
if (!error) {
if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) ||
(ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid))
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -98,7 +98,7 @@ int fat_generic_ioctl(struct inode *inod
* out the RO attribute for checking by the security
* module, just because it maps to a file mode.
*/
- err = security_inode_setattr(filp->f_path.dentry, &ia);
+ err = security_inode_setattr(filp->f_path.dentry, filp->f_path.mnt, &ia);
if (err)
goto up;
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -412,6 +412,7 @@ static inline void security_free_mnt_opt
* file attributes change (such as when a file is truncated, chown/chmod
* operations, transferring disk quotas, etc).
* @dentry contains the dentry structure for the file.
+ * @mnt is the vfsmount corresponding to @dentry (may be NULL).
* @attr is the iattr structure containing the new file attributes.
* Return 0 if permission is granted.
* @inode_getattr:
@@ -1371,7 +1372,8 @@ struct security_operations {
int (*inode_readlink) (struct dentry *dentry);
int (*inode_follow_link) (struct dentry *dentry, struct nameidata *nd);
int (*inode_permission) (struct inode *inode, int mask);
- int (*inode_setattr) (struct dentry *dentry, struct iattr *attr);
+ int (*inode_setattr) (struct dentry *dentry, struct vfsmount *,
+ struct iattr *attr);
int (*inode_getattr) (struct vfsmount *mnt, struct dentry *dentry);
void (*inode_delete) (struct inode *inode);
int (*inode_setxattr) (struct dentry *dentry, const char *name,
@@ -1638,7 +1640,8 @@ int security_inode_rename(struct inode *
int security_inode_readlink(struct dentry *dentry);
int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd);
int security_inode_permission(struct inode *inode, int mask);
-int security_inode_setattr(struct dentry *dentry, struct iattr *attr);
+int security_inode_setattr(struct dentry *dentry, struct vfsmount *mnt,
+ struct iattr *attr);
int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry);
void security_inode_delete(struct inode *inode);
int security_inode_setxattr(struct dentry *dentry, const char *name,
@@ -2041,7 +2044,8 @@ static inline int security_inode_permiss
}
static inline int security_inode_setattr(struct dentry *dentry,
- struct iattr *attr)
+ struct vfsmount *mnt,
+ struct iattr *attr)
{
return 0;
}
--- a/security/capability.c
+++ b/security/capability.c
@@ -216,7 +216,8 @@ static int cap_inode_permission(struct i
return 0;
}
-static int cap_inode_setattr(struct dentry *dentry, struct iattr *iattr)
+static int cap_inode_setattr(struct dentry *dentry, struct vfsmount *mnt,
+ struct iattr *iattr)
{
return 0;
}
--- a/security/security.c
+++ b/security/security.c
@@ -441,11 +441,12 @@ int security_inode_permission(struct ino
return security_ops->inode_permission(inode, mask);
}
-int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
+int security_inode_setattr(struct dentry *dentry, struct vfsmount *mnt,
+ struct iattr *attr)
{
if (unlikely(IS_PRIVATE(dentry->d_inode)))
return 0;
- return security_ops->inode_setattr(dentry, attr);
+ return security_ops->inode_setattr(dentry, mnt, attr);
}
EXPORT_SYMBOL_GPL(security_inode_setattr);
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2656,11 +2656,12 @@ static int selinux_inode_permission(stru
open_file_mask_to_av(inode->i_mode, mask), NULL);
}
-static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
+static int selinux_inode_setattr(struct dentry *dentry, struct vfsmount *mnt,
+ struct iattr *iattr)
{
int rc;
- rc = secondary_ops->inode_setattr(dentry, iattr);
+ rc = secondary_ops->inode_setattr(dentry, mnt, iattr);
if (rc)
return rc;
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -559,7 +559,8 @@ static int smack_inode_permission(struct
*
* Returns 0 if access is permitted, an error code otherwise
*/
-static int smack_inode_setattr(struct dentry *dentry, struct iattr *iattr)
+static int smack_inode_setattr(struct dentry *dentry, struct vfsmount *mnt,
+ struct iattr *iattr)
{
/*
* Need to allow for clearing the setuid bit.

View File

@@ -0,0 +1,256 @@
From: Tony Jones <tonyj@suse.de>
Subject: Pass struct vfsmount to the inode_setxattr LSM hook
This is needed for computing pathnames in the AppArmor LSM.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/xattr.c | 4 ++--
include/linux/security.h | 41 ++++++++++++++++++++++++++---------------
security/capability.c | 3 ++-
security/commoncap.c | 5 +++--
security/security.c | 16 ++++++++++------
security/selinux/hooks.c | 8 +++++---
security/smack/smack_lsm.c | 12 ++++++++----
7 files changed, 56 insertions(+), 33 deletions(-)
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -78,7 +78,7 @@ vfs_setxattr(struct dentry *dentry, stru
return error;
mutex_lock(&inode->i_mutex);
- error = security_inode_setxattr(dentry, name, value, size, flags);
+ error = security_inode_setxattr(dentry, mnt, name, value, size, flags);
if (error)
goto out;
error = -EOPNOTSUPP;
@@ -86,7 +86,7 @@ vfs_setxattr(struct dentry *dentry, stru
error = inode->i_op->setxattr(dentry, name, value, size, flags);
if (!error) {
fsnotify_xattr(dentry);
- security_inode_post_setxattr(dentry, name, value,
+ security_inode_post_setxattr(dentry, mnt, name, value,
size, flags);
}
} else if (!strncmp(name, XATTR_SECURITY_PREFIX,
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -54,8 +54,9 @@ extern void cap_capset_set(struct task_s
extern int cap_bprm_set_security(struct linux_binprm *bprm);
extern void cap_bprm_apply_creds(struct linux_binprm *bprm, int unsafe);
extern int cap_bprm_secureexec(struct linux_binprm *bprm);
-extern int cap_inode_setxattr(struct dentry *dentry, const char *name,
- const void *value, size_t size, int flags);
+extern int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
+ const char *name, const void *value, size_t size,
+ int flags);
extern int cap_inode_removexattr(struct dentry *dentry, const char *name);
extern int cap_inode_need_killpriv(struct dentry *dentry);
extern int cap_inode_killpriv(struct dentry *dentry);
@@ -438,11 +439,11 @@ static inline void security_free_mnt_opt
* inode.
* @inode_setxattr:
* Check permission before setting the extended attributes
- * @value identified by @name for @dentry.
+ * @value identified by @name for @dentry and @mnt.
* Return 0 if permission is granted.
* @inode_post_setxattr:
* Update inode security field after successful setxattr operation.
- * @value identified by @name for @dentry.
+ * @value identified by @name for @dentry and @mnt.
* @inode_getxattr:
* Check permission before obtaining the extended attributes
* identified by @name for @dentry.
@@ -1392,10 +1393,13 @@ struct security_operations {
struct iattr *attr);
int (*inode_getattr) (struct vfsmount *mnt, struct dentry *dentry);
void (*inode_delete) (struct inode *inode);
- int (*inode_setxattr) (struct dentry *dentry, const char *name,
- const void *value, size_t size, int flags);
- void (*inode_post_setxattr) (struct dentry *dentry, const char *name,
- const void *value, size_t size, int flags);
+ int (*inode_setxattr) (struct dentry *dentry, struct vfsmount *mnt,
+ const char *name, const void *value, size_t size,
+ int flags);
+ void (*inode_post_setxattr) (struct dentry *dentry,
+ struct vfsmount *mnt,
+ const char *name, const void *value,
+ size_t size, int flags);
int (*inode_getxattr) (struct dentry *dentry, const char *name);
int (*inode_listxattr) (struct dentry *dentry);
int (*inode_removexattr) (struct dentry *dentry, const char *name);
@@ -1666,10 +1670,12 @@ int security_inode_setattr(struct dentry
struct iattr *attr);
int security_inode_getattr(struct vfsmount *mnt, struct dentry *dentry);
void security_inode_delete(struct inode *inode);
-int security_inode_setxattr(struct dentry *dentry, const char *name,
- const void *value, size_t size, int flags);
-void security_inode_post_setxattr(struct dentry *dentry, const char *name,
- const void *value, size_t size, int flags);
+int security_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
+ const char *name, const void *value,
+ size_t size, int flags);
+void security_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt,
+ const char *name, const void *value,
+ size_t size, int flags);
int security_inode_getxattr(struct dentry *dentry, const char *name);
int security_inode_listxattr(struct dentry *dentry);
int security_inode_removexattr(struct dentry *dentry, const char *name);
@@ -2092,13 +2098,18 @@ static inline void security_inode_delete
{ }
static inline int security_inode_setxattr(struct dentry *dentry,
- const char *name, const void *value, size_t size, int flags)
+ struct vfsmount *mnt,
+ const char *name, const void *value,
+ size_t size, int flags)
{
- return cap_inode_setxattr(dentry, name, value, size, flags);
+ return cap_inode_setxattr(dentry, mnt, name, value, size, flags);
}
static inline void security_inode_post_setxattr(struct dentry *dentry,
- const char *name, const void *value, size_t size, int flags)
+ struct vfsmount *mnt,
+ const char *name,
+ const void *value,
+ size_t size, int flags)
{ }
static inline int security_inode_getxattr(struct dentry *dentry,
--- a/security/capability.c
+++ b/security/capability.c
@@ -235,7 +235,8 @@ static void cap_inode_delete(struct inod
{
}
-static void cap_inode_post_setxattr(struct dentry *dentry, const char *name,
+static void cap_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt,
+ const char *name,
const void *value, size_t size, int flags)
{
}
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -414,8 +414,9 @@ int cap_bprm_secureexec (struct linux_bi
current->egid != current->gid);
}
-int cap_inode_setxattr(struct dentry *dentry, const char *name,
- const void *value, size_t size, int flags)
+int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
+ const char *name, const void *value, size_t size,
+ int flags)
{
if (!strcmp(name, XATTR_NAME_CAPS)) {
if (!capable(CAP_SETFCAP))
--- a/security/security.c
+++ b/security/security.c
@@ -471,20 +471,24 @@ void security_inode_delete(struct inode
security_ops->inode_delete(inode);
}
-int security_inode_setxattr(struct dentry *dentry, const char *name,
- const void *value, size_t size, int flags)
+int security_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
+ const char *name, const void *value, size_t size,
+ int flags)
{
if (unlikely(IS_PRIVATE(dentry->d_inode)))
return 0;
- return security_ops->inode_setxattr(dentry, name, value, size, flags);
+ return security_ops->inode_setxattr(dentry, mnt, name, value, size,
+ flags);
}
-void security_inode_post_setxattr(struct dentry *dentry, const char *name,
- const void *value, size_t size, int flags)
+void security_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt,
+ const char *name, const void *value,
+ size_t size, int flags)
{
if (unlikely(IS_PRIVATE(dentry->d_inode)))
return;
- security_ops->inode_post_setxattr(dentry, name, value, size, flags);
+ security_ops->inode_post_setxattr(dentry, mnt, name, value, size,
+ flags);
}
int security_inode_getxattr(struct dentry *dentry, const char *name)
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2713,8 +2713,9 @@ static int selinux_inode_setotherxattr(s
return dentry_has_perm(current, NULL, dentry, FILE__SETATTR);
}
-static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
- const void *value, size_t size, int flags)
+static int selinux_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
+ const char *name, const void *value,
+ size_t size, int flags)
{
struct task_security_struct *tsec = current->security;
struct inode *inode = dentry->d_inode;
@@ -2768,7 +2769,8 @@ static int selinux_inode_setxattr(struct
&ad);
}
-static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
+static void selinux_inode_post_setxattr(struct dentry *dentry,
+ struct vfsmount *mnt, const char *name,
const void *value, size_t size,
int flags)
{
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -595,6 +595,7 @@ static int smack_inode_getattr(struct vf
/**
* smack_inode_setxattr - Smack check for setting xattrs
* @dentry: the object
+ * @mnt: unused
* @name: name of the attribute
* @value: unused
* @size: unused
@@ -604,8 +605,9 @@ static int smack_inode_getattr(struct vf
*
* Returns 0 if access is permitted, an error code otherwise
*/
-static int smack_inode_setxattr(struct dentry *dentry, const char *name,
- const void *value, size_t size, int flags)
+static int smack_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
+ const char *name, const void *value,
+ size_t size, int flags)
{
int rc = 0;
@@ -617,7 +619,7 @@ static int smack_inode_setxattr(struct d
if (size == 0)
rc = -EINVAL;
} else
- rc = cap_inode_setxattr(dentry, name, value, size, flags);
+ rc = cap_inode_setxattr(dentry, mnt, name, value, size, flags);
if (rc == 0)
rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE);
@@ -628,6 +630,7 @@ static int smack_inode_setxattr(struct d
/**
* smack_inode_post_setxattr - Apply the Smack update approved above
* @dentry: object
+ * @mnt: unused
* @name: attribute name
* @value: attribute value
* @size: attribute size
@@ -636,7 +639,8 @@ static int smack_inode_setxattr(struct d
* Set the pointer in the inode blob to the entry found
* in the master label list.
*/
-static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
+static void smack_inode_post_setxattr(struct dentry *dentry,
+ struct vfsmount *mnt, const char *name,
const void *value, size_t size, int flags)
{
struct inode_smack *isp;

View File

@@ -0,0 +1,105 @@
From: Tony Jones <tonyj@suse.de>
Subject: Pass struct vfsmount to the inode_symlink LSM hook
This is needed for computing pathnames in the AppArmor LSM.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/namei.c | 2 +-
include/linux/security.h | 8 +++++---
security/capability.c | 2 +-
security/security.c | 4 ++--
security/selinux/hooks.c | 3 ++-
5 files changed, 11 insertions(+), 8 deletions(-)
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2358,7 +2358,7 @@ int vfs_symlink(struct inode *dir, struc
if (!dir->i_op || !dir->i_op->symlink)
return -EPERM;
- error = security_inode_symlink(dir, dentry, oldname);
+ error = security_inode_symlink(dir, dentry, mnt, oldname);
if (error)
return error;
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -355,6 +355,7 @@ static inline void security_free_mnt_opt
* Check the permission to create a symbolic link to a file.
* @dir contains the inode structure of parent directory of the symbolic link.
* @dentry contains the dentry structure of the symbolic link.
+ * @mnt is the vfsmount corresponding to @dentry (may be NULL).
* @old_name contains the pathname of file.
* Return 0 if permission is granted.
* @inode_mkdir:
@@ -1363,8 +1364,8 @@ struct security_operations {
int (*inode_link) (struct dentry *old_dentry,
struct inode *dir, struct dentry *new_dentry);
int (*inode_unlink) (struct inode *dir, struct dentry *dentry);
- int (*inode_symlink) (struct inode *dir,
- struct dentry *dentry, const char *old_name);
+ int (*inode_symlink) (struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, const char *old_name);
int (*inode_mkdir) (struct inode *dir, struct dentry *dentry,
struct vfsmount *mnt, int mode);
int (*inode_rmdir) (struct inode *dir, struct dentry *dentry);
@@ -1634,7 +1635,7 @@ int security_inode_link(struct dentry *o
struct dentry *new_dentry);
int security_inode_unlink(struct inode *dir, struct dentry *dentry);
int security_inode_symlink(struct inode *dir, struct dentry *dentry,
- const char *old_name);
+ struct vfsmount *mnt, const char *old_name);
int security_inode_mkdir(struct inode *dir, struct dentry *dentry,
struct vfsmount *mnt, int mode);
int security_inode_rmdir(struct inode *dir, struct dentry *dentry);
@@ -1999,6 +2000,7 @@ static inline int security_inode_unlink(
static inline int security_inode_symlink(struct inode *dir,
struct dentry *dentry,
+ struct vfsmount *mnt,
const char *old_name)
{
return 0;
--- a/security/capability.c
+++ b/security/capability.c
@@ -172,7 +172,7 @@ static int cap_inode_unlink(struct inode
}
static int cap_inode_symlink(struct inode *inode, struct dentry *dentry,
- const char *name)
+ struct vfsmount *mnt, const char *name)
{
return 0;
}
--- a/security/security.c
+++ b/security/security.c
@@ -382,11 +382,11 @@ int security_inode_unlink(struct inode *
}
int security_inode_symlink(struct inode *dir, struct dentry *dentry,
- const char *old_name)
+ struct vfsmount *mnt, const char *old_name)
{
if (unlikely(IS_PRIVATE(dir)))
return 0;
- return security_ops->inode_symlink(dir, dentry, old_name);
+ return security_ops->inode_symlink(dir, dentry, mnt, old_name);
}
int security_inode_mkdir(struct inode *dir, struct dentry *dentry,
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2592,7 +2592,8 @@ static int selinux_inode_unlink(struct i
return may_link(dir, dentry, MAY_UNLINK);
}
-static int selinux_inode_symlink(struct inode *dir, struct dentry *dentry, const char *name)
+static int selinux_inode_symlink(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt, const char *name)
{
return may_create(dir, dentry, SECCLASS_LNK_FILE);
}

View File

@@ -0,0 +1,132 @@
From: Tony Jones <tonyj@suse.de>
Subject: Pass struct vfsmount to the inode_unlink LSM hook
This is needed for computing pathnames in the AppArmor LSM.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/namei.c | 2 +-
include/linux/security.h | 10 +++++++---
security/capability.c | 3 ++-
security/security.c | 5 +++--
security/selinux/hooks.c | 5 +++--
security/smack/smack_lsm.c | 4 +++-
6 files changed, 19 insertions(+), 10 deletions(-)
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2264,7 +2264,7 @@ int vfs_unlink(struct inode *dir, struct
if (d_mountpoint(dentry))
error = -EBUSY;
else {
- error = security_inode_unlink(dir, dentry);
+ error = security_inode_unlink(dir, dentry, mnt);
if (!error)
error = dir->i_op->unlink(dir, dentry);
}
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -352,6 +352,7 @@ static inline void security_free_mnt_opt
* Check the permission to remove a hard link to a file.
* @dir contains the inode structure of parent directory of the file.
* @dentry contains the dentry structure for file to be unlinked.
+ * @mnt is the vfsmount corresponding to @dentry (may be NULL).
* Return 0 if permission is granted.
* @inode_symlink:
* Check the permission to create a symbolic link to a file.
@@ -1368,7 +1369,8 @@ struct security_operations {
int (*inode_link) (struct dentry *old_dentry, struct vfsmount *old_mnt,
struct inode *dir, struct dentry *new_dentry,
struct vfsmount *new_mnt);
- int (*inode_unlink) (struct inode *dir, struct dentry *dentry);
+ int (*inode_unlink) (struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt);
int (*inode_symlink) (struct inode *dir, struct dentry *dentry,
struct vfsmount *mnt, const char *old_name);
int (*inode_mkdir) (struct inode *dir, struct dentry *dentry,
@@ -1640,7 +1642,8 @@ int security_inode_create(struct inode *
int security_inode_link(struct dentry *old_dentry, struct vfsmount *old_mnt,
struct inode *dir, struct dentry *new_dentry,
struct vfsmount *new_mnt);
-int security_inode_unlink(struct inode *dir, struct dentry *dentry);
+int security_inode_unlink(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt);
int security_inode_symlink(struct inode *dir, struct dentry *dentry,
struct vfsmount *mnt, const char *old_name);
int security_inode_mkdir(struct inode *dir, struct dentry *dentry,
@@ -2003,7 +2006,8 @@ static inline int security_inode_link(st
}
static inline int security_inode_unlink(struct inode *dir,
- struct dentry *dentry)
+ struct dentry *dentry,
+ struct vfsmount *mnt)
{
return 0;
}
--- a/security/capability.c
+++ b/security/capability.c
@@ -167,7 +167,8 @@ static int cap_inode_link(struct dentry
return 0;
}
-static int cap_inode_unlink(struct inode *inode, struct dentry *dentry)
+static int cap_inode_unlink(struct inode *inode, struct dentry *dentry,
+ struct vfsmount *mnt)
{
return 0;
}
--- a/security/security.c
+++ b/security/security.c
@@ -376,11 +376,12 @@ int security_inode_link(struct dentry *o
new_dentry, new_mnt);
}
-int security_inode_unlink(struct inode *dir, struct dentry *dentry)
+int security_inode_unlink(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt)
{
if (unlikely(IS_PRIVATE(dentry->d_inode)))
return 0;
- return security_ops->inode_unlink(dir, dentry);
+ return security_ops->inode_unlink(dir, dentry, mnt);
}
int security_inode_symlink(struct inode *dir, struct dentry *dentry,
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2587,11 +2587,12 @@ static int selinux_inode_link(struct den
return may_link(dir, old_dentry, MAY_LINK);
}
-static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry)
+static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt)
{
int rc;
- rc = secondary_ops->inode_unlink(dir, dentry);
+ rc = secondary_ops->inode_unlink(dir, dentry, mnt);
if (rc)
return rc;
return may_link(dir, dentry, MAY_UNLINK);
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -454,11 +454,13 @@ static int smack_inode_link(struct dentr
* smack_inode_unlink - Smack check on inode deletion
* @dir: containing directory object
* @dentry: file to unlink
+ * @mnt: vfsmount of file to unlink
*
* Returns 0 if current can write the containing directory
* and the object, error code otherwise
*/
-static int smack_inode_unlink(struct inode *dir, struct dentry *dentry)
+static int smack_inode_unlink(struct inode *dir, struct dentry *dentry,
+ struct vfsmount *mnt)
{
struct inode *ip = dentry->d_inode;
int rc;

View File

@@ -0,0 +1,592 @@
From: Andreas Gruenbacher <agruen@suse.de>
Subject: Pass struct file down the inode_*xattr security LSM hooks
This allows LSMs to also distinguish between file descriptor and path
access for the xattr operations. (The other relevant operations are
covered by the setattr hook.)
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/xattr.c | 59 +++++++++++++++++++++++----------------------
include/linux/security.h | 38 ++++++++++++++++------------
include/linux/xattr.h | 9 +++---
security/capability.c | 5 ++-
security/commoncap.c | 4 +--
security/security.c | 17 ++++++------
security/selinux/hooks.c | 10 ++++---
security/smack/smack_lsm.c | 14 ++++++----
8 files changed, 87 insertions(+), 69 deletions(-)
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -68,7 +68,7 @@ xattr_permission(struct inode *inode, co
int
vfs_setxattr(struct dentry *dentry, struct vfsmount *mnt, const char *name,
- const void *value, size_t size, int flags)
+ const void *value, size_t size, int flags, struct file *file)
{
struct inode *inode = dentry->d_inode;
int error;
@@ -78,7 +78,7 @@ vfs_setxattr(struct dentry *dentry, stru
return error;
mutex_lock(&inode->i_mutex);
- error = security_inode_setxattr(dentry, mnt, name, value, size, flags);
+ error = security_inode_setxattr(dentry, mnt, name, value, size, flags, file);
if (error)
goto out;
error = -EOPNOTSUPP;
@@ -132,7 +132,7 @@ EXPORT_SYMBOL_GPL(xattr_getsecurity);
ssize_t
vfs_getxattr(struct dentry *dentry, struct vfsmount *mnt, const char *name,
- void *value, size_t size)
+ void *value, size_t size, struct file *file)
{
struct inode *inode = dentry->d_inode;
int error;
@@ -141,7 +141,7 @@ vfs_getxattr(struct dentry *dentry, stru
if (error)
return error;
- error = security_inode_getxattr(dentry, mnt, name);
+ error = security_inode_getxattr(dentry, mnt, name, file);
if (error)
return error;
@@ -169,12 +169,12 @@ EXPORT_SYMBOL_GPL(vfs_getxattr);
ssize_t
vfs_listxattr(struct dentry *dentry, struct vfsmount *mnt, char *list,
- size_t size)
+ size_t size, struct file *file)
{
struct inode *inode = dentry->d_inode;
ssize_t error;
- error = security_inode_listxattr(dentry, mnt);
+ error = security_inode_listxattr(dentry, mnt, file);
if (error)
return error;
error = -EOPNOTSUPP;
@@ -190,7 +190,8 @@ vfs_listxattr(struct dentry *dentry, str
EXPORT_SYMBOL_GPL(vfs_listxattr);
int
-vfs_removexattr(struct dentry *dentry, struct vfsmount *mnt, const char *name)
+vfs_removexattr(struct dentry *dentry, struct vfsmount *mnt, const char *name,
+ struct file *file)
{
struct inode *inode = dentry->d_inode;
int error;
@@ -202,7 +203,7 @@ vfs_removexattr(struct dentry *dentry, s
if (error)
return error;
- error = security_inode_removexattr(dentry, mnt, name);
+ error = security_inode_removexattr(dentry, mnt, name, file);
if (error)
return error;
@@ -222,7 +223,7 @@ EXPORT_SYMBOL_GPL(vfs_removexattr);
*/
static long
setxattr(struct dentry *dentry, struct vfsmount *mnt, const char __user *name,
- const void __user *value, size_t size, int flags)
+ const void __user *value, size_t size, int flags, struct file *file)
{
int error;
void *kvalue = NULL;
@@ -249,7 +250,7 @@ setxattr(struct dentry *dentry, struct v
}
}
- error = vfs_setxattr(dentry, mnt, kname, kvalue, size, flags);
+ error = vfs_setxattr(dentry, mnt, kname, kvalue, size, flags, file);
kfree(kvalue);
return error;
}
@@ -266,7 +267,7 @@ SYSCALL_DEFINE5(setxattr, const char __u
return error;
error = mnt_want_write(path.mnt);
if (!error) {
- error = setxattr(path.dentry, path.mnt, name, value, size, flags);
+ error = setxattr(path.dentry, path.mnt, name, value, size, flags, NULL);
mnt_drop_write(path.mnt);
}
path_put(&path);
@@ -285,7 +286,7 @@ SYSCALL_DEFINE5(lsetxattr, const char __
return error;
error = mnt_want_write(path.mnt);
if (!error) {
- error = setxattr(path.dentry, path.mnt, name, value, size, flags);
+ error = setxattr(path.dentry, path.mnt, name, value, size, flags, NULL);
mnt_drop_write(path.mnt);
}
path_put(&path);
@@ -306,7 +307,8 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, cons
audit_inode(NULL, dentry);
error = mnt_want_write_file(f->f_path.mnt, f);
if (!error) {
- error = setxattr(dentry, f->f_vfsmnt, name, value, size, flags);
+ error = setxattr(dentry, f->f_vfsmnt, name, value, size, flags,
+ f);
mnt_drop_write(f->f_path.mnt);
}
fput(f);
@@ -318,7 +320,7 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, cons
*/
static ssize_t
getxattr(struct dentry *dentry, struct vfsmount *mnt, const char __user *name,
- void __user *value, size_t size)
+ void __user *value, size_t size, struct file *file)
{
ssize_t error;
void *kvalue = NULL;
@@ -338,7 +340,7 @@ getxattr(struct dentry *dentry, struct v
return -ENOMEM;
}
- error = vfs_getxattr(dentry, mnt, kname, kvalue, size);
+ error = vfs_getxattr(dentry, mnt, kname, kvalue, size, file);
if (error > 0) {
if (size && copy_to_user(value, kvalue, error))
error = -EFAULT;
@@ -360,7 +362,7 @@ SYSCALL_DEFINE4(getxattr, const char __u
error = user_path(pathname, &path);
if (error)
return error;
- error = getxattr(path.dentry, path.mnt, name, value, size);
+ error = getxattr(path.dentry, path.mnt, name, value, size, NULL);
path_put(&path);
return error;
}
@@ -374,7 +376,7 @@ SYSCALL_DEFINE4(lgetxattr, const char __
error = user_lpath(pathname, &path);
if (error)
return error;
- error = getxattr(path.dentry, path.mnt, name, value, size);
+ error = getxattr(path.dentry, path.mnt, name, value, size, NULL);
path_put(&path);
return error;
}
@@ -389,7 +391,7 @@ SYSCALL_DEFINE4(fgetxattr, int, fd, cons
if (!f)
return error;
audit_inode(NULL, f->f_path.dentry);
- error = getxattr(f->f_path.dentry, f->f_path.mnt, name, value, size);
+ error = getxattr(f->f_path.dentry, f->f_path.mnt, name, value, size, f);
fput(f);
return error;
}
@@ -399,7 +401,7 @@ SYSCALL_DEFINE4(fgetxattr, int, fd, cons
*/
static ssize_t
listxattr(struct dentry *dentry, struct vfsmount *mnt, char __user *list,
- size_t size)
+ size_t size, struct file *file)
{
ssize_t error;
char *klist = NULL;
@@ -412,7 +414,7 @@ listxattr(struct dentry *dentry, struct
return -ENOMEM;
}
- error = vfs_listxattr(dentry, mnt, klist, size);
+ error = vfs_listxattr(dentry, mnt, klist, size, file);
if (error > 0) {
if (size && copy_to_user(list, klist, error))
error = -EFAULT;
@@ -434,7 +436,7 @@ SYSCALL_DEFINE3(listxattr, const char __
error = user_path(pathname, &path);
if (error)
return error;
- error = listxattr(path.dentry, path.mnt, list, size);
+ error = listxattr(path.dentry, path.mnt, list, size, NULL);
path_put(&path);
return error;
}
@@ -448,7 +450,7 @@ SYSCALL_DEFINE3(llistxattr, const char _
error = user_lpath(pathname, &path);
if (error)
return error;
- error = listxattr(path.dentry, path.mnt, list, size);
+ error = listxattr(path.dentry, path.mnt, list, size, NULL);
path_put(&path);
return error;
}
@@ -462,7 +464,7 @@ SYSCALL_DEFINE3(flistxattr, int, fd, cha
if (!f)
return error;
audit_inode(NULL, f->f_path.dentry);
- error = listxattr(f->f_path.dentry, f->f_path.mnt, list, size);
+ error = listxattr(f->f_path.dentry, f->f_path.mnt, list, size, f);
fput(f);
return error;
}
@@ -471,7 +473,8 @@ SYSCALL_DEFINE3(flistxattr, int, fd, cha
* Extended attribute REMOVE operations
*/
static long
-removexattr(struct dentry *dentry, struct vfsmount *mnt, const char __user *name)
+removexattr(struct dentry *dentry, struct vfsmount *mnt,
+ const char __user *name, struct file *file)
{
int error;
char kname[XATTR_NAME_MAX + 1];
@@ -482,7 +485,7 @@ removexattr(struct dentry *dentry, struc
if (error < 0)
return error;
- return vfs_removexattr(dentry, mnt, kname);
+ return vfs_removexattr(dentry, mnt, kname, file);
}
SYSCALL_DEFINE2(removexattr, const char __user *, pathname,
@@ -496,7 +499,7 @@ SYSCALL_DEFINE2(removexattr, const char
return error;
error = mnt_want_write(path.mnt);
if (!error) {
- error = removexattr(path.dentry, path.mnt, name);
+ error = removexattr(path.dentry, path.mnt, name, NULL);
mnt_drop_write(path.mnt);
}
path_put(&path);
@@ -514,7 +517,7 @@ SYSCALL_DEFINE2(lremovexattr, const char
return error;
error = mnt_want_write(path.mnt);
if (!error) {
- error = removexattr(path.dentry, path.mnt, name);
+ error = removexattr(path.dentry, path.mnt, name, NULL);
mnt_drop_write(path.mnt);
}
path_put(&path);
@@ -534,7 +537,7 @@ SYSCALL_DEFINE2(fremovexattr, int, fd, c
audit_inode(NULL, dentry);
error = mnt_want_write_file(f->f_path.mnt, f);
if (!error) {
- error = removexattr(dentry, f->f_path.mnt, name);
+ error = removexattr(dentry, f->f_path.mnt, name, f);
mnt_drop_write(f->f_path.mnt);
}
fput(f);
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -56,9 +56,9 @@ extern void cap_bprm_apply_creds(struct
extern int cap_bprm_secureexec(struct linux_binprm *bprm);
extern int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
const char *name, const void *value, size_t size,
- int flags);
+ int flags, struct file *file);
extern int cap_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt,
- const char *name);
+ const char *name, struct file *file);
extern int cap_inode_need_killpriv(struct dentry *dentry);
extern int cap_inode_killpriv(struct dentry *dentry);
extern int cap_task_post_setuid(uid_t old_ruid, uid_t old_euid, uid_t old_suid, int flags);
@@ -1396,16 +1396,17 @@ struct security_operations {
void (*inode_delete) (struct inode *inode);
int (*inode_setxattr) (struct dentry *dentry, struct vfsmount *mnt,
const char *name, const void *value, size_t size,
- int flags);
+ int flags, struct file *file);
void (*inode_post_setxattr) (struct dentry *dentry,
struct vfsmount *mnt,
const char *name, const void *value,
size_t size, int flags);
int (*inode_getxattr) (struct dentry *dentry, struct vfsmount *mnt,
- const char *name);
- int (*inode_listxattr) (struct dentry *dentry, struct vfsmount *mnt);
+ const char *name, struct file *file);
+ int (*inode_listxattr) (struct dentry *dentry, struct vfsmount *mnt,
+ struct file *file);
int (*inode_removexattr) (struct dentry *dentry, struct vfsmount *mnt,
- const char *name);
+ const char *name, struct file *file);
int (*inode_need_killpriv) (struct dentry *dentry);
int (*inode_killpriv) (struct dentry *dentry);
int (*inode_getsecurity) (const struct inode *inode, const char *name, void **buffer, bool alloc);
@@ -1675,15 +1676,16 @@ int security_inode_getattr(struct vfsmou
void security_inode_delete(struct inode *inode);
int security_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
const char *name, const void *value,
- size_t size, int flags);
+ size_t size, int flags, struct file *file);
void security_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt,
const char *name, const void *value,
size_t size, int flags);
int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
- const char *name);
-int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt);
+ const char *name, struct file *file);
+int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt,
+ struct file *file);
int security_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt,
- const char *name);
+ const char *name, struct file *file);
int security_inode_need_killpriv(struct dentry *dentry);
int security_inode_killpriv(struct dentry *dentry);
int security_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc);
@@ -2105,9 +2107,10 @@ static inline void security_inode_delete
static inline int security_inode_setxattr(struct dentry *dentry,
struct vfsmount *mnt,
const char *name, const void *value,
- size_t size, int flags)
+ size_t size, int flags,
+ struct file *file)
{
- return cap_inode_setxattr(dentry, mnt, name, value, size, flags);
+ return cap_inode_setxattr(dentry, mnt, name, value, size, flags, file);
}
static inline void security_inode_post_setxattr(struct dentry *dentry,
@@ -2119,22 +2122,25 @@ static inline void security_inode_post_s
static inline int security_inode_getxattr(struct dentry *dentry,
struct vfsmount *mnt,
- const char *name)
+ const char *name,
+ struct file *file)
{
return 0;
}
static inline int security_inode_listxattr(struct dentry *dentry,
- struct vfsmount *mnt)
+ struct vfsmount *mnt,
+ struct file *file)
{
return 0;
}
static inline int security_inode_removexattr(struct dentry *dentry,
struct vfsmount *mnt,
- const char *name)
+ const char *name,
+ struct file *file)
{
- return cap_inode_removexattr(dentry, mnt, name);
+ return cap_inode_removexattr(dentry, mnt, name, file);
}
static inline int security_inode_need_killpriv(struct dentry *dentry)
--- a/include/linux/xattr.h
+++ b/include/linux/xattr.h
@@ -17,6 +17,7 @@
#include <linux/types.h>
#include <linux/mount.h>
+#include <linux/fs.h>
/* Namespaces */
#define XATTR_OS2_PREFIX "os2."
@@ -48,10 +49,10 @@ struct xattr_handler {
};
ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t);
-ssize_t vfs_getxattr(struct dentry *, struct vfsmount *, const char *, void *, size_t);
-ssize_t vfs_listxattr(struct dentry *d, struct vfsmount *, char *list, size_t size);
-int vfs_setxattr(struct dentry *, struct vfsmount *, const char *, const void *, size_t, int);
-int vfs_removexattr(struct dentry *, struct vfsmount *mnt, const char *);
+ssize_t vfs_getxattr(struct dentry *, struct vfsmount *, const char *, void *, size_t, struct file *file);
+ssize_t vfs_listxattr(struct dentry *d, struct vfsmount *, char *list, size_t size, struct file *file);
+int vfs_setxattr(struct dentry *, struct vfsmount *, const char *, const void *, size_t, int, struct file *file);
+int vfs_removexattr(struct dentry *, struct vfsmount *mnt, const char *, struct file *file);
ssize_t generic_getxattr(struct dentry *dentry, const char *name, void *buffer, size_t size);
ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size);
--- a/security/capability.c
+++ b/security/capability.c
@@ -242,12 +242,13 @@ static void cap_inode_post_setxattr(stru
}
static int cap_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
- const char *name)
+ const char *name, struct file *f)
{
return 0;
}
-static int cap_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt)
+static int cap_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt,
+ struct file *f)
{
return 0;
}
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -416,7 +416,7 @@ int cap_bprm_secureexec (struct linux_bi
int cap_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
const char *name, const void *value, size_t size,
- int flags)
+ int flags, struct file *file)
{
if (!strcmp(name, XATTR_NAME_CAPS)) {
if (!capable(CAP_SETFCAP))
@@ -430,7 +430,7 @@ int cap_inode_setxattr(struct dentry *de
}
int cap_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt,
- const char *name)
+ const char *name, struct file *file)
{
if (!strcmp(name, XATTR_NAME_CAPS)) {
if (!capable(CAP_SETFCAP))
--- a/security/security.c
+++ b/security/security.c
@@ -473,12 +473,12 @@ void security_inode_delete(struct inode
int security_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
const char *name, const void *value, size_t size,
- int flags)
+ int flags, struct file *file)
{
if (unlikely(IS_PRIVATE(dentry->d_inode)))
return 0;
return security_ops->inode_setxattr(dentry, mnt, name, value, size,
- flags);
+ flags, file);
}
void security_inode_post_setxattr(struct dentry *dentry, struct vfsmount *mnt,
@@ -492,26 +492,27 @@ void security_inode_post_setxattr(struct
}
int security_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
- const char *name)
+ const char *name, struct file *file)
{
if (unlikely(IS_PRIVATE(dentry->d_inode)))
return 0;
- return security_ops->inode_getxattr(dentry, mnt, name);
+ return security_ops->inode_getxattr(dentry, mnt, name, file);
}
-int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt)
+int security_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt,
+ struct file *file)
{
if (unlikely(IS_PRIVATE(dentry->d_inode)))
return 0;
- return security_ops->inode_listxattr(dentry, mnt);
+ return security_ops->inode_listxattr(dentry, mnt, file);
}
int security_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt,
- const char *name)
+ const char *name, struct file *file)
{
if (unlikely(IS_PRIVATE(dentry->d_inode)))
return 0;
- return security_ops->inode_removexattr(dentry, mnt, name);
+ return security_ops->inode_removexattr(dentry, mnt, name, file);
}
int security_inode_need_killpriv(struct dentry *dentry)
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2715,7 +2715,7 @@ static int selinux_inode_setotherxattr(s
static int selinux_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
const char *name, const void *value,
- size_t size, int flags)
+ size_t size, int flags, struct file *file)
{
struct task_security_struct *tsec = current->security;
struct inode *inode = dentry->d_inode;
@@ -2797,18 +2797,20 @@ static void selinux_inode_post_setxattr(
}
static int selinux_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
- const char *name)
+ const char *name, struct file *file)
{
return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
}
-static int selinux_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt)
+static int selinux_inode_listxattr(struct dentry *dentry, struct vfsmount *mnt,
+ struct file *file)
{
return dentry_has_perm(current, NULL, dentry, FILE__GETATTR);
}
static int selinux_inode_removexattr(struct dentry *dentry,
- struct vfsmount *mnt, const char *name)
+ struct vfsmount *mnt, const char *name,
+ struct file *file)
{
if (strcmp(name, XATTR_NAME_SELINUX))
return selinux_inode_setotherxattr(dentry, name);
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -600,6 +600,7 @@ static int smack_inode_getattr(struct vf
* @value: unused
* @size: unused
* @flags: unused
+ * @file: unused
*
* This protects the Smack attribute explicitly.
*
@@ -607,7 +608,7 @@ static int smack_inode_getattr(struct vf
*/
static int smack_inode_setxattr(struct dentry *dentry, struct vfsmount *mnt,
const char *name, const void *value,
- size_t size, int flags)
+ size_t size, int flags, struct file *file)
{
int rc = 0;
@@ -619,7 +620,8 @@ static int smack_inode_setxattr(struct d
if (size == 0)
rc = -EINVAL;
} else
- rc = cap_inode_setxattr(dentry, mnt, name, value, size, flags);
+ rc = cap_inode_setxattr(dentry, mnt, name, value, size, flags,
+ file);
if (rc == 0)
rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE);
@@ -675,11 +677,12 @@ static void smack_inode_post_setxattr(st
* @dentry: the object
* @mnt: unused
* @name: unused
+ * @file: unused
*
* Returns 0 if access is permitted, an error code otherwise
*/
static int smack_inode_getxattr(struct dentry *dentry, struct vfsmount *mnt,
- const char *name)
+ const char *name, struct file *file)
{
return smk_curacc(smk_of_inode(dentry->d_inode), MAY_READ);
}
@@ -689,13 +692,14 @@ static int smack_inode_getxattr(struct d
* @dentry: the object
* @mnt: unused
* @name: name of the attribute
+ * @file: unused
*
* Removing the Smack attribute requires CAP_MAC_ADMIN
*
* Returns 0 if access is permitted, an error code otherwise
*/
static int smack_inode_removexattr(struct dentry *dentry, struct vfsmount *mnt,
- const char *name)
+ const char *name, struct file *file)
{
int rc = 0;
@@ -705,7 +709,7 @@ static int smack_inode_removexattr(struc
if (!capable(CAP_MAC_ADMIN))
rc = -EPERM;
} else
- rc = cap_inode_removexattr(dentry, mnt, name);
+ rc = cap_inode_removexattr(dentry, mnt, name, file);
if (rc == 0)
rc = smk_curacc(smk_of_inode(dentry->d_inode), MAY_WRITE);

View File

@@ -0,0 +1,111 @@
From: Andreas Gruenbacher <agruen@suse.de>
Subject: Factor out sysctl pathname code
Convert the selinux sysctl pathname computation code into a standalone
function.
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
Reviewed-by: James Morris <jmorris@namei.org>
---
include/linux/sysctl.h | 2 ++
kernel/sysctl.c | 27 +++++++++++++++++++++++++++
security/selinux/hooks.c | 34 +++++-----------------------------
3 files changed, 34 insertions(+), 29 deletions(-)
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -996,6 +996,8 @@ extern int proc_doulongvec_minmax(struct
extern int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int,
struct file *, void __user *, size_t *, loff_t *);
+extern char *sysctl_pathname(ctl_table *, char *, int);
+
extern int do_sysctl (int __user *name, int nlen,
void __user *oldval, size_t __user *oldlenp,
void __user *newval, size_t newlen);
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1554,6 +1554,33 @@ void register_sysctl_root(struct ctl_tab
spin_unlock(&sysctl_lock);
}
+char *sysctl_pathname(struct ctl_table *table, char *buffer, int buflen)
+{
+ if (buflen < 1)
+ return NULL;
+ buffer += --buflen;
+ *buffer = '\0';
+
+ while (table) {
+ int namelen = strlen(table->procname);
+
+ if (buflen < namelen + 1)
+ return NULL;
+ buflen -= namelen + 1;
+ buffer -= namelen;
+ memcpy(buffer, table->procname, namelen);
+ *--buffer = '/';
+ table = table->parent;
+ }
+ if (buflen < 4)
+ return NULL;
+ buffer -= 4;
+ memcpy(buffer, "/sys", 4);
+
+ return buffer;
+}
+EXPORT_SYMBOL_GPL(sysctl_pathname);
+
#ifdef CONFIG_SYSCTL_SYSCALL
/* Perform the actual read/write of a sysctl table entry. */
static int do_sysctl_strategy(struct ctl_table_root *root,
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -1811,40 +1811,16 @@ static int selinux_capable(struct task_s
static int selinux_sysctl_get_sid(ctl_table *table, u16 tclass, u32 *sid)
{
- int buflen, rc;
- char *buffer, *path, *end;
+ char *buffer, *path;
+ int rc = -ENOMEM;
- rc = -ENOMEM;
buffer = (char *)__get_free_page(GFP_KERNEL);
if (!buffer)
goto out;
- buflen = PAGE_SIZE;
- end = buffer+buflen;
- *--end = '\0';
- buflen--;
- path = end-1;
- *path = '/';
- while (table) {
- const char *name = table->procname;
- size_t namelen = strlen(name);
- buflen -= namelen + 1;
- if (buflen < 0)
- goto out_free;
- end -= namelen;
- memcpy(end, name, namelen);
- *--end = '/';
- path = end;
- table = table->parent;
- }
- buflen -= 4;
- if (buflen < 0)
- goto out_free;
- end -= 4;
- memcpy(end, "/sys", 4);
- path = end;
- rc = security_genfs_sid("proc", path, tclass, sid);
-out_free:
+ path = sysctl_pathname(table, buffer, PAGE_SIZE);
+ if (path)
+ rc = security_genfs_sid("proc", path, tclass, sid);
free_page((unsigned long)buffer);
out:
return rc;

View File

@@ -0,0 +1,267 @@
From: Andreas Gruenbacher <agruen@suse.de>
Subject: Fix __d_path() for lazy unmounts and make it unambiguous
First, when __d_path() hits a lazily unmounted mount point, it tries to prepend
the name of the lazily unmounted dentry to the path name. It gets this wrong,
and also overwrites the slash that separates the name from the following
pathname component. This patch fixes that; if a process was in directory
/foo/bar and /foo got lazily unmounted, the old result was ``foobar'' (note the
missing slash), while the new result with this patch is ``foo/bar''.
Second, it isn't always possible to tell from the __d_path() result whether the
specified root and rootmnt (i.e., the chroot) was reached. We need an
unambiguous result for AppArmor at least though, so we make sure that paths
will only start with a slash if the path leads all the way up to the root.
We also add a @fail_deleted argument, which allows to get rid of some of the
mess in sys_getcwd().
This patch leaves getcwd() and d_path() as they were before for everything
except for bind-mounted directories; for them, it reports ``/foo/bar'' instead
of ``foobar'' in the example described above.
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
Acked-by: Alan Cox <alan@lxorguk.ukuu.org.uk>
[ Moved dcache_lock outside vfsmount_lock to fix lock order (bnc#490902) ]
Signed-off-by: Nick Piggin <npiggin@suse.de>
---
fs/dcache.c | 126 +++++++++++++++++++++++++++----------------------
fs/seq_file.c | 4 -
include/linux/dcache.h | 5 +
3 files changed, 75 insertions(+), 60 deletions(-)
Index: linux-2.6.27/fs/dcache.c
===================================================================
--- linux-2.6.27.orig/fs/dcache.c
+++ linux-2.6.27/fs/dcache.c
@@ -1898,44 +1898,46 @@ static int prepend_name(char **buffer, i
* @root: root vfsmnt/dentry (may be modified by this function)
* @buffer: buffer to return value in
* @buflen: buffer length
+ * @flags: flags controling behavior of d_path
*
- * Convert a dentry into an ASCII path name. If the entry has been deleted
- * the string " (deleted)" is appended. Note that this is ambiguous.
- *
- * Returns the buffer or an error code if the path was too long.
- *
- * "buflen" should be positive. Caller holds the dcache_lock.
+ * Convert a dentry into an ASCII path name. If the entry has been deleted,
+ * then if @flags has D_PATH_FAIL_DELETED set, ERR_PTR(-ENOENT) is returned.
+ * Otherwise, the string " (deleted)" is appended. Note that this is ambiguous.
*
* If path is not reachable from the supplied root, then the value of
- * root is changed (without modifying refcounts).
+ * root is changed (without modifying refcounts). The path returned in this
+ * case will be relative (i.e., it will not start with a slash).
+ *
+ * Returns the buffer or an error code if the path was too long.
*/
char *__d_path(const struct path *path, struct path *root,
- char *buffer, int buflen)
+ char *buffer, int buflen, int flags)
{
struct dentry *dentry = path->dentry;
struct vfsmount *vfsmnt = path->mnt;
- char *end = buffer + buflen;
- char *retval;
+ const unsigned char *name;
+ int namelen;
+
+ buffer += buflen;
+ prepend(&buffer, &buflen, "\0", 1);
+ spin_lock(&dcache_lock);
spin_lock(&vfsmount_lock);
- prepend(&end, &buflen, "\0", 1);
- if (!IS_ROOT(dentry) && d_unhashed(dentry) &&
- (prepend(&end, &buflen, " (deleted)", 10) != 0))
+ if (!IS_ROOT(dentry) && d_unhashed(dentry)) {
+ if (flags & D_PATH_FAIL_DELETED) {
+ buffer = ERR_PTR(-ENOENT);
+ goto out;
+ }
+ if (prepend(&buffer, &buflen, " (deleted)", 10) != 0)
goto Elong;
-
+ }
if (buflen < 1)
goto Elong;
- /* Get '/' right */
- retval = end-1;
- *retval = '/';
- for (;;) {
+ while (dentry != root->dentry || vfsmnt != root->mnt) {
struct dentry * parent;
- if (dentry == root->dentry && vfsmnt == root->mnt)
- break;
if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
- /* Global root? */
if (vfsmnt->mnt_parent == vfsmnt) {
goto global_root;
}
@@ -1945,27 +1947,51 @@ char *__d_path(const struct path *path,
}
parent = dentry->d_parent;
prefetch(parent);
- if ((prepend_name(&end, &buflen, &dentry->d_name) != 0) ||
- (prepend(&end, &buflen, "/", 1) != 0))
+ if ((prepend_name(&buffer, &buflen, &dentry->d_name) != 0) ||
+ (prepend(&buffer, &buflen, "/", 1) != 0))
goto Elong;
- retval = end;
dentry = parent;
}
+ /* Get '/' right. */
+ if (*buffer != '/' && prepend(&buffer, &buflen, "/", 1))
+ goto Elong;
out:
spin_unlock(&vfsmount_lock);
- return retval;
+ spin_unlock(&dcache_lock);
+ return buffer;
global_root:
- retval += 1; /* hit the slash */
- if (prepend_name(&retval, &buflen, &dentry->d_name) != 0)
+ /*
+ * We went past the (vfsmount, dentry) we were looking for and have
+ * either hit a root dentry, a lazily unmounted dentry, an
+ * unconnected dentry, or the file is on a pseudo filesystem.
+ */
+ namelen = dentry->d_name.len;
+ name = dentry->d_name.name;
+
+ /*
+ * If this is a root dentry, then overwrite the slash. This
+ * will also DTRT with pseudo filesystems which have root
+ * dentries named "foo:".
+ */
+ if (IS_ROOT(dentry) && *buffer == '/') {
+ buffer++;
+ buflen++;
+ }
+ if ((flags & D_PATH_DISCONNECT) && *name == '/') {
+ /* Make sure we won't return a pathname starting with '/' */
+ name++;
+ namelen--;
+ }
+ if (prepend(&buffer, &buflen, name, namelen))
goto Elong;
root->mnt = vfsmnt;
root->dentry = dentry;
goto out;
Elong:
- retval = ERR_PTR(-ENAMETOOLONG);
+ buffer = ERR_PTR(-ENAMETOOLONG);
goto out;
}
@@ -2002,10 +2028,8 @@ char *d_path(const struct path *path, ch
root = current->fs->root;
path_get(&root);
read_unlock(&current->fs->lock);
- spin_lock(&dcache_lock);
tmp = root;
- res = __d_path(path, &tmp, buf, buflen);
- spin_unlock(&dcache_lock);
+ res = __d_path(path, &tmp, buf, buflen, 0);
path_put(&root);
return res;
}
@@ -2088,9 +2112,9 @@ Elong:
*/
SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size)
{
- int error;
- struct path pwd, root;
- char *page = (char *) __get_free_page(GFP_USER);
+ int error, len;
+ struct path pwd, root, tmp;
+ char *page = (char *) __get_free_page(GFP_USER), *cwd;
if (!page)
return -ENOMEM;
@@ -2102,30 +2126,20 @@ SYSCALL_DEFINE2(getcwd, char __user *, b
path_get(&root);
read_unlock(&current->fs->lock);
- error = -ENOENT;
- /* Has the current directory has been unlinked? */
- spin_lock(&dcache_lock);
- if (IS_ROOT(pwd.dentry) || !d_unhashed(pwd.dentry)) {
- unsigned long len;
- struct path tmp = root;
- char * cwd;
-
- cwd = __d_path(&pwd, &tmp, page, PAGE_SIZE);
- spin_unlock(&dcache_lock);
-
+ tmp = root;
+ cwd = __d_path(&pwd, &tmp, page, PAGE_SIZE, D_PATH_FAIL_DELETED);
+ if (IS_ERR(cwd)) {
error = PTR_ERR(cwd);
- if (IS_ERR(cwd))
- goto out;
+ goto out;
+ }
- error = -ERANGE;
- len = PAGE_SIZE + page - cwd;
- if (len <= size) {
- error = len;
- if (copy_to_user(buf, cwd, len))
- error = -EFAULT;
- }
- } else
- spin_unlock(&dcache_lock);
+ error = -ERANGE;
+ len = PAGE_SIZE + page - cwd;
+ if (len <= size) {
+ error = len;
+ if (copy_to_user(buf, cwd, len))
+ error = -EFAULT;
+ }
out:
path_put(&pwd);
Index: linux-2.6.27/fs/seq_file.c
===================================================================
--- linux-2.6.27.orig/fs/seq_file.c
+++ linux-2.6.27/fs/seq_file.c
@@ -441,9 +441,7 @@ int seq_path_root(struct seq_file *m, st
char *s = m->buf + m->count;
char *p;
- spin_lock(&dcache_lock);
- p = __d_path(path, root, s, m->size - m->count);
- spin_unlock(&dcache_lock);
+ p = __d_path(path, root, s, m->size - m->count, 0);
err = PTR_ERR(p);
if (!IS_ERR(p)) {
s = mangle_path(s, p, esc);
Index: linux-2.6.27/include/linux/dcache.h
===================================================================
--- linux-2.6.27.orig/include/linux/dcache.h
+++ linux-2.6.27/include/linux/dcache.h
@@ -299,9 +299,12 @@ extern int d_validate(struct dentry *, s
/*
* helper function for dentry_operations.d_dname() members
*/
+#define D_PATH_FAIL_DELETED 1
+#define D_PATH_DISCONNECT 2
extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...);
-extern char *__d_path(const struct path *path, struct path *root, char *, int);
+extern char *__d_path(const struct path *path, struct path *root, char *, int,
+ int);
extern char *d_path(const struct path *, char *, int);
extern char *dentry_path(struct dentry *, char *, int);

View File

@@ -0,0 +1,190 @@
From: Tony Jones <tonyj@suse.de>
Subject: Add a struct vfsmount parameter to vfs_getxattr()
The vfsmount will be passed down to the LSM hook so that LSMs can compute
pathnames.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/nfsd/nfs4xdr.c | 2 +-
fs/nfsd/vfs.c | 21 ++++++++++++---------
fs/xattr.c | 15 ++++++++-------
include/linux/nfsd/nfsd.h | 3 ++-
include/linux/xattr.h | 2 +-
5 files changed, 24 insertions(+), 19 deletions(-)
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -1446,7 +1446,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, s
}
if (bmval0 & (FATTR4_WORD0_ACL | FATTR4_WORD0_ACLSUPPORT
| FATTR4_WORD0_SUPPORTED_ATTRS)) {
- err = nfsd4_get_nfs4_acl(rqstp, dentry, &acl);
+ err = nfsd4_get_nfs4_acl(rqstp, dentry, exp->ex_path.mnt, &acl);
aclsupport = (err == 0);
if (bmval0 & FATTR4_WORD0_ACL) {
if (err == -EOPNOTSUPP)
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -420,11 +420,12 @@ out_nfserr:
#if defined(CONFIG_NFSD_V2_ACL) || \
defined(CONFIG_NFSD_V3_ACL) || \
defined(CONFIG_NFSD_V4)
-static ssize_t nfsd_getxattr(struct dentry *dentry, char *key, void **buf)
+static ssize_t nfsd_getxattr(struct dentry *dentry, struct vfsmount *mnt,
+ char *key, void **buf)
{
ssize_t buflen;
- buflen = vfs_getxattr(dentry, key, NULL, 0);
+ buflen = vfs_getxattr(dentry, mnt, key, NULL, 0);
if (buflen <= 0)
return buflen;
@@ -432,7 +433,7 @@ static ssize_t nfsd_getxattr(struct dent
if (!*buf)
return -ENOMEM;
- return vfs_getxattr(dentry, key, *buf, buflen);
+ return vfs_getxattr(dentry, mnt, key, *buf, buflen);
}
#endif
@@ -513,13 +514,13 @@ out_nfserr:
}
static struct posix_acl *
-_get_posix_acl(struct dentry *dentry, char *key)
+_get_posix_acl(struct dentry *dentry, struct vfsmount *mnt, char *key)
{
void *buf = NULL;
struct posix_acl *pacl = NULL;
int buflen;
- buflen = nfsd_getxattr(dentry, key, &buf);
+ buflen = nfsd_getxattr(dentry, mnt, key, &buf);
if (!buflen)
buflen = -ENODATA;
if (buflen <= 0)
@@ -531,14 +532,15 @@ _get_posix_acl(struct dentry *dentry, ch
}
int
-nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_acl **acl)
+nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
+ struct vfsmount *mnt, struct nfs4_acl **acl)
{
struct inode *inode = dentry->d_inode;
int error = 0;
struct posix_acl *pacl = NULL, *dpacl = NULL;
unsigned int flags = 0;
- pacl = _get_posix_acl(dentry, POSIX_ACL_XATTR_ACCESS);
+ pacl = _get_posix_acl(dentry, mnt, POSIX_ACL_XATTR_ACCESS);
if (IS_ERR(pacl) && PTR_ERR(pacl) == -ENODATA)
pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
if (IS_ERR(pacl)) {
@@ -548,7 +550,7 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqst
}
if (S_ISDIR(inode->i_mode)) {
- dpacl = _get_posix_acl(dentry, POSIX_ACL_XATTR_DEFAULT);
+ dpacl = _get_posix_acl(dentry, mnt, POSIX_ACL_XATTR_DEFAULT);
if (IS_ERR(dpacl) && PTR_ERR(dpacl) == -ENODATA)
dpacl = NULL;
else if (IS_ERR(dpacl)) {
@@ -2080,7 +2082,8 @@ nfsd_get_posix_acl(struct svc_fh *fhp, i
return ERR_PTR(-EOPNOTSUPP);
}
- size = nfsd_getxattr(fhp->fh_dentry, name, &value);
+ size = nfsd_getxattr(fhp->fh_dentry, fhp->fh_export->ex_path.mnt, name,
+ &value);
if (size < 0)
return ERR_PTR(size);
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -131,7 +131,8 @@ out_noalloc:
EXPORT_SYMBOL_GPL(xattr_getsecurity);
ssize_t
-vfs_getxattr(struct dentry *dentry, const char *name, void *value, size_t size)
+vfs_getxattr(struct dentry *dentry, struct vfsmount *mnt, const char *name,
+ void *value, size_t size)
{
struct inode *inode = dentry->d_inode;
int error;
@@ -314,8 +315,8 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, cons
* Extended attribute GET operations
*/
static ssize_t
-getxattr(struct dentry *d, const char __user *name, void __user *value,
- size_t size)
+getxattr(struct dentry *dentry, struct vfsmount *mnt, const char __user *name,
+ void __user *value, size_t size)
{
ssize_t error;
void *kvalue = NULL;
@@ -335,7 +336,7 @@ getxattr(struct dentry *d, const char __
return -ENOMEM;
}
- error = vfs_getxattr(d, kname, kvalue, size);
+ error = vfs_getxattr(dentry, mnt, kname, kvalue, size);
if (error > 0) {
if (size && copy_to_user(value, kvalue, error))
error = -EFAULT;
@@ -357,7 +358,7 @@ SYSCALL_DEFINE4(getxattr, const char __u
error = user_path(pathname, &path);
if (error)
return error;
- error = getxattr(path.dentry, name, value, size);
+ error = getxattr(path.dentry, path.mnt, name, value, size);
path_put(&path);
return error;
}
@@ -371,7 +372,7 @@ SYSCALL_DEFINE4(lgetxattr, const char __
error = user_lpath(pathname, &path);
if (error)
return error;
- error = getxattr(path.dentry, name, value, size);
+ error = getxattr(path.dentry, path.mnt, name, value, size);
path_put(&path);
return error;
}
@@ -386,7 +387,7 @@ SYSCALL_DEFINE4(fgetxattr, int, fd, cons
if (!f)
return error;
audit_inode(NULL, f->f_path.dentry);
- error = getxattr(f->f_path.dentry, name, value, size);
+ error = getxattr(f->f_path.dentry, f->f_path.mnt, name, value, size);
fput(f);
return error;
}
--- a/include/linux/nfsd/nfsd.h
+++ b/include/linux/nfsd/nfsd.h
@@ -85,7 +85,8 @@ __be32 nfsd_setattr(struct svc_rqst *,
#ifdef CONFIG_NFSD_V4
__be32 nfsd4_set_nfs4_acl(struct svc_rqst *, struct svc_fh *,
struct nfs4_acl *);
-int nfsd4_get_nfs4_acl(struct svc_rqst *, struct dentry *, struct nfs4_acl **);
+int nfsd4_get_nfs4_acl(struct svc_rqst *, struct dentry *,
+ struct vfsmount *mnt, struct nfs4_acl **);
#endif /* CONFIG_NFSD_V4 */
__be32 nfsd_create(struct svc_rqst *, struct svc_fh *,
char *name, int len, struct iattr *attrs,
--- a/include/linux/xattr.h
+++ b/include/linux/xattr.h
@@ -48,7 +48,7 @@ struct xattr_handler {
};
ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t);
-ssize_t vfs_getxattr(struct dentry *, const char *, void *, size_t);
+ssize_t vfs_getxattr(struct dentry *, struct vfsmount *, const char *, void *, size_t);
ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size);
int vfs_setxattr(struct dentry *, struct vfsmount *, const char *, const void *, size_t, int);
int vfs_removexattr(struct dentry *, const char *);

View File

@@ -0,0 +1,91 @@
From: Tony Jones <tonyj@suse.de>
Subject: Add struct vfsmount parameters to vfs_link()
The vfsmount will be passed down to the LSM hook so that LSMs can compute
pathnames.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/ecryptfs/inode.c | 9 +++++++--
fs/namei.c | 6 ++++--
fs/nfsd/vfs.c | 3 ++-
include/linux/fs.h | 2 +-
4 files changed, 14 insertions(+), 6 deletions(-)
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -403,19 +403,24 @@ static int ecryptfs_link(struct dentry *
struct dentry *new_dentry)
{
struct dentry *lower_old_dentry;
+ struct vfsmount *lower_old_mnt;
struct dentry *lower_new_dentry;
+ struct vfsmount *lower_new_mnt;
struct dentry *lower_dir_dentry;
u64 file_size_save;
int rc;
file_size_save = i_size_read(old_dentry->d_inode);
lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry);
+ lower_old_mnt = ecryptfs_dentry_to_lower_mnt(old_dentry);
lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry);
+ lower_new_mnt = ecryptfs_dentry_to_lower_mnt(new_dentry);
dget(lower_old_dentry);
dget(lower_new_dentry);
lower_dir_dentry = lock_parent(lower_new_dentry);
- rc = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode,
- lower_new_dentry);
+ rc = vfs_link(lower_old_dentry, lower_old_mnt,
+ lower_dir_dentry->d_inode, lower_new_dentry,
+ lower_new_mnt);
if (rc || !lower_new_dentry->d_inode)
goto out_lock;
rc = ecryptfs_interpose(lower_new_dentry, new_dentry, dir->i_sb, 0);
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2412,7 +2412,7 @@ SYSCALL_DEFINE2(symlink, const char __us
return sys_symlinkat(oldname, AT_FDCWD, newname);
}
-int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
+int vfs_link(struct dentry *old_dentry, struct vfsmount *old_mnt, struct inode *dir, struct dentry *new_dentry, struct vfsmount *new_mnt)
{
struct inode *inode = old_dentry->d_inode;
int error;
@@ -2490,7 +2490,9 @@ SYSCALL_DEFINE5(linkat, int, olddfd, con
error = mnt_want_write(nd.path.mnt);
if (error)
goto out_dput;
- error = vfs_link(old_path.dentry, nd.path.dentry->d_inode, new_dentry);
+ error = vfs_link(old_path.dentry, old_path.mnt,
+ nd.path.dentry->d_inode,
+ new_dentry, nd.path.mnt);
mnt_drop_write(nd.path.mnt);
out_dput:
dput(new_dentry);
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1650,7 +1650,8 @@ nfsd_link(struct svc_rqst *rqstp, struct
err = nfserrno(host_err);
goto out_dput;
}
- host_err = vfs_link(dold, dirp, dnew);
+ host_err = vfs_link(dold, tfhp->fh_export->ex_path.mnt, dirp,
+ dnew, ffhp->fh_export->ex_path.mnt);
if (!host_err) {
if (EX_ISSYNC(ffhp->fh_export)) {
err = nfserrno(nfsd_sync_dir(ddir));
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1181,7 +1181,7 @@ extern int vfs_create(struct inode *, st
extern int vfs_mkdir(struct inode *, struct dentry *, struct vfsmount *, int);
extern int vfs_mknod(struct inode *, struct dentry *, struct vfsmount *, int, dev_t);
extern int vfs_symlink(struct inode *, struct dentry *, struct vfsmount *, const char *);
-extern int vfs_link(struct dentry *, struct inode *, struct dentry *);
+extern int vfs_link(struct dentry *, struct vfsmount *, struct inode *, struct dentry *, struct vfsmount *);
extern int vfs_rmdir(struct inode *, struct dentry *);
extern int vfs_unlink(struct inode *, struct dentry *);
extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);

View File

@@ -0,0 +1,101 @@
From: Tony Jones <tonyj@suse.de>
Subject: Add a struct vfsmount parameter to vfs_listxattr()
The vfsmount will be passed down to the LSM hook so that LSMs can compute
pathnames.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/xattr.c | 25 ++++++++++++++-----------
include/linux/xattr.h | 2 +-
2 files changed, 15 insertions(+), 12 deletions(-)
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -168,18 +168,20 @@ nolsm:
EXPORT_SYMBOL_GPL(vfs_getxattr);
ssize_t
-vfs_listxattr(struct dentry *d, char *list, size_t size)
+vfs_listxattr(struct dentry *dentry, struct vfsmount *mnt, char *list,
+ size_t size)
{
+ struct inode *inode = dentry->d_inode;
ssize_t error;
- error = security_inode_listxattr(d);
+ error = security_inode_listxattr(dentry);
if (error)
return error;
error = -EOPNOTSUPP;
- if (d->d_inode->i_op && d->d_inode->i_op->listxattr) {
- error = d->d_inode->i_op->listxattr(d, list, size);
- } else {
- error = security_inode_listsecurity(d->d_inode, list, size);
+ if (inode->i_op && inode->i_op->listxattr)
+ error = inode->i_op->listxattr(dentry, list, size);
+ else {
+ error = security_inode_listsecurity(inode, list, size);
if (size && error > size)
error = -ERANGE;
}
@@ -396,7 +398,8 @@ SYSCALL_DEFINE4(fgetxattr, int, fd, cons
* Extended attribute LIST operations
*/
static ssize_t
-listxattr(struct dentry *d, char __user *list, size_t size)
+listxattr(struct dentry *dentry, struct vfsmount *mnt, char __user *list,
+ size_t size)
{
ssize_t error;
char *klist = NULL;
@@ -409,7 +412,7 @@ listxattr(struct dentry *d, char __user
return -ENOMEM;
}
- error = vfs_listxattr(d, klist, size);
+ error = vfs_listxattr(dentry, mnt, klist, size);
if (error > 0) {
if (size && copy_to_user(list, klist, error))
error = -EFAULT;
@@ -431,7 +434,7 @@ SYSCALL_DEFINE3(listxattr, const char __
error = user_path(pathname, &path);
if (error)
return error;
- error = listxattr(path.dentry, list, size);
+ error = listxattr(path.dentry, path.mnt, list, size);
path_put(&path);
return error;
}
@@ -445,7 +448,7 @@ SYSCALL_DEFINE3(llistxattr, const char _
error = user_lpath(pathname, &path);
if (error)
return error;
- error = listxattr(path.dentry, list, size);
+ error = listxattr(path.dentry, path.mnt, list, size);
path_put(&path);
return error;
}
@@ -459,7 +462,7 @@ SYSCALL_DEFINE3(flistxattr, int, fd, cha
if (!f)
return error;
audit_inode(NULL, f->f_path.dentry);
- error = listxattr(f->f_path.dentry, list, size);
+ error = listxattr(f->f_path.dentry, f->f_path.mnt, list, size);
fput(f);
return error;
}
--- a/include/linux/xattr.h
+++ b/include/linux/xattr.h
@@ -49,7 +49,7 @@ struct xattr_handler {
ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t);
ssize_t vfs_getxattr(struct dentry *, struct vfsmount *, const char *, void *, size_t);
-ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size);
+ssize_t vfs_listxattr(struct dentry *d, struct vfsmount *, char *list, size_t size);
int vfs_setxattr(struct dentry *, struct vfsmount *, const char *, const void *, size_t, int);
int vfs_removexattr(struct dentry *, const char *);

View File

@@ -0,0 +1,137 @@
From: Tony Jones <tonyj@suse.de>
Subject: Add struct vfsmount parameter to vfs_mkdir()
The vfsmount will be passed down to the LSM hook so that LSMs can compute
pathnames.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/ecryptfs/inode.c | 5 ++++-
fs/namei.c | 5 +++--
fs/nfsd/nfs4recover.c | 3 ++-
fs/nfsd/vfs.c | 8 +++++---
include/linux/fs.h | 2 +-
kernel/cgroup.c | 2 +-
6 files changed, 16 insertions(+), 9 deletions(-)
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -501,11 +501,14 @@ static int ecryptfs_mkdir(struct inode *
{
int rc;
struct dentry *lower_dentry;
+ struct vfsmount *lower_mnt;
struct dentry *lower_dir_dentry;
lower_dentry = ecryptfs_dentry_to_lower(dentry);
+ lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry);
lower_dir_dentry = lock_parent(lower_dentry);
- rc = vfs_mkdir(lower_dir_dentry->d_inode, lower_dentry, mode);
+ rc = vfs_mkdir(lower_dir_dentry->d_inode, lower_dentry, lower_mnt,
+ mode);
if (rc || !lower_dentry->d_inode)
goto out;
rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0);
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2077,7 +2077,8 @@ SYSCALL_DEFINE3(mknod, const char __user
return sys_mknodat(AT_FDCWD, filename, mode, dev);
}
-int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+int vfs_mkdir(struct inode *dir, struct dentry *dentry, struct vfsmount *mnt,
+ int mode)
{
int error = may_create(dir, dentry, 1);
@@ -2120,7 +2121,7 @@ SYSCALL_DEFINE3(mkdirat, int, dfd, const
error = mnt_want_write(nd.path.mnt);
if (error)
goto out_dput;
- error = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode);
+ error = vfs_mkdir(nd.path.dentry->d_inode, dentry, nd.path.mnt, mode);
mnt_drop_write(nd.path.mnt);
out_dput:
dput(dentry);
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -158,7 +158,8 @@ nfsd4_create_clid_dir(struct nfs4_client
status = mnt_want_write(rec_dir.path.mnt);
if (status)
goto out_put;
- status = vfs_mkdir(rec_dir.path.dentry->d_inode, dentry, S_IRWXU);
+ status = vfs_mkdir(rec_dir.path.dentry->d_inode, dentry,
+ rec_dir.path.mnt, S_IRWXU);
mnt_drop_write(rec_dir.path.mnt);
out_put:
dput(dentry);
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1215,6 +1215,7 @@ nfsd_create(struct svc_rqst *rqstp, stru
int type, dev_t rdev, struct svc_fh *resfhp)
{
struct dentry *dentry, *dchild = NULL;
+ struct svc_export *exp;
struct inode *dirp;
__be32 err;
__be32 err2;
@@ -1232,6 +1233,7 @@ nfsd_create(struct svc_rqst *rqstp, stru
goto out;
dentry = fhp->fh_dentry;
+ exp = fhp->fh_export;
dirp = dentry->d_inode;
err = nfserr_notdir;
@@ -1248,7 +1250,7 @@ nfsd_create(struct svc_rqst *rqstp, stru
host_err = PTR_ERR(dchild);
if (IS_ERR(dchild))
goto out_nfserr;
- err = fh_compose(resfhp, fhp->fh_export, dchild, fhp);
+ err = fh_compose(resfhp, exp, dchild, fhp);
if (err)
goto out;
} else {
@@ -1298,7 +1300,7 @@ nfsd_create(struct svc_rqst *rqstp, stru
host_err = vfs_create(dirp, dchild, iap->ia_mode, NULL);
break;
case S_IFDIR:
- host_err = vfs_mkdir(dirp, dchild, iap->ia_mode);
+ host_err = vfs_mkdir(dirp, dchild, exp->ex_path.mnt, iap->ia_mode);
break;
case S_IFCHR:
case S_IFBLK:
@@ -1312,7 +1314,7 @@ nfsd_create(struct svc_rqst *rqstp, stru
goto out_nfserr;
}
- if (EX_ISSYNC(fhp->fh_export)) {
+ if (EX_ISSYNC(exp)) {
err = nfserrno(nfsd_sync_dir(dentry));
write_inode_now(dchild->d_inode, 1);
}
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1178,7 +1178,7 @@ extern void unlock_super(struct super_bl
*/
extern int vfs_permission(struct nameidata *, int);
extern int vfs_create(struct inode *, struct dentry *, int, struct nameidata *);
-extern int vfs_mkdir(struct inode *, struct dentry *, int);
+extern int vfs_mkdir(struct inode *, struct dentry *, struct vfsmount *, int);
extern int vfs_mknod(struct inode *, struct dentry *, int, dev_t);
extern int vfs_symlink(struct inode *, struct dentry *, const char *);
extern int vfs_link(struct dentry *, struct inode *, struct dentry *);
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -2911,7 +2911,7 @@ int cgroup_clone(struct task_struct *tsk
}
/* Create the cgroup directory, which also creates the cgroup */
- ret = vfs_mkdir(inode, dentry, S_IFDIR | 0755);
+ ret = vfs_mkdir(inode, dentry, NULL, S_IFDIR | 0755);
child = __d_cgrp(dentry);
dput(dentry);
if (ret) {

View File

@@ -0,0 +1,99 @@
From: Tony Jones <tonyj@suse.de>
Subject: Add a struct vfsmount parameter to vfs_mknod()
The vfsmount will be passed down to the LSM hook so that LSMs can compute
pathnames.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/ecryptfs/inode.c | 5 ++++-
fs/namei.c | 10 ++++++----
fs/nfsd/vfs.c | 3 ++-
include/linux/fs.h | 2 +-
net/unix/af_unix.c | 3 ++-
5 files changed, 15 insertions(+), 8 deletions(-)
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -552,11 +552,14 @@ ecryptfs_mknod(struct inode *dir, struct
{
int rc;
struct dentry *lower_dentry;
+ struct vfsmount *lower_mnt;
struct dentry *lower_dir_dentry;
lower_dentry = ecryptfs_dentry_to_lower(dentry);
+ lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry);
lower_dir_dentry = lock_parent(lower_dentry);
- rc = vfs_mknod(lower_dir_dentry->d_inode, lower_dentry, mode, dev);
+ rc = vfs_mknod(lower_dir_dentry->d_inode, lower_dentry, lower_mnt, mode,
+ dev);
if (rc || !lower_dentry->d_inode)
goto out;
rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0);
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1976,7 +1976,8 @@ fail:
}
EXPORT_SYMBOL_GPL(lookup_create);
-int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
+int vfs_mknod(struct inode *dir, struct dentry *dentry, struct vfsmount *mnt,
+ int mode, dev_t dev)
{
int error = may_create(dir, dentry, 0);
@@ -2054,11 +2055,12 @@ SYSCALL_DEFINE4(mknodat, int, dfd, const
error = vfs_create(nd.path.dentry->d_inode,dentry,mode,&nd);
break;
case S_IFCHR: case S_IFBLK:
- error = vfs_mknod(nd.path.dentry->d_inode,dentry,mode,
- new_decode_dev(dev));
+ error = vfs_mknod(nd.path.dentry->d_inode, dentry,
+ nd.path, mode, new_decode_dev(dev));
break;
case S_IFIFO: case S_IFSOCK:
- error = vfs_mknod(nd.path.dentry->d_inode,dentry,mode,0);
+ error = vfs_mknod(nd.path.dentry->d_inode, dentry,
+ nd.path, mode, 0);
break;
}
mnt_drop_write(nd.path.mnt);
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1306,7 +1306,8 @@ nfsd_create(struct svc_rqst *rqstp, stru
case S_IFBLK:
case S_IFIFO:
case S_IFSOCK:
- host_err = vfs_mknod(dirp, dchild, iap->ia_mode, rdev);
+ host_err = vfs_mknod(dirp, dchild, exp->ex_path.mnt,
+ iap->ia_mode, rdev);
break;
}
if (host_err < 0) {
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1179,7 +1179,7 @@ extern void unlock_super(struct super_bl
extern int vfs_permission(struct nameidata *, int);
extern int vfs_create(struct inode *, struct dentry *, int, struct nameidata *);
extern int vfs_mkdir(struct inode *, struct dentry *, struct vfsmount *, int);
-extern int vfs_mknod(struct inode *, struct dentry *, int, dev_t);
+extern int vfs_mknod(struct inode *, struct dentry *, struct vfsmount *, int, dev_t);
extern int vfs_symlink(struct inode *, struct dentry *, const char *);
extern int vfs_link(struct dentry *, struct inode *, struct dentry *);
extern int vfs_rmdir(struct inode *, struct dentry *);
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -827,7 +827,8 @@ static int unix_bind(struct socket *sock
err = mnt_want_write(nd.path.mnt);
if (err)
goto out_mknod_dput;
- err = vfs_mknod(nd.path.dentry->d_inode, dentry, mode, 0);
+ err = vfs_mknod(nd.path.dentry->d_inode, dentry, nd.path.mnt,
+ mode, 0);
mnt_drop_write(nd.path.mnt);
if (err)
goto out_mknod_dput;

View File

@@ -0,0 +1,291 @@
From: Tony Jones <tonyj@suse.de>
Subject: Add a vfsmount parameter to notify_change()
The vfsmount parameter must be set appropriately for files visibile
outside the kernel. Files that are only used in a filesystem (e.g.,
reiserfs xattr files) will have a NULL vfsmount.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/attr.c | 3 ++-
fs/ecryptfs/inode.c | 4 +++-
fs/exec.c | 3 ++-
fs/hpfs/namei.c | 2 +-
fs/namei.c | 2 +-
fs/nfsd/vfs.c | 8 ++++----
fs/open.c | 28 +++++++++++++++-------------
fs/utimes.c | 2 +-
include/linux/fs.h | 6 +++---
mm/filemap.c | 2 +-
10 files changed, 33 insertions(+), 27 deletions(-)
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -100,7 +100,8 @@ int inode_setattr(struct inode * inode,
}
EXPORT_SYMBOL(inode_setattr);
-int notify_change(struct dentry * dentry, struct iattr * attr)
+int notify_change(struct dentry *dentry, struct vfsmount *mnt,
+ struct iattr *attr)
{
struct inode *inode = dentry->d_inode;
mode_t mode = inode->i_mode;
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -850,6 +850,7 @@ static int ecryptfs_setattr(struct dentr
{
int rc = 0;
struct dentry *lower_dentry;
+ struct vfsmount *lower_mnt;
struct inode *inode;
struct inode *lower_inode;
struct ecryptfs_crypt_stat *crypt_stat;
@@ -860,6 +861,7 @@ static int ecryptfs_setattr(struct dentr
inode = dentry->d_inode;
lower_inode = ecryptfs_inode_to_lower(inode);
lower_dentry = ecryptfs_dentry_to_lower(dentry);
+ lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry);
mutex_lock(&crypt_stat->cs_mutex);
if (S_ISDIR(dentry->d_inode->i_mode))
crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);
@@ -911,7 +913,7 @@ static int ecryptfs_setattr(struct dentr
ia->ia_valid &= ~ATTR_MODE;
mutex_lock(&lower_dentry->d_inode->i_mutex);
- rc = notify_change(lower_dentry, ia);
+ rc = notify_change(lower_dentry, lower_mnt, ia);
mutex_unlock(&lower_dentry->d_inode->i_mutex);
out:
fsstack_copy_attr_all(inode, lower_inode, NULL);
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1841,7 +1841,8 @@ int do_coredump(long signr, int exit_cod
goto close_fail;
if (!file->f_op->write)
goto close_fail;
- if (!ispipe && do_truncate(file->f_path.dentry, 0, 0, file) != 0)
+ if (!ispipe &&
+ do_truncate(file->f_path.dentry, file->f_path.mnt, 0, 0, file) != 0)
goto close_fail;
retval = binfmt->core_dump(signr, regs, file, core_limit);
--- a/fs/hpfs/namei.c
+++ b/fs/hpfs/namei.c
@@ -426,7 +426,7 @@ again:
/*printk("HPFS: truncating file before delete.\n");*/
newattrs.ia_size = 0;
newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
- err = notify_change(dentry, &newattrs);
+ err = notify_change(dentry, NULL, &newattrs);
put_write_access(inode);
if (!err)
goto again;
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1619,7 +1619,7 @@ int may_open(struct nameidata *nd, int a
if (!error) {
DQUOT_INIT(inode);
- error = do_truncate(dentry, 0,
+ error = do_truncate(dentry, nd->path.mnt, 0,
ATTR_MTIME|ATTR_CTIME|ATTR_OPEN,
NULL);
}
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -397,7 +397,7 @@ nfsd_setattr(struct svc_rqst *rqstp, str
err = nfserr_notsync;
if (!check_guard || guardtime == inode->i_ctime.tv_sec) {
fh_lock(fhp);
- host_err = notify_change(dentry, iap);
+ host_err = notify_change(dentry, fhp->fh_export->ex_path.mnt, iap);
/* to get NFSERR_JUKEBOX on the wire, need -ETIMEDOUT */
if (host_err == -EAGAIN)
host_err = -ETIMEDOUT;
@@ -964,13 +964,13 @@ out:
return err;
}
-static void kill_suid(struct dentry *dentry)
+static void kill_suid(struct dentry *dentry, struct vfsmount *mnt)
{
struct iattr ia;
ia.ia_valid = ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
mutex_lock(&dentry->d_inode->i_mutex);
- notify_change(dentry, &ia);
+ notify_change(dentry, mnt, &ia);
mutex_unlock(&dentry->d_inode->i_mutex);
}
@@ -1033,7 +1033,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, s
/* clear setuid/setgid flag after write */
if (host_err >= 0 && (inode->i_mode & (S_ISUID | S_ISGID)))
- kill_suid(dentry);
+ kill_suid(dentry, exp->ex_path.mnt);
if (host_err >= 0 && stable) {
static ino_t last_ino;
--- a/fs/open.c
+++ b/fs/open.c
@@ -195,8 +195,8 @@ out:
return error;
}
-int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
- struct file *filp)
+int do_truncate(struct dentry *dentry, struct vfsmount *mnt, loff_t length,
+ unsigned int time_attrs, struct file *filp)
{
int err;
struct iattr newattrs;
@@ -216,7 +216,7 @@ int do_truncate(struct dentry *dentry, l
newattrs.ia_valid |= should_remove_suid(dentry);
mutex_lock(&dentry->d_inode->i_mutex);
- err = notify_change(dentry, &newattrs);
+ err = notify_change(dentry, mnt, &newattrs);
mutex_unlock(&dentry->d_inode->i_mutex);
return err;
}
@@ -272,7 +272,7 @@ static long do_sys_truncate(const char _
error = locks_verify_truncate(inode, NULL, length);
if (!error) {
DQUOT_INIT(inode);
- error = do_truncate(path.dentry, length, 0, NULL);
+ error = do_truncate(path.dentry, path.mnt, length, 0, NULL);
}
put_write_and_out:
@@ -327,7 +327,8 @@ static long do_sys_ftruncate(unsigned in
error = locks_verify_truncate(inode, file, length);
if (!error)
- error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, file);
+ error = do_truncate(dentry, file->f_path.mnt, length,
+ ATTR_MTIME|ATTR_CTIME, file);
out_putf:
fput(file);
out:
@@ -624,7 +625,7 @@ SYSCALL_DEFINE2(fchmod, unsigned int, fd
mode = inode->i_mode;
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
- err = notify_change(dentry, &newattrs);
+ err = notify_change(dentry, file->f_path.mnt, &newattrs);
mutex_unlock(&inode->i_mutex);
mnt_drop_write(file->f_path.mnt);
out_putf:
@@ -653,7 +654,7 @@ SYSCALL_DEFINE3(fchmodat, int, dfd, cons
mode = inode->i_mode;
newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
- error = notify_change(path.dentry, &newattrs);
+ error = notify_change(path.dentry, path.mnt, &newattrs);
mutex_unlock(&inode->i_mutex);
mnt_drop_write(path.mnt);
dput_and_out:
@@ -667,7 +668,8 @@ SYSCALL_DEFINE2(chmod, const char __user
return sys_fchmodat(AT_FDCWD, filename, mode);
}
-static int chown_common(struct dentry * dentry, uid_t user, gid_t group)
+static int chown_common(struct dentry * dentry, struct vfsmount *mnt,
+ uid_t user, gid_t group)
{
struct inode *inode = dentry->d_inode;
int error;
@@ -686,7 +688,7 @@ static int chown_common(struct dentry *
newattrs.ia_valid |=
ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
mutex_lock(&inode->i_mutex);
- error = notify_change(dentry, &newattrs);
+ error = notify_change(dentry, mnt, &newattrs);
mutex_unlock(&inode->i_mutex);
return error;
@@ -703,7 +705,7 @@ SYSCALL_DEFINE3(chown, const char __user
error = mnt_want_write(path.mnt);
if (error)
goto out_release;
- error = chown_common(path.dentry, user, group);
+ error = chown_common(path.dentry, path.mnt, user, group);
mnt_drop_write(path.mnt);
out_release:
path_put(&path);
@@ -728,7 +730,7 @@ SYSCALL_DEFINE5(fchownat, int, dfd, cons
error = mnt_want_write(path.mnt);
if (error)
goto out_release;
- error = chown_common(path.dentry, user, group);
+ error = chown_common(path.dentry, path.mnt, user, group);
mnt_drop_write(path.mnt);
out_release:
path_put(&path);
@@ -747,7 +749,7 @@ SYSCALL_DEFINE3(lchown, const char __use
error = mnt_want_write(path.mnt);
if (error)
goto out_release;
- error = chown_common(path.dentry, user, group);
+ error = chown_common(path.dentry, path.mnt, user, group);
mnt_drop_write(path.mnt);
out_release:
path_put(&path);
@@ -770,7 +772,7 @@ SYSCALL_DEFINE3(fchown, unsigned int, fd
goto out_fput;
dentry = file->f_path.dentry;
audit_inode(NULL, dentry);
- error = chown_common(dentry, user, group);
+ error = chown_common(dentry, file->f_path.mnt, user, group);
mnt_drop_write(file->f_path.mnt);
out_fput:
fput(file);
--- a/fs/utimes.c
+++ b/fs/utimes.c
@@ -102,7 +102,7 @@ static int utimes_common(struct path *pa
}
}
mutex_lock(&inode->i_mutex);
- error = notify_change(path->dentry, &newattrs);
+ error = notify_change(path->dentry, path->mnt, &newattrs);
mutex_unlock(&inode->i_mutex);
mnt_drop_write_and_out:
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1636,8 +1636,8 @@ static inline int break_lease(struct ino
/* fs/open.c */
-extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs,
- struct file *filp);
+extern int do_truncate(struct dentry *, struct vfsmount *, loff_t start,
+ unsigned int time_attrs, struct file *filp);
extern long do_sys_open(int dfd, const char __user *filename, int flags,
int mode);
extern struct file *filp_open(const char *, int, int);
@@ -1798,7 +1798,7 @@ extern int do_remount_sb(struct super_bl
#ifdef CONFIG_BLOCK
extern sector_t bmap(struct inode *, sector_t);
#endif
-extern int notify_change(struct dentry *, struct iattr *);
+extern int notify_change(struct dentry *, struct vfsmount *, struct iattr *);
extern int inode_permission(struct inode *, int);
extern int generic_permission(struct inode *, int,
int (*check_acl)(struct inode *, int));
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -1831,7 +1831,7 @@ static int __remove_suid(struct path *pa
struct iattr newattrs;
newattrs.ia_valid = ATTR_FORCE | kill;
- return notify_change(path->dentry, &newattrs);
+ return notify_change(path->dentry, path->mnt, &newattrs);
}
int file_remove_suid(struct file *file)

View File

@@ -0,0 +1,121 @@
From: Tony Jones <tonyj@suse.de>
Subject: Add a struct vfsmount parameter to vfs_removexattr()
The vfsmount will be passed down to the LSM hook so that LSMs can compute
pathnames.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/nfsd/vfs.c | 11 ++++++-----
fs/xattr.c | 12 ++++++------
include/linux/xattr.h | 2 +-
3 files changed, 13 insertions(+), 12 deletions(-)
--- linux-2.6.27.orig/fs/nfsd/vfs.c
+++ linux-2.6.27/fs/nfsd/vfs.c
@@ -2095,6 +2095,7 @@ nfsd_get_posix_acl(struct svc_fh *fhp, i
int
nfsd_set_posix_acl(struct svc_fh *fhp, int type, struct posix_acl *acl)
{
+ struct vfsmount *mnt;
struct inode *inode = fhp->fh_dentry->d_inode;
char *name;
void *value = NULL;
@@ -2127,22 +2128,22 @@ nfsd_set_posix_acl(struct svc_fh *fhp, i
} else
size = 0;
- error = mnt_want_write(fhp->fh_export->ex_path.mnt);
+ mnt = fhp->fh_export->ex_path.mnt;
+ error = mnt_want_write(mnt);
if (error)
goto getout;
if (size)
- error = vfs_setxattr(fhp->fh_dentry, fhp->fh_export->ex_path.mnt,
- name, value, size,0);
+ error = vfs_setxattr(fhp->fh_dentry, mnt, name, value, size,0);
else {
if (!S_ISDIR(inode->i_mode) && type == ACL_TYPE_DEFAULT)
error = 0;
else {
- error = vfs_removexattr(fhp->fh_dentry, name);
+ error = vfs_removexattr(fhp->fh_dentry, mnt, name);
if (error == -ENODATA)
error = 0;
}
}
- mnt_drop_write(fhp->fh_export->ex_path.mnt);
+ mnt_drop_write(mnt);
getout:
kfree(value);
--- linux-2.6.27.orig/fs/xattr.c
+++ linux-2.6.27/fs/xattr.c
@@ -190,7 +190,7 @@ vfs_listxattr(struct dentry *dentry, str
EXPORT_SYMBOL_GPL(vfs_listxattr);
int
-vfs_removexattr(struct dentry *dentry, const char *name)
+vfs_removexattr(struct dentry *dentry, struct vfsmount *mnt, const char *name)
{
struct inode *inode = dentry->d_inode;
int error;
@@ -471,7 +471,7 @@ SYSCALL_DEFINE3(flistxattr, int, fd, cha
* Extended attribute REMOVE operations
*/
static long
-removexattr(struct dentry *d, const char __user *name)
+removexattr(struct dentry *dentry, struct vfsmount *mnt, const char __user *name)
{
int error;
char kname[XATTR_NAME_MAX + 1];
@@ -482,7 +482,7 @@ removexattr(struct dentry *d, const char
if (error < 0)
return error;
- return vfs_removexattr(d, kname);
+ return vfs_removexattr(dentry, mnt, kname);
}
SYSCALL_DEFINE2(removexattr, const char __user *, pathname,
@@ -496,7 +496,7 @@ SYSCALL_DEFINE2(removexattr, const char
return error;
error = mnt_want_write(path.mnt);
if (!error) {
- error = removexattr(path.dentry, name);
+ error = removexattr(path.dentry, path.mnt, name);
mnt_drop_write(path.mnt);
}
path_put(&path);
@@ -514,7 +514,7 @@ SYSCALL_DEFINE2(lremovexattr, const char
return error;
error = mnt_want_write(path.mnt);
if (!error) {
- error = removexattr(path.dentry, name);
+ error = removexattr(path.dentry, path.mnt, name);
mnt_drop_write(path.mnt);
}
path_put(&path);
@@ -534,7 +534,7 @@ SYSCALL_DEFINE2(fremovexattr, int, fd, c
audit_inode(NULL, dentry);
error = mnt_want_write_file(f->f_path.mnt, f);
if (!error) {
- error = removexattr(dentry, name);
+ error = removexattr(dentry, f->f_path.mnt, name);
mnt_drop_write(f->f_path.mnt);
}
fput(f);
--- linux-2.6.27.orig/include/linux/xattr.h
+++ linux-2.6.27/include/linux/xattr.h
@@ -51,7 +51,7 @@ ssize_t xattr_getsecurity(struct inode *
ssize_t vfs_getxattr(struct dentry *, struct vfsmount *, const char *, void *, size_t);
ssize_t vfs_listxattr(struct dentry *d, struct vfsmount *, char *list, size_t size);
int vfs_setxattr(struct dentry *, struct vfsmount *, const char *, const void *, size_t, int);
-int vfs_removexattr(struct dentry *, const char *);
+int vfs_removexattr(struct dentry *, struct vfsmount *mnt, const char *);
ssize_t generic_getxattr(struct dentry *dentry, const char *name, void *buffer, size_t size);
ssize_t generic_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size);

View File

@@ -0,0 +1,125 @@
From: Tony Jones <tonyj@suse.de>
Subject: Add struct vfsmount parameters to vfs_rename()
The vfsmount will be passed down to the LSM hook so that LSMs can compute
pathnames.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/ecryptfs/inode.c | 7 ++++++-
fs/namei.c | 19 ++++++++++++-------
fs/nfsd/vfs.c | 3 ++-
include/linux/fs.h | 2 +-
4 files changed, 21 insertions(+), 10 deletions(-)
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -590,19 +590,24 @@ ecryptfs_rename(struct inode *old_dir, s
{
int rc;
struct dentry *lower_old_dentry;
+ struct vfsmount *lower_old_mnt;
struct dentry *lower_new_dentry;
+ struct vfsmount *lower_new_mnt;
struct dentry *lower_old_dir_dentry;
struct dentry *lower_new_dir_dentry;
lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry);
+ lower_old_mnt = ecryptfs_dentry_to_lower_mnt(old_dentry);
lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry);
+ lower_new_mnt = ecryptfs_dentry_to_lower_mnt(new_dentry);
dget(lower_old_dentry);
dget(lower_new_dentry);
lower_old_dir_dentry = dget_parent(lower_old_dentry);
lower_new_dir_dentry = dget_parent(lower_new_dentry);
lock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
rc = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry,
- lower_new_dir_dentry->d_inode, lower_new_dentry);
+ lower_old_mnt, lower_new_dir_dentry->d_inode,
+ lower_new_dentry, lower_new_mnt);
if (rc)
goto out_lock;
fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode, NULL);
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2547,7 +2547,8 @@ SYSCALL_DEFINE2(link, const char __user
* locking].
*/
static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry)
+ struct vfsmount *old_mnt, struct inode *new_dir,
+ struct dentry *new_dentry, struct vfsmount *new_mnt)
{
int error = 0;
struct inode *target;
@@ -2590,7 +2591,8 @@ static int vfs_rename_dir(struct inode *
}
static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry)
+ struct vfsmount *old_mnt, struct inode *new_dir,
+ struct dentry *new_dentry, struct vfsmount *new_mnt)
{
struct inode *target;
int error;
@@ -2618,7 +2620,8 @@ static int vfs_rename_other(struct inode
}
int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry)
+ struct vfsmount *old_mnt, struct inode *new_dir,
+ struct dentry *new_dentry, struct vfsmount *new_mnt)
{
int error;
int is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
@@ -2647,9 +2650,11 @@ int vfs_rename(struct inode *old_dir, st
old_name = fsnotify_oldname_init(old_dentry->d_name.name);
if (is_dir)
- error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
+ error = vfs_rename_dir(old_dir, old_dentry, old_mnt,
+ new_dir, new_dentry, new_mnt);
else
- error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry);
+ error = vfs_rename_other(old_dir, old_dentry, old_mnt,
+ new_dir, new_dentry, new_mnt);
if (!error) {
const char *new_name = old_dentry->d_name.name;
fsnotify_move(old_dir, new_dir, old_name, new_name, is_dir,
@@ -2726,8 +2731,8 @@ SYSCALL_DEFINE4(renameat, int, olddfd, c
error = mnt_want_write(oldnd.path.mnt);
if (error)
goto exit5;
- error = vfs_rename(old_dir->d_inode, old_dentry,
- new_dir->d_inode, new_dentry);
+ error = vfs_rename(old_dir->d_inode, old_dentry, oldnd.path.mnt,
+ new_dir->d_inode, new_dentry, newnd.path.mnt);
mnt_drop_write(oldnd.path.mnt);
exit5:
dput(new_dentry);
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1752,7 +1752,8 @@ nfsd_rename(struct svc_rqst *rqstp, stru
if (host_err)
goto out_dput_new;
- host_err = vfs_rename(fdir, odentry, tdir, ndentry);
+ host_err = vfs_rename(fdir, odentry, ffhp->fh_export->ex_path.mnt,
+ tdir, ndentry, tfhp->fh_export->ex_path.mnt);
if (!host_err && EX_ISSYNC(tfhp->fh_export)) {
host_err = nfsd_sync_dir(tdentry);
if (!host_err)
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1184,7 +1184,7 @@ extern int vfs_symlink(struct inode *, s
extern int vfs_link(struct dentry *, struct vfsmount *, struct inode *, struct dentry *, struct vfsmount *);
extern int vfs_rmdir(struct inode *, struct dentry *, struct vfsmount *);
extern int vfs_unlink(struct inode *, struct dentry *, struct vfsmount *);
-extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
+extern int vfs_rename(struct inode *, struct dentry *, struct vfsmount *, struct inode *, struct dentry *, struct vfsmount *);
/*
* VFS dentry helper functions.

View File

@@ -0,0 +1,123 @@
From: Tony Jones <tonyj@suse.de>
Subject: Add a struct vfsmount parameter to vfs_rmdir()
The vfsmount will be passed down to the LSM hook so that LSMs can compute
pathnames.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/ecryptfs/inode.c | 4 +++-
fs/namei.c | 4 ++--
fs/nfsd/nfs4recover.c | 2 +-
fs/nfsd/vfs.c | 8 +++++---
include/linux/fs.h | 2 +-
5 files changed, 12 insertions(+), 8 deletions(-)
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -534,14 +534,16 @@ out:
static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry)
{
struct dentry *lower_dentry;
+ struct vfsmount *lower_mnt;
struct dentry *lower_dir_dentry;
int rc;
lower_dentry = ecryptfs_dentry_to_lower(dentry);
+ lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry);
dget(dentry);
lower_dir_dentry = lock_parent(lower_dentry);
dget(lower_dentry);
- rc = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry);
+ rc = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry, lower_mnt);
dput(lower_dentry);
if (!rc)
d_delete(lower_dentry);
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2167,7 +2167,7 @@ void dentry_unhash(struct dentry *dentry
spin_unlock(&dcache_lock);
}
-int vfs_rmdir(struct inode *dir, struct dentry *dentry)
+int vfs_rmdir(struct inode *dir, struct dentry *dentry,struct vfsmount *mnt)
{
int error = may_delete(dir, dentry, 1);
@@ -2230,7 +2230,7 @@ static long do_rmdir(int dfd, const char
error = mnt_want_write(nd.path.mnt);
if (error)
goto exit3;
- error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
+ error = vfs_rmdir(nd.path.dentry->d_inode, dentry, nd.path.mnt);
mnt_drop_write(nd.path.mnt);
exit3:
dput(dentry);
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -279,7 +279,7 @@ nfsd4_clear_clid_dir(struct dentry *dir,
* a kernel from the future.... */
nfsd4_list_rec_dir(dentry, nfsd4_remove_clid_file);
mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
- status = vfs_rmdir(dir->d_inode, dentry);
+ status = vfs_rmdir(dir->d_inode, dentry, rec_dir.path.mnt);
mutex_unlock(&dir->d_inode->i_mutex);
return status;
}
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1790,6 +1790,7 @@ nfsd_unlink(struct svc_rqst *rqstp, stru
char *fname, int flen)
{
struct dentry *dentry, *rdentry;
+ struct svc_export *exp;
struct inode *dirp;
__be32 err;
int host_err;
@@ -1804,6 +1805,7 @@ nfsd_unlink(struct svc_rqst *rqstp, stru
fh_lock_nested(fhp, I_MUTEX_PARENT);
dentry = fhp->fh_dentry;
dirp = dentry->d_inode;
+ exp = fhp->fh_export;
rdentry = lookup_one_len(fname, dentry, flen);
host_err = PTR_ERR(rdentry);
@@ -1825,21 +1827,21 @@ nfsd_unlink(struct svc_rqst *rqstp, stru
if (type != S_IFDIR) { /* It's UNLINK */
#ifdef MSNFS
- if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) &&
+ if ((exp->ex_flags & NFSEXP_MSNFS) &&
(atomic_read(&rdentry->d_count) > 1)) {
host_err = -EPERM;
} else
#endif
host_err = vfs_unlink(dirp, rdentry);
} else { /* It's RMDIR */
- host_err = vfs_rmdir(dirp, rdentry);
+ host_err = vfs_rmdir(dirp, rdentry, exp->ex_path.mnt);
}
dput(rdentry);
if (host_err)
goto out_drop;
- if (EX_ISSYNC(fhp->fh_export))
+ if (EX_ISSYNC(exp))
host_err = nfsd_sync_dir(dentry);
out_drop:
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1182,7 +1182,7 @@ extern int vfs_mkdir(struct inode *, str
extern int vfs_mknod(struct inode *, struct dentry *, struct vfsmount *, int, dev_t);
extern int vfs_symlink(struct inode *, struct dentry *, struct vfsmount *, const char *);
extern int vfs_link(struct dentry *, struct vfsmount *, struct inode *, struct dentry *, struct vfsmount *);
-extern int vfs_rmdir(struct inode *, struct dentry *);
+extern int vfs_rmdir(struct inode *, struct dentry *, struct vfsmount *);
extern int vfs_unlink(struct inode *, struct dentry *);
extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);

View File

@@ -0,0 +1,159 @@
From: Tony Jones <tonyj@suse.de>
Subject: Add a struct vfsmount parameter to vfs_setxattr()
The vfsmount will be passed down to the LSM hook so that LSMs can compute
pathnames.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/nfsd/vfs.c | 16 +++++++++++-----
fs/xattr.c | 16 ++++++++--------
include/linux/xattr.h | 3 ++-
3 files changed, 21 insertions(+), 14 deletions(-)
--- linux-2.6.27.orig/fs/nfsd/vfs.c
+++ linux-2.6.27/fs/nfsd/vfs.c
@@ -438,7 +438,8 @@ static ssize_t nfsd_getxattr(struct dent
#if defined(CONFIG_NFSD_V4)
static int
-set_nfsv4_acl_one(struct dentry *dentry, struct posix_acl *pacl, char *key)
+set_nfsv4_acl_one(struct dentry *dentry, struct vfsmount *mnt,
+ struct posix_acl *pacl, char *key)
{
int len;
size_t buflen;
@@ -457,7 +458,7 @@ set_nfsv4_acl_one(struct dentry *dentry,
goto out;
}
- error = vfs_setxattr(dentry, key, buf, len, 0);
+ error = vfs_setxattr(dentry, mnt, key, buf, len, 0);
out:
kfree(buf);
return error;
@@ -470,6 +471,7 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqst
__be32 error;
int host_error;
struct dentry *dentry;
+ struct vfsmount *mnt;
struct inode *inode;
struct posix_acl *pacl = NULL, *dpacl = NULL;
unsigned int flags = 0;
@@ -480,6 +482,7 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqst
return error;
dentry = fhp->fh_dentry;
+ mnt = fhp->fh_export->ex_path.mnt;
inode = dentry->d_inode;
if (S_ISDIR(inode->i_mode))
flags = NFS4_ACL_DIR;
@@ -490,12 +493,14 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqst
} else if (host_error < 0)
goto out_nfserr;
- host_error = set_nfsv4_acl_one(dentry, pacl, POSIX_ACL_XATTR_ACCESS);
+ host_error = set_nfsv4_acl_one(dentry, mnt, pacl,
+ POSIX_ACL_XATTR_ACCESS);
if (host_error < 0)
goto out_release;
if (S_ISDIR(inode->i_mode))
- host_error = set_nfsv4_acl_one(dentry, dpacl, POSIX_ACL_XATTR_DEFAULT);
+ host_error = set_nfsv4_acl_one(dentry, mnt, dpacl,
+ POSIX_ACL_XATTR_DEFAULT);
out_release:
posix_acl_release(pacl);
@@ -2123,7 +2128,8 @@ nfsd_set_posix_acl(struct svc_fh *fhp, i
if (error)
goto getout;
if (size)
- error = vfs_setxattr(fhp->fh_dentry, name, value, size, 0);
+ error = vfs_setxattr(fhp->fh_dentry, fhp->fh_export->ex_path.mnt,
+ name, value, size,0);
else {
if (!S_ISDIR(inode->i_mode) && type == ACL_TYPE_DEFAULT)
error = 0;
--- linux-2.6.27.orig/fs/xattr.c
+++ linux-2.6.27/fs/xattr.c
@@ -67,8 +67,8 @@ xattr_permission(struct inode *inode, co
}
int
-vfs_setxattr(struct dentry *dentry, const char *name, const void *value,
- size_t size, int flags)
+vfs_setxattr(struct dentry *dentry, struct vfsmount *mnt, const char *name,
+ const void *value, size_t size, int flags)
{
struct inode *inode = dentry->d_inode;
int error;
@@ -218,8 +218,8 @@ EXPORT_SYMBOL_GPL(vfs_removexattr);
* Extended attribute SET operations
*/
static long
-setxattr(struct dentry *d, const char __user *name, const void __user *value,
- size_t size, int flags)
+setxattr(struct dentry *dentry, struct vfsmount *mnt, const char __user *name,
+ const void __user *value, size_t size, int flags)
{
int error;
void *kvalue = NULL;
@@ -246,7 +246,7 @@ setxattr(struct dentry *d, const char __
}
}
- error = vfs_setxattr(d, kname, kvalue, size, flags);
+ error = vfs_setxattr(dentry, mnt, kname, kvalue, size, flags);
kfree(kvalue);
return error;
}
@@ -263,7 +263,7 @@ SYSCALL_DEFINE5(setxattr, const char __u
return error;
error = mnt_want_write(path.mnt);
if (!error) {
- error = setxattr(path.dentry, name, value, size, flags);
+ error = setxattr(path.dentry, path.mnt, name, value, size, flags);
mnt_drop_write(path.mnt);
}
path_put(&path);
@@ -282,7 +282,7 @@ SYSCALL_DEFINE5(lsetxattr, const char __
return error;
error = mnt_want_write(path.mnt);
if (!error) {
- error = setxattr(path.dentry, name, value, size, flags);
+ error = setxattr(path.dentry, path.mnt, name, value, size, flags);
mnt_drop_write(path.mnt);
}
path_put(&path);
@@ -303,7 +303,7 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, cons
audit_inode(NULL, dentry);
error = mnt_want_write_file(f->f_path.mnt, f);
if (!error) {
- error = setxattr(dentry, name, value, size, flags);
+ error = setxattr(dentry, f->f_vfsmnt, name, value, size, flags);
mnt_drop_write(f->f_path.mnt);
}
fput(f);
--- linux-2.6.27.orig/include/linux/xattr.h
+++ linux-2.6.27/include/linux/xattr.h
@@ -16,6 +16,7 @@
#ifdef __KERNEL__
#include <linux/types.h>
+#include <linux/mount.h>
/* Namespaces */
#define XATTR_OS2_PREFIX "os2."
@@ -49,7 +50,7 @@ struct xattr_handler {
ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t);
ssize_t vfs_getxattr(struct dentry *, const char *, void *, size_t);
ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size);
-int vfs_setxattr(struct dentry *, const char *, const void *, size_t, int);
+int vfs_setxattr(struct dentry *, struct vfsmount *, const char *, const void *, size_t, int);
int vfs_removexattr(struct dentry *, const char *);
ssize_t generic_getxattr(struct dentry *dentry, const char *name, void *buffer, size_t size);

View File

@@ -0,0 +1,123 @@
From: Tony Jones <tonyj@suse.de>
Subject: Add a struct vfsmount parameter to vfs_symlink()
The vfsmount will be passed down to the LSM hook so that LSMs can compute
pathnames.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/ecryptfs/inode.c | 4 +++-
fs/namei.c | 5 +++--
fs/nfsd/vfs.c | 12 ++++++++----
include/linux/fs.h | 2 +-
4 files changed, 15 insertions(+), 8 deletions(-)
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -464,6 +464,7 @@ static int ecryptfs_symlink(struct inode
{
int rc;
struct dentry *lower_dentry;
+ struct vfsmount *lower_mnt;
struct dentry *lower_dir_dentry;
char *encoded_symname;
int encoded_symlen;
@@ -471,6 +472,7 @@ static int ecryptfs_symlink(struct inode
lower_dentry = ecryptfs_dentry_to_lower(dentry);
dget(lower_dentry);
+ lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry);
lower_dir_dentry = lock_parent(lower_dentry);
encoded_symlen = ecryptfs_encode_filename(crypt_stat, symname,
strlen(symname),
@@ -479,7 +481,7 @@ static int ecryptfs_symlink(struct inode
rc = encoded_symlen;
goto out_lock;
}
- rc = vfs_symlink(lower_dir_dentry->d_inode, lower_dentry,
+ rc = vfs_symlink(lower_dir_dentry->d_inode, lower_dentry, lower_mnt,
encoded_symname);
kfree(encoded_symname);
if (rc || !lower_dentry->d_inode)
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2347,7 +2347,8 @@ SYSCALL_DEFINE1(unlink, const char __use
return do_unlinkat(AT_FDCWD, pathname);
}
-int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
+int vfs_symlink(struct inode *dir, struct dentry *dentry, struct vfsmount *mnt,
+ const char *oldname)
{
int error = may_create(dir, dentry, 0);
@@ -2393,7 +2394,7 @@ SYSCALL_DEFINE3(symlinkat, const char __
error = mnt_want_write(nd.path.mnt);
if (error)
goto out_dput;
- error = vfs_symlink(nd.path.dentry->d_inode, dentry, from);
+ error = vfs_symlink(nd.path.dentry->d_inode, dentry, nd.path.mnt, from);
mnt_drop_write(nd.path.mnt);
out_dput:
dput(dentry);
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1545,6 +1545,7 @@ nfsd_symlink(struct svc_rqst *rqstp, str
struct iattr *iap)
{
struct dentry *dentry, *dnew;
+ struct svc_export *exp;
__be32 err, cerr;
int host_err;
@@ -1569,6 +1570,7 @@ nfsd_symlink(struct svc_rqst *rqstp, str
if (host_err)
goto out_nfserr;
+ exp = fhp->fh_export;
if (unlikely(path[plen] != 0)) {
char *path_alloced = kmalloc(plen+1, GFP_KERNEL);
if (path_alloced == NULL)
@@ -1576,14 +1578,16 @@ nfsd_symlink(struct svc_rqst *rqstp, str
else {
strncpy(path_alloced, path, plen);
path_alloced[plen] = 0;
- host_err = vfs_symlink(dentry->d_inode, dnew, path_alloced);
+ host_err = vfs_symlink(dentry->d_inode, dnew,
+ exp->ex_path.mnt, path_alloced);
kfree(path_alloced);
}
} else
- host_err = vfs_symlink(dentry->d_inode, dnew, path);
+ host_err = vfs_symlink(dentry->d_inode, dnew, exp->ex_path.mnt,
+ path);
if (!host_err) {
- if (EX_ISSYNC(fhp->fh_export))
+ if (EX_ISSYNC(exp))
host_err = nfsd_sync_dir(dentry);
}
err = nfserrno(host_err);
@@ -1591,7 +1595,7 @@ nfsd_symlink(struct svc_rqst *rqstp, str
mnt_drop_write(fhp->fh_export->ex_path.mnt);
- cerr = fh_compose(resfhp, fhp->fh_export, dnew, fhp);
+ cerr = fh_compose(resfhp, exp, dnew, fhp);
dput(dnew);
if (err==0) err = cerr;
out:
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1180,7 +1180,7 @@ extern int vfs_permission(struct nameida
extern int vfs_create(struct inode *, struct dentry *, int, struct nameidata *);
extern int vfs_mkdir(struct inode *, struct dentry *, struct vfsmount *, int);
extern int vfs_mknod(struct inode *, struct dentry *, struct vfsmount *, int, dev_t);
-extern int vfs_symlink(struct inode *, struct dentry *, const char *);
+extern int vfs_symlink(struct inode *, struct dentry *, struct vfsmount *, const char *);
extern int vfs_link(struct dentry *, struct inode *, struct dentry *);
extern int vfs_rmdir(struct inode *, struct dentry *);
extern int vfs_unlink(struct inode *, struct dentry *);

View File

@@ -0,0 +1,99 @@
From: Tony Jones <tonyj@suse.de>
Subject: Add a struct vfsmount parameter to vfs_unlink()
The vfsmount will be passed down to the LSM hook so that LSMs can compute
pathnames.
Signed-off-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Andreas Gruenbacher <agruen@suse.de>
Signed-off-by: John Johansen <jjohansen@suse.de>
---
fs/ecryptfs/inode.c | 3 ++-
fs/namei.c | 4 ++--
fs/nfsd/nfs4recover.c | 2 +-
fs/nfsd/vfs.c | 2 +-
include/linux/fs.h | 2 +-
ipc/mqueue.c | 2 +-
6 files changed, 8 insertions(+), 7 deletions(-)
--- a/fs/ecryptfs/inode.c
+++ b/fs/ecryptfs/inode.c
@@ -445,11 +445,12 @@ static int ecryptfs_unlink(struct inode
{
int rc = 0;
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
+ struct vfsmount *lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry);
struct inode *lower_dir_inode = ecryptfs_inode_to_lower(dir);
struct dentry *lower_dir_dentry;
lower_dir_dentry = lock_parent(lower_dentry);
- rc = vfs_unlink(lower_dir_inode, lower_dentry);
+ rc = vfs_unlink(lower_dir_inode, lower_dentry, lower_mnt);
if (rc) {
printk(KERN_ERR "Error in vfs_unlink; rc = [%d]\n", rc);
goto out_unlock;
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2248,7 +2248,7 @@ SYSCALL_DEFINE1(rmdir, const char __user
return do_rmdir(AT_FDCWD, pathname);
}
-int vfs_unlink(struct inode *dir, struct dentry *dentry)
+int vfs_unlink(struct inode *dir, struct dentry *dentry, struct vfsmount *mnt)
{
int error = may_delete(dir, dentry, 0);
@@ -2313,7 +2313,7 @@ static long do_unlinkat(int dfd, const c
error = mnt_want_write(nd.path.mnt);
if (error)
goto exit2;
- error = vfs_unlink(nd.path.dentry->d_inode, dentry);
+ error = vfs_unlink(nd.path.dentry->d_inode, dentry, nd.path.mnt);
mnt_drop_write(nd.path.mnt);
exit2:
dput(dentry);
--- a/fs/nfsd/nfs4recover.c
+++ b/fs/nfsd/nfs4recover.c
@@ -264,7 +264,7 @@ nfsd4_remove_clid_file(struct dentry *di
return -EINVAL;
}
mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
- status = vfs_unlink(dir->d_inode, dentry);
+ status = vfs_unlink(dir->d_inode, dentry, rec_dir.path.mnt);
mutex_unlock(&dir->d_inode->i_mutex);
return status;
}
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1832,7 +1832,7 @@ nfsd_unlink(struct svc_rqst *rqstp, stru
host_err = -EPERM;
} else
#endif
- host_err = vfs_unlink(dirp, rdentry);
+ host_err = vfs_unlink(dirp, rdentry, exp->ex_path.mnt);
} else { /* It's RMDIR */
host_err = vfs_rmdir(dirp, rdentry, exp->ex_path.mnt);
}
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1183,7 +1183,7 @@ extern int vfs_mknod(struct inode *, str
extern int vfs_symlink(struct inode *, struct dentry *, struct vfsmount *, const char *);
extern int vfs_link(struct dentry *, struct vfsmount *, struct inode *, struct dentry *, struct vfsmount *);
extern int vfs_rmdir(struct inode *, struct dentry *, struct vfsmount *);
-extern int vfs_unlink(struct inode *, struct dentry *);
+extern int vfs_unlink(struct inode *, struct dentry *, struct vfsmount *);
extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
/*
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -746,7 +746,7 @@ SYSCALL_DEFINE1(mq_unlink, const char __
err = mnt_want_write(mqueue_mnt);
if (err)
goto out_err;
- err = vfs_unlink(dentry->d_parent->d_inode, dentry);
+ err = vfs_unlink(dentry->d_parent->d_inode, dentry, mqueue_mnt);
mnt_drop_write(mqueue_mnt);
out_err:
dput(dentry);

View File

@@ -0,0 +1,48 @@
From: Bernhard Walle <bwalle@suse.de>
Subject: [PATCH] Fix memory map for ia64/discontmem for kdump
makedumpfile[1] cannot run on ia64 discontigmem kernel, because the member
node_mem_map of struct pgdat_list has invalid value. This patch fixes it.
node_start_pfn shows the start pfn of each node, and node_mem_map should
point 'struct page' of each node's node_start_pfn.
On my machine, node0's node_start_pfn shows 0x400 and its node_mem_map points
0xa0007fffbf000000. This address is the same as vmem_map, so the node_mem_map
points 'struct page' of pfn 0, even if its node_start_pfn shows 0x400.
The cause is due to the round down of min_pfn in count_node_pages() and
node0's node_mem_map points 'struct page' of inactive pfn (0x0).
This patch fixes it.
makedumpfile[1]: dump filtering command
https://sourceforge.net/projects/makedumpfile/
Signed-off-by: Ken'ichi Ohmichi <oomichi@mxs.nes.nec.co.jp>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
arch/ia64/include/asm/meminit.h | 1 -
arch/ia64/mm/discontig.c | 1 -
2 files changed, 2 deletions(-)
--- a/arch/ia64/include/asm/meminit.h
+++ b/arch/ia64/include/asm/meminit.h
@@ -47,7 +47,6 @@ extern int reserve_elfcorehdr(unsigned l
*/
#define GRANULEROUNDDOWN(n) ((n) & ~(IA64_GRANULE_SIZE-1))
#define GRANULEROUNDUP(n) (((n)+IA64_GRANULE_SIZE-1) & ~(IA64_GRANULE_SIZE-1))
-#define ORDERROUNDDOWN(n) ((n) & ~((PAGE_SIZE<<MAX_ORDER)-1))
#ifdef CONFIG_NUMA
extern void call_pernode_memory (unsigned long start, unsigned long len, void *func);
--- a/arch/ia64/mm/discontig.c
+++ b/arch/ia64/mm/discontig.c
@@ -635,7 +635,6 @@ static __init int count_node_pages(unsig
(min(end, __pa(MAX_DMA_ADDRESS)) - start) >>PAGE_SHIFT;
#endif
start = GRANULEROUNDDOWN(start);
- start = ORDERROUNDDOWN(start);
end = GRANULEROUNDUP(end);
mem_data[node].max_pfn = max(mem_data[node].max_pfn,
end >> PAGE_SHIFT);

View File

@@ -0,0 +1,143 @@
From: Arnd Bergmann <arnd.bergmann@de.ibm.com>
Subject: powerpc/cell/axon-msi: retry on missing interrupt
References: bnc#445964,bnc#467633
The MSI capture logic on the axon bridge can sometimes
lose interrupts in case of high DMA and interrupt load,
when it signals an MSI interrupt to the MPIC interrupt
controller while we are already handling another MSI.
Each MSI vector gets written into a FIFO buffer in main
memory using DMA, and that DMA access is normally flushed
by the actual interrupt packet on the IOIF. An MMIO
register in the MSIC holds the position of the last
entry in the FIFO buffer that was written. However,
reading that position does not flush the DMA, so that
we can observe stale data in the buffer.
In a stress test, we have observed the DMA to arrive
up to 14 microseconds after reading the register.
We can reliably detect this conditioning by writing
an invalid MSI vector into the FIFO buffer after
reading from it, assuming that all MSIs we get
are valid. After detecting an invalid MSI vector,
we udelay(1) in the interrupt cascade for up to
100 times before giving up.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Acked-by: John Jolly <jjolly@novell.com>
commit 23e0e8afafd9ac065d81506524adf3339584044b
Author: Arnd Bergmann <arnd@arndb.de>
Date: Fri Dec 12 09:19:50 2008 +0000
powerpc/cell/axon-msi: Fix MSI after kexec
Commit d015fe995 'powerpc/cell/axon-msi: Retry on missing interrupt'
has turned a rare failure to kexec on QS22 into a reproducible
error, which we have now analysed.
The problem is that after a kexec, the MSIC hardware still points
into the middle of the old ring buffer. We set up the ring buffer
during reboot, but not the offset into it. On older kernels, this
would cause a storm of thousands of spurious interrupts after a
kexec, which would most of the time get dropped silently.
With the new code, we time out on each interrupt, waiting for
it to become valid. If more interrupts come in that we time
out on, this goes on indefinitely, which eventually leads to
a hard crash.
The solution in this commit is to read the current offset from
the MSIC when reinitializing it. This now works correctly, as
expected.
Reported-by: Dirk Herrendoerfer <d.herrendoerfer@de.ibm.com>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Acked-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: Paul Mackerras <paulus@samba.org>
---
arch/powerpc/platforms/cell/axon_msi.c | 39 ++++++++++++++++++++++++++++-----
1 file changed, 34 insertions(+), 5 deletions(-)
--- a/arch/powerpc/platforms/cell/axon_msi.c
+++ b/arch/powerpc/platforms/cell/axon_msi.c
@@ -95,6 +95,7 @@ static void axon_msi_cascade(unsigned in
struct axon_msic *msic = get_irq_data(irq);
u32 write_offset, msi;
int idx;
+ int retry = 0;
write_offset = dcr_read(msic->dcr_host, MSIC_WRITE_OFFSET_REG);
pr_debug("axon_msi: original write_offset 0x%x\n", write_offset);
@@ -102,7 +103,7 @@ static void axon_msi_cascade(unsigned in
/* write_offset doesn't wrap properly, so we have to mask it */
write_offset &= MSIC_FIFO_SIZE_MASK;
- while (msic->read_offset != write_offset) {
+ while (msic->read_offset != write_offset && retry < 100) {
idx = msic->read_offset / sizeof(__le32);
msi = le32_to_cpu(msic->fifo_virt[idx]);
msi &= 0xFFFF;
@@ -110,13 +111,37 @@ static void axon_msi_cascade(unsigned in
pr_debug("axon_msi: woff %x roff %x msi %x\n",
write_offset, msic->read_offset, msi);
+ if (msi < NR_IRQS && irq_map[msi].host == msic->irq_host) {
+ generic_handle_irq(msi);
+ msic->fifo_virt[idx] = cpu_to_le32(0xffffffff);
+ } else {
+ /*
+ * Reading the MSIC_WRITE_OFFSET_REG does not
+ * reliably flush the outstanding DMA to the
+ * FIFO buffer. Here we were reading stale
+ * data, so we need to retry.
+ */
+ udelay(1);
+ retry++;
+ pr_debug("axon_msi: invalid irq 0x%x!\n", msi);
+ continue;
+ }
+
+ if (retry) {
+ pr_debug("axon_msi: late irq 0x%x, retry %d\n",
+ msi, retry);
+ retry = 0;
+ }
+
msic->read_offset += MSIC_FIFO_ENTRY_SIZE;
msic->read_offset &= MSIC_FIFO_SIZE_MASK;
+ }
- if (msi < NR_IRQS && irq_map[msi].host == msic->irq_host)
- generic_handle_irq(msi);
- else
- pr_debug("axon_msi: invalid irq 0x%x!\n", msi);
+ if (retry) {
+ printk(KERN_WARNING "axon_msi: irq timed out\n");
+
+ msic->read_offset += MSIC_FIFO_ENTRY_SIZE;
+ msic->read_offset &= MSIC_FIFO_SIZE_MASK;
}
desc->chip->eoi(irq);
@@ -364,6 +389,7 @@ static int axon_msi_probe(struct of_devi
dn->full_name);
goto out_free_fifo;
}
+ memset(msic->fifo_virt, 0xff, MSIC_FIFO_SIZE_BYTES);
msic->irq_host = irq_alloc_host(dn, IRQ_HOST_MAP_NOMAP,
NR_IRQS, &msic_host_ops, 0);
@@ -387,6 +413,9 @@ static int axon_msi_probe(struct of_devi
MSIC_CTRL_IRQ_ENABLE | MSIC_CTRL_ENABLE |
MSIC_CTRL_FIFO_SIZE);
+ msic->read_offset = dcr_read(msic->dcr_host, MSIC_WRITE_OFFSET_REG)
+ & MSIC_FIFO_SIZE_MASK;
+
device->dev.platform_data = msic;
ppc_md.setup_msi_irqs = axon_msi_setup_msi_irqs;

View File

@@ -0,0 +1,215 @@
From: Chandru <chandru@in.ibm.com>
Date: Sat, 30 Aug 2008 00:28:16 +1000
Subject: [PATCH] powerpc: Add support for dynamic reconfiguration memory in kexec/kdump kernels
References: bnc#431492
X-Git-Commit: cf00085d8045cddd80a8aabad97de96fa8131793 Mon Sep 17 00:00:00 2001
Kdump kernel needs to use only those memory regions that it is allowed
to use (crashkernel, rtas, tce, etc.). Each of these regions have
their own sizes and are currently added under 'linux,usable-memory'
property under each memory@xxx node of the device tree.
The ibm,dynamic-memory property of ibm,dynamic-reconfiguration-memory
node (on POWER6) now stores in it the representation for most of the
logical memory blocks with the size of each memory block being a
constant (lmb_size). If one or more or part of the above mentioned
regions lie under one of the lmb from ibm,dynamic-memory property,
there is a need to identify those regions within the given lmb.
This makes the kernel recognize a new 'linux,drconf-usable-memory'
property added by kexec-tools. Each entry in this property is of the
form of a count followed by that many (base, size) pairs for the above
mentioned regions. The number of cells in the count value is given by
the #size-cells property of the root node.
Signed-off-by: Chandru Siddalingappa <chandru@in.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
arch/powerpc/kernel/prom.c | 40 +++++++++++++++++++---
arch/powerpc/mm/numa.c | 79 +++++++++++++++++++++++++++++++++++----------
2 files changed, 96 insertions(+), 23 deletions(-)
--- a/arch/powerpc/kernel/prom.c
+++ b/arch/powerpc/kernel/prom.c
@@ -888,9 +888,10 @@ static u64 __init dt_mem_next_cell(int s
*/
static int __init early_init_dt_scan_drconf_memory(unsigned long node)
{
- cell_t *dm, *ls;
+ cell_t *dm, *ls, *usm;
unsigned long l, n, flags;
u64 base, size, lmb_size;
+ unsigned int is_kexec_kdump = 0, rngs;
ls = (cell_t *)of_get_flat_dt_prop(node, "ibm,lmb-size", &l);
if (ls == NULL || l < dt_root_size_cells * sizeof(cell_t))
@@ -905,6 +906,12 @@ static int __init early_init_dt_scan_drc
if (l < (n * (dt_root_addr_cells + 4) + 1) * sizeof(cell_t))
return 0;
+ /* check if this is a kexec/kdump kernel. */
+ usm = (cell_t *)of_get_flat_dt_prop(node, "linux,drconf-usable-memory",
+ &l);
+ if (usm != NULL)
+ is_kexec_kdump = 1;
+
for (; n != 0; --n) {
base = dt_mem_next_cell(dt_root_addr_cells, &dm);
flags = dm[3];
@@ -915,13 +922,34 @@ static int __init early_init_dt_scan_drc
if ((flags & 0x80) || !(flags & 0x8))
continue;
size = lmb_size;
- if (iommu_is_off) {
- if (base >= 0x80000000ul)
+ rngs = 1;
+ if (is_kexec_kdump) {
+ /*
+ * For each lmb in ibm,dynamic-memory, a corresponding
+ * entry in linux,drconf-usable-memory property contains
+ * a counter 'p' followed by 'p' (base, size) duple.
+ * Now read the counter from
+ * linux,drconf-usable-memory property
+ */
+ rngs = dt_mem_next_cell(dt_root_size_cells, &usm);
+ if (!rngs) /* there are no (base, size) duple */
continue;
- if ((base + size) > 0x80000000ul)
- size = 0x80000000ul - base;
}
- lmb_add(base, size);
+ do {
+ if (is_kexec_kdump) {
+ base = dt_mem_next_cell(dt_root_addr_cells,
+ &usm);
+ size = dt_mem_next_cell(dt_root_size_cells,
+ &usm);
+ }
+ if (iommu_is_off) {
+ if (base >= 0x80000000ul)
+ continue;
+ if ((base + size) > 0x80000000ul)
+ size = 0x80000000ul - base;
+ }
+ lmb_add(base, size);
+ } while (--rngs);
}
lmb_dump_all();
return 0;
--- a/arch/powerpc/mm/numa.c
+++ b/arch/powerpc/mm/numa.c
@@ -192,6 +192,21 @@ static const int *of_get_associativity(s
return of_get_property(dev, "ibm,associativity", NULL);
}
+/*
+ * Returns the property linux,drconf-usable-memory if
+ * it exists (the property exists only in kexec/kdump kernels,
+ * added by kexec-tools)
+ */
+static const u32 *of_get_usable_memory(struct device_node *memory)
+{
+ const u32 *prop;
+ u32 len;
+ prop = of_get_property(memory, "linux,drconf-usable-memory", &len);
+ if (!prop || len < sizeof(unsigned int))
+ return 0;
+ return prop;
+}
+
/* Returns nid in the range [0..MAX_NUMNODES-1], or -1 if no useful numa
* info is found.
*/
@@ -529,14 +544,29 @@ static unsigned long __init numa_enforce
}
/*
+ * Reads the counter for a given entry in
+ * linux,drconf-usable-memory property
+ */
+static inline int __init read_usm_ranges(const u32 **usm)
+{
+ /*
+ * For each lmb in ibm,dynamic-memory a corresponding
+ * entry in linux,drconf-usable-memory property contains
+ * a counter followed by that many (base, size) duple.
+ * read the counter from linux,drconf-usable-memory
+ */
+ return read_n_cells(n_mem_size_cells, usm);
+}
+
+/*
* Extract NUMA information from the ibm,dynamic-reconfiguration-memory
* node. This assumes n_mem_{addr,size}_cells have been set.
*/
static void __init parse_drconf_memory(struct device_node *memory)
{
- const u32 *dm;
- unsigned int n, rc;
- unsigned long lmb_size, size;
+ const u32 *dm, *usm;
+ unsigned int n, rc, ranges, is_kexec_kdump = 0;
+ unsigned long lmb_size, base, size, sz;
int nid;
struct assoc_arrays aa;
@@ -552,6 +582,11 @@ static void __init parse_drconf_memory(s
if (rc)
return;
+ /* check if this is a kexec/kdump kernel */
+ usm = of_get_usable_memory(memory);
+ if (usm != NULL)
+ is_kexec_kdump = 1;
+
for (; n != 0; --n) {
struct of_drconf_cell drmem;
@@ -563,21 +598,31 @@ static void __init parse_drconf_memory(s
|| !(drmem.flags & DRCONF_MEM_ASSIGNED))
continue;
- nid = of_drconf_to_nid_single(&drmem, &aa);
-
- fake_numa_create_new_node(
- ((drmem.base_addr + lmb_size) >> PAGE_SHIFT),
+ base = drmem.base_addr;
+ size = lmb_size;
+ ranges = 1;
+
+ if (is_kexec_kdump) {
+ ranges = read_usm_ranges(&usm);
+ if (!ranges) /* there are no (base, size) duple */
+ continue;
+ }
+ do {
+ if (is_kexec_kdump) {
+ base = read_n_cells(n_mem_addr_cells, &usm);
+ size = read_n_cells(n_mem_size_cells, &usm);
+ }
+ nid = of_drconf_to_nid_single(&drmem, &aa);
+ fake_numa_create_new_node(
+ ((base + size) >> PAGE_SHIFT),
&nid);
-
- node_set_online(nid);
-
- size = numa_enforce_memory_limit(drmem.base_addr, lmb_size);
- if (!size)
- continue;
-
- add_active_range(nid, drmem.base_addr >> PAGE_SHIFT,
- (drmem.base_addr >> PAGE_SHIFT)
- + (size >> PAGE_SHIFT));
+ node_set_online(nid);
+ sz = numa_enforce_memory_limit(base, size);
+ if (sz)
+ add_active_range(nid, base >> PAGE_SHIFT,
+ (base >> PAGE_SHIFT)
+ + (sz >> PAGE_SHIFT));
+ } while (--ranges);
}
}

View File

@@ -0,0 +1,41 @@
Date: Thu, 9 Oct 2008 11:20:27 -0400
From: Neil Horman <nhorman@tuxdriver.com>
To: linux-kernel@vger.kernel.org, kexec@lists.infradead.org,
vgoyal@redhat.com, hbabu@us.ibm.com
Subject: [PATCH] add additional symbols to /sys/kernel/vmcoreinfo data for
ppc(64)
Cc: nhorman@tuxdriver.com
Hey-
The makdumpdile dump filtering program, in some modes of operation needs
the node_data and/or contig_page_data symbols to function properly. These
symbols are missing from the powerpc kernel. This patch adds those symbols in
properly. Tested successfully by myself and the reporter.
Regards
Neil
Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
arch/powerpc/kernel/machine_kexec.c | 8 ++++++++
1 file changed, 8 insertions(+)
--- a/arch/powerpc/kernel/machine_kexec.c
+++ b/arch/powerpc/kernel/machine_kexec.c
@@ -44,6 +44,14 @@ void machine_kexec_cleanup(struct kimage
ppc_md.machine_kexec_cleanup(image);
}
+void arch_crash_save_vmcoreinfo(void)
+{
+#ifdef CONFIG_NEED_MULTIPLE_NODES
+ VMCOREINFO_SYMBOL(node_data);
+ VMCOREINFO_LENGTH(node_data, MAX_NUMNODES);
+#endif
+}
+
/*
* Do not allocate memory (or fail in any way) in machine_kexec().
* We are past the point of no return, committed to rebooting now.

View File

@@ -0,0 +1,28 @@
From: Gerald Schaefer <geraldsc@de.ibm.com>
Subject: cio: update sac values
References: bnc#445100
Symptom: Drivers based on fcx fail to start I/O.
Problem: Values for the sac field have changed.
Solution: Update code accordingly.
Acked-by: John Jolly <jjolly@suse.de>
---
arch/s390/include/asm/fcx.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
Index: linux-sles11/arch/s390/include/asm/fcx.h
===================================================================
--- linux-sles11.orig/arch/s390/include/asm/fcx.h
+++ linux-sles11/arch/s390/include/asm/fcx.h
@@ -248,8 +248,8 @@ struct dcw {
#define TCCB_MAX_SIZE (sizeof(struct tccb_tcah) + \
TCCB_MAX_DCW * sizeof(struct dcw) + \
sizeof(struct tccb_tcat))
-#define TCCB_SAC_DEFAULT 0xf901
-#define TCCB_SAC_INTRG 0xf902
+#define TCCB_SAC_DEFAULT 0x1ffe
+#define TCCB_SAC_INTRG 0x1fff
/**
* struct tccb_tcah - Transport-Command-Area Header (TCAH)

View File

@@ -0,0 +1,73 @@
From: Gerald Schaefer <geraldsc@de.ibm.com>
Subject: zfcp: Remove message for failed port
References: bnc#464466
Symptom: During opening of an adapter the message "Remote port ...
could not be opened" is emitted for initiator ports,
confusing users.
Problem: The port scan tries to open all ports, including
initiator ports to determine if they are target ports.
Sometimes, a different error status is returned for the
initiator ports, triggering the message mentioned above.
Solution: Remove the message, target port failures will be checked
later in the error recovery, printing a different message
if necessary.
Acked-by: John Jolly <jjolly@suse.de>
---
Documentation/kmsg/s390/zfcp | 15 ---------------
drivers/s390/scsi/zfcp_dbf.c | 2 +-
drivers/s390/scsi/zfcp_fsf.c | 6 ------
3 files changed, 1 insertion(+), 22 deletions(-)
--- a/Documentation/kmsg/s390/zfcp 2008-12-19 13:18:45.000000000 +0100
+++ b/Documentation/kmsg/s390/zfcp 2008-12-19 13:18:59.000000000 +0100
@@ -677,21 +677,6 @@
*/
/*?
- * Text: "%s: Remote port 0x%016Lx could not be opened\n"
- * Severity: Warning
- * Parameter:
- * @1: bus ID of the zfcp device
- * @2: WWPN
- * Description:
- * The FCP adapter rejected a request to open the specified port. No retry
- * is possible.
- * User action:
- * Verify the setup and try removing and adding the port again. If this
- * problem persists, gather Linux debug data, collect the FCP adapter
- * hardware logs, and report the problem to your support organization.
- */
-
-/*?
* Text: "%s: LUN 0x%Lx on port 0x%Lx is already in use by CSS%d, MIF Image ID %x\n"
* Severity: Warning
* Parameter:
--- a/drivers/s390/scsi/zfcp_dbf.c 2008-12-19 13:18:45.000000000 +0100
+++ b/drivers/s390/scsi/zfcp_dbf.c 2008-12-19 13:18:59.000000000 +0100
@@ -521,7 +521,7 @@ static const char *zfcp_rec_dbf_ids[] =
[29] = "link down",
[30] = "link up status read",
[31] = "open port failed",
- [32] = "open port failed",
+ [32] = "",
[33] = "close port",
[34] = "open unit failed",
[35] = "exclusive open unit failed",
--- a/drivers/s390/scsi/zfcp_fsf.c 2008-12-19 13:18:45.000000000 +0100
+++ b/drivers/s390/scsi/zfcp_fsf.c 2008-12-19 13:18:59.000000000 +0100
@@ -1405,13 +1405,7 @@ static void zfcp_fsf_open_port_handler(s
switch (header->fsf_status_qual.word[0]) {
case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
- req->status |= ZFCP_STATUS_FSFREQ_ERROR;
- break;
case FSF_SQ_NO_RETRY_POSSIBLE:
- dev_warn(&req->adapter->ccw_device->dev,
- "Remote port 0x%016Lx could not be opened\n",
- (unsigned long long)port->wwpn);
- zfcp_erp_port_failed(port, 32, req);
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
}

View File

@@ -0,0 +1,378 @@
From: Gerald Schaefer <geraldsc@de.ibm.com>
Subject: zfcp: Add support for unchained FSF requests
References: bnc#464466
Symptom: On a z900 zfcp loops in error recovery.
Problem: The z900 requires support for unchained FSF requests for
CT and ELS requests. The chained format triggers the ERP
from the qdio error handler.
Solution: Check the hardware feature flag and send unchained CT
and ELS requests if chaining is not support. Adapt the
size of the GPN_FT request as necessary and add debug data
and a warning, in case the CT request hits a limit.
Acked-by: John Jolly <jjolly@suse.de>
---
Documentation/kmsg/s390/zfcp | 16 ++++++++++++
drivers/s390/scsi/zfcp_dbf.c | 2 +
drivers/s390/scsi/zfcp_dbf.h | 1
drivers/s390/scsi/zfcp_def.h | 9 -------
drivers/s390/scsi/zfcp_fc.c | 55 ++++++++++++++++++++++++-------------------
drivers/s390/scsi/zfcp_fsf.c | 32 +++++++++++++++++++------
drivers/s390/scsi/zfcp_fsf.h | 2 +
7 files changed, 77 insertions(+), 40 deletions(-)
--- a/drivers/s390/scsi/zfcp_fc.c 2008-12-19 13:36:23.000000000 +0100
+++ b/drivers/s390/scsi/zfcp_fc.c 2008-12-19 13:36:27.000000000 +0100
@@ -25,9 +25,12 @@ struct gpn_ft_resp_acc {
u64 wwpn;
} __attribute__ ((packed));
-#define ZFCP_GPN_FT_ENTRIES ((PAGE_SIZE - sizeof(struct ct_hdr)) \
- / sizeof(struct gpn_ft_resp_acc))
+#define ZFCP_CT_SIZE_ONE_PAGE (PAGE_SIZE - sizeof(struct ct_hdr))
+#define ZFCP_GPN_FT_ENTRIES (ZFCP_CT_SIZE_ONE_PAGE \
+ / sizeof(struct gpn_ft_resp_acc))
#define ZFCP_GPN_FT_BUFFERS 4
+#define ZFCP_GPN_FT_MAX_SIZE (ZFCP_GPN_FT_BUFFERS * PAGE_SIZE \
+ - sizeof(struct ct_hdr))
#define ZFCP_GPN_FT_MAX_ENTRIES ZFCP_GPN_FT_BUFFERS * (ZFCP_GPN_FT_ENTRIES + 1)
struct ct_iu_gpn_ft_resp {
@@ -283,8 +286,6 @@ int static zfcp_fc_ns_gid_pn_request(str
gid_pn->ct.timeout = ZFCP_NS_GID_PN_TIMEOUT;
gid_pn->ct.req = &gid_pn->req;
gid_pn->ct.resp = &gid_pn->resp;
- gid_pn->ct.req_count = 1;
- gid_pn->ct.resp_count = 1;
sg_init_one(&gid_pn->req, &gid_pn->ct_iu_req,
sizeof(struct ct_iu_gid_pn_req));
sg_init_one(&gid_pn->resp, &gid_pn->ct_iu_resp,
@@ -296,7 +297,7 @@ int static zfcp_fc_ns_gid_pn_request(str
gid_pn->ct_iu_req.header.gs_subtype = ZFCP_CT_NAME_SERVER;
gid_pn->ct_iu_req.header.options = ZFCP_CT_SYNCHRONOUS;
gid_pn->ct_iu_req.header.cmd_rsp_code = ZFCP_CT_GID_PN;
- gid_pn->ct_iu_req.header.max_res_size = ZFCP_CT_MAX_SIZE;
+ gid_pn->ct_iu_req.header.max_res_size = ZFCP_CT_SIZE_ONE_PAGE / 4;
gid_pn->ct_iu_req.wwpn = erp_action->port->wwpn;
init_completion(&compl_rec.done);
@@ -406,8 +407,6 @@ static int zfcp_fc_adisc(struct zfcp_por
sg_init_one(adisc->els.resp, &adisc->ls_adisc_acc,
sizeof(struct zfcp_ls_adisc));
- adisc->els.req_count = 1;
- adisc->els.resp_count = 1;
adisc->els.adapter = adapter;
adisc->els.port = port;
adisc->els.d_id = port->d_id;
@@ -447,17 +446,17 @@ void zfcp_test_link(struct zfcp_port *po
zfcp_erp_port_forced_reopen(port, 0, 65, NULL);
}
-static void zfcp_free_sg_env(struct zfcp_gpn_ft *gpn_ft)
+static void zfcp_free_sg_env(struct zfcp_gpn_ft *gpn_ft, int buf_num)
{
struct scatterlist *sg = &gpn_ft->sg_req;
kfree(sg_virt(sg)); /* free request buffer */
- zfcp_sg_free_table(gpn_ft->sg_resp, ZFCP_GPN_FT_BUFFERS);
+ zfcp_sg_free_table(gpn_ft->sg_resp, buf_num);
kfree(gpn_ft);
}
-static struct zfcp_gpn_ft *zfcp_alloc_sg_env(void)
+static struct zfcp_gpn_ft *zfcp_alloc_sg_env(int buf_num)
{
struct zfcp_gpn_ft *gpn_ft;
struct ct_iu_gpn_ft_req *req;
@@ -474,8 +473,8 @@ static struct zfcp_gpn_ft *zfcp_alloc_sg
}
sg_init_one(&gpn_ft->sg_req, req, sizeof(*req));
- if (zfcp_sg_setup_table(gpn_ft->sg_resp, ZFCP_GPN_FT_BUFFERS)) {
- zfcp_free_sg_env(gpn_ft);
+ if (zfcp_sg_setup_table(gpn_ft->sg_resp, buf_num)) {
+ zfcp_free_sg_env(gpn_ft, buf_num);
gpn_ft = NULL;
}
out:
@@ -484,7 +483,8 @@ out:
static int zfcp_scan_issue_gpn_ft(struct zfcp_gpn_ft *gpn_ft,
- struct zfcp_adapter *adapter)
+ struct zfcp_adapter *adapter,
+ int max_bytes)
{
struct zfcp_send_ct *ct = &gpn_ft->ct;
struct ct_iu_gpn_ft_req *req = sg_virt(&gpn_ft->sg_req);
@@ -497,8 +497,7 @@ static int zfcp_scan_issue_gpn_ft(struct
req->header.gs_subtype = ZFCP_CT_NAME_SERVER;
req->header.options = ZFCP_CT_SYNCHRONOUS;
req->header.cmd_rsp_code = ZFCP_CT_GPN_FT;
- req->header.max_res_size = (sizeof(struct gpn_ft_resp_acc) *
- (ZFCP_GPN_FT_MAX_ENTRIES - 1)) >> 2;
+ req->header.max_res_size = max_bytes / 4;
req->flags = 0;
req->domain_id_scope = 0;
req->area_id_scope = 0;
@@ -511,8 +510,6 @@ static int zfcp_scan_issue_gpn_ft(struct
ct->timeout = 10;
ct->req = &gpn_ft->sg_req;
ct->resp = gpn_ft->sg_resp;
- ct->req_count = 1;
- ct->resp_count = ZFCP_GPN_FT_BUFFERS;
init_completion(&compl_rec.done);
compl_rec.handler = NULL;
@@ -539,7 +536,7 @@ static void zfcp_validate_port(struct zf
zfcp_port_dequeue(port);
}
-static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft)
+static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft, int max_entries)
{
struct zfcp_send_ct *ct = &gpn_ft->ct;
struct scatterlist *sg = gpn_ft->sg_resp;
@@ -559,13 +556,17 @@ static int zfcp_scan_eval_gpn_ft(struct
return -EIO;
}
- if (hdr->max_res_size)
+ if (hdr->max_res_size) {
+ dev_warn(&adapter->ccw_device->dev,
+ "The name server reported %d words residual data\n",
+ hdr->max_res_size);
return -E2BIG;
+ }
down(&zfcp_data.config_sema);
/* first entry is the header */
- for (x = 1; x < ZFCP_GPN_FT_MAX_ENTRIES && !last; x++) {
+ for (x = 1; x < max_entries && !last; x++) {
if (x % (ZFCP_GPN_FT_ENTRIES + 1))
acc++;
else
@@ -611,6 +612,12 @@ int zfcp_scan_ports(struct zfcp_adapter
{
int ret, i;
struct zfcp_gpn_ft *gpn_ft;
+ int chain, max_entries, buf_num, max_bytes;
+
+ chain = adapter->adapter_features & FSF_FEATURE_ELS_CT_CHAINED_SBALS;
+ buf_num = chain ? ZFCP_GPN_FT_BUFFERS : 1;
+ max_entries = chain ? ZFCP_GPN_FT_MAX_ENTRIES : ZFCP_GPN_FT_ENTRIES;
+ max_bytes = chain ? ZFCP_GPN_FT_MAX_SIZE : ZFCP_CT_SIZE_ONE_PAGE;
zfcp_erp_wait(adapter); /* wait until adapter is finished with ERP */
if (fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPORT)
@@ -620,23 +627,23 @@ int zfcp_scan_ports(struct zfcp_adapter
if (ret)
return ret;
- gpn_ft = zfcp_alloc_sg_env();
+ gpn_ft = zfcp_alloc_sg_env(buf_num);
if (!gpn_ft) {
ret = -ENOMEM;
goto out;
}
for (i = 0; i < 3; i++) {
- ret = zfcp_scan_issue_gpn_ft(gpn_ft, adapter);
+ ret = zfcp_scan_issue_gpn_ft(gpn_ft, adapter, max_bytes);
if (!ret) {
- ret = zfcp_scan_eval_gpn_ft(gpn_ft);
+ ret = zfcp_scan_eval_gpn_ft(gpn_ft, max_entries);
if (ret == -EAGAIN)
ssleep(1);
else
break;
}
}
- zfcp_free_sg_env(gpn_ft);
+ zfcp_free_sg_env(gpn_ft, buf_num);
out:
zfcp_wka_port_put(&adapter->nsp);
return ret;
--- a/drivers/s390/scsi/zfcp_fsf.h 2008-12-19 13:36:23.000000000 +0100
+++ b/drivers/s390/scsi/zfcp_fsf.h 2008-12-19 13:36:27.000000000 +0100
@@ -164,6 +164,7 @@
#define FSF_FEATURE_LUN_SHARING 0x00000004
#define FSF_FEATURE_NOTIFICATION_LOST 0x00000008
#define FSF_FEATURE_HBAAPI_MANAGEMENT 0x00000010
+#define FSF_FEATURE_ELS_CT_CHAINED_SBALS 0x00000020
#define FSF_FEATURE_UPDATE_ALERT 0x00000100
#define FSF_FEATURE_MEASUREMENT_DATA 0x00000200
@@ -322,6 +323,7 @@ struct fsf_nport_serv_param {
u8 vendor_version_level[16];
} __attribute__ ((packed));
+#define FSF_PLOGI_MIN_LEN 112
struct fsf_plogi {
u32 code;
struct fsf_nport_serv_param serv_param;
--- a/drivers/s390/scsi/zfcp_fsf.c 2008-12-19 13:36:23.000000000 +0100
+++ b/drivers/s390/scsi/zfcp_fsf.c 2008-12-19 13:36:27.000000000 +0100
@@ -1012,12 +1012,29 @@ skip_fsfstatus:
send_ct->handler(send_ct->handler_data);
}
-static int zfcp_fsf_setup_sbals(struct zfcp_fsf_req *req,
- struct scatterlist *sg_req,
- struct scatterlist *sg_resp, int max_sbals)
+static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req,
+ struct scatterlist *sg_req,
+ struct scatterlist *sg_resp,
+ int max_sbals)
{
+ struct qdio_buffer_element *sbale = zfcp_qdio_sbale_req(req);
+ u32 feat = req->adapter->adapter_features;
int bytes;
+ if (!(feat & FSF_FEATURE_ELS_CT_CHAINED_SBALS)) {
+ if (sg_req->length > PAGE_SIZE || sg_resp->length > PAGE_SIZE ||
+ !sg_is_last(sg_req) || !sg_is_last(sg_resp))
+ return -EOPNOTSUPP;
+
+ sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE_READ;
+ sbale[2].addr = sg_virt(sg_req);
+ sbale[2].length = sg_req->length;
+ sbale[3].addr = sg_virt(sg_resp);
+ sbale[3].length = sg_resp->length;
+ sbale[3].flags |= SBAL_FLAGS_LAST_ENTRY;
+ return 0;
+ }
+
bytes = zfcp_qdio_sbals_from_sg(req, SBAL_FLAGS0_TYPE_WRITE_READ,
sg_req, max_sbals);
if (bytes <= 0)
@@ -1059,8 +1076,8 @@ int zfcp_fsf_send_ct(struct zfcp_send_ct
goto out;
}
- ret = zfcp_fsf_setup_sbals(req, ct->req, ct->resp,
- FSF_MAX_SBALS_PER_REQ);
+ ret = zfcp_fsf_setup_ct_els_sbals(req, ct->req, ct->resp,
+ FSF_MAX_SBALS_PER_REQ);
if (ret)
goto failed_send;
@@ -1170,7 +1187,7 @@ int zfcp_fsf_send_els(struct zfcp_send_e
goto out;
}
- ret = zfcp_fsf_setup_sbals(req, els->req, els->resp, 2);
+ ret = zfcp_fsf_setup_ct_els_sbals(req, els->req, els->resp, 2);
if (ret)
goto failed_send;
@@ -1433,7 +1450,8 @@ static void zfcp_fsf_open_port_handler(s
* Alternately, an ADISC/PDISC ELS should suffice, as well.
*/
plogi = (struct fsf_plogi *) req->qtcb->bottom.support.els;
- if (req->qtcb->bottom.support.els1_length >= sizeof(*plogi)) {
+ if (req->qtcb->bottom.support.els1_length >=
+ FSF_PLOGI_MIN_LEN) {
if (plogi->serv_param.wwpn != port->wwpn)
atomic_clear_mask(ZFCP_STATUS_PORT_DID_DID,
&port->status);
--- a/Documentation/kmsg/s390/zfcp 2008-12-19 13:36:23.000000000 +0100
+++ b/Documentation/kmsg/s390/zfcp 2008-12-19 13:36:27.000000000 +0100
@@ -813,3 +813,19 @@
* problem persists, gather Linux debug data, collect the FCP adapter
* hardware logs, and report the problem to your support organization.
*/
+
+/*?
+ * Text: "%s: The name server reported %d words residual data\n"
+ * Severity: Warning
+ * Parameter:
+ * @1: bus ID of the zfcp device
+ * @2: number of words in residual data
+ * Description:
+ * The fibre channel name server sent too much information about remote ports.
+ * The zfcp device driver did not receive sufficient information to attach all
+ * available remote ports in the SAN.
+ * User action:
+ * Verify that you are running the latest firmware level on the FCP
+ * adapter. Check your SAN setup and consider reducing the number of ports
+ * visible to the FCP adapter by using more restrictive zoning in the SAN.
+ */
--- a/drivers/s390/scsi/zfcp_dbf.c 2008-12-19 13:36:23.000000000 +0100
+++ b/drivers/s390/scsi/zfcp_dbf.c 2008-12-19 13:36:27.000000000 +0100
@@ -935,6 +935,7 @@ void zfcp_san_dbf_event_ct_response(stru
rct->reason_code = hdr->reason_code;
rct->expl = hdr->reason_code_expl;
rct->vendor_unique = hdr->vendor_unique;
+ rct->max_res_size = hdr->max_res_size;
rct->len = min((int)ct->resp->length - (int)sizeof(struct ct_hdr),
ZFCP_DBF_SAN_MAX_PAYLOAD);
debug_event(adapter->san_dbf, level, r, sizeof(*r));
@@ -1042,6 +1043,7 @@ static int zfcp_san_dbf_view_format(debu
zfcp_dbf_out(&p, "reason_code", "0x%02x", ct->reason_code);
zfcp_dbf_out(&p, "reason_code_expl", "0x%02x", ct->expl);
zfcp_dbf_out(&p, "vendor_unique", "0x%02x", ct->vendor_unique);
+ zfcp_dbf_out(&p, "max_res_size", "0x%04x", ct->max_res_size);
} else if (strncmp(r->tag, "oels", ZFCP_DBF_TAG_SIZE) == 0 ||
strncmp(r->tag, "rels", ZFCP_DBF_TAG_SIZE) == 0 ||
strncmp(r->tag, "iels", ZFCP_DBF_TAG_SIZE) == 0) {
--- a/drivers/s390/scsi/zfcp_dbf.h 2008-12-19 13:36:23.000000000 +0100
+++ b/drivers/s390/scsi/zfcp_dbf.h 2008-12-19 13:36:27.000000000 +0100
@@ -171,6 +171,7 @@ struct zfcp_san_dbf_record_ct_response {
u8 reason_code;
u8 expl;
u8 vendor_unique;
+ u16 max_res_size;
u32 len;
} __attribute__ ((packed));
--- a/drivers/s390/scsi/zfcp_def.h 2008-12-19 13:36:23.000000000 +0100
+++ b/drivers/s390/scsi/zfcp_def.h 2008-12-19 13:36:27.000000000 +0100
@@ -210,7 +210,6 @@ struct zfcp_ls_adisc {
#define ZFCP_CT_UNABLE_TO_PERFORM_CMD 0x09
#define ZFCP_CT_GID_PN 0x0121
#define ZFCP_CT_GPN_FT 0x0172
-#define ZFCP_CT_MAX_SIZE 0x1020
#define ZFCP_CT_ACCEPT 0x8002
#define ZFCP_CT_REJECT 0x8001
@@ -339,8 +338,6 @@ struct ct_iu_gid_pn_resp {
* @wka_port: port where the request is sent to
* @req: scatter-gather list for request
* @resp: scatter-gather list for response
- * @req_count: number of elements in request scatter-gather list
- * @resp_count: number of elements in response scatter-gather list
* @handler: handler function (called for response to the request)
* @handler_data: data passed to handler function
* @timeout: FSF timeout for this request
@@ -351,8 +348,6 @@ struct zfcp_send_ct {
struct zfcp_wka_port *wka_port;
struct scatterlist *req;
struct scatterlist *resp;
- unsigned int req_count;
- unsigned int resp_count;
void (*handler)(unsigned long);
unsigned long handler_data;
int timeout;
@@ -377,8 +372,6 @@ struct zfcp_gid_pn_data {
* @d_id: destiniation id of port where request is sent to
* @req: scatter-gather list for request
* @resp: scatter-gather list for response
- * @req_count: number of elements in request scatter-gather list
- * @resp_count: number of elements in response scatter-gather list
* @handler: handler function (called for response to the request)
* @handler_data: data passed to handler function
* @completion: completion for synchronization purposes
@@ -391,8 +384,6 @@ struct zfcp_send_els {
u32 d_id;
struct scatterlist *req;
struct scatterlist *resp;
- unsigned int req_count;
- unsigned int resp_count;
void (*handler)(unsigned long);
unsigned long handler_data;
struct completion *completion;

View File

@@ -0,0 +1,207 @@
From: Gerald Schaefer <geraldsc@de.ibm.com>
Subject: kernel: fix cpu topology support
References: bnc#464466
Symptom: CPU topology changes aren't recognized by the scheduler.
Problem: The common code scheduler used to have a hook which could be
called from architecture code to trigger a rebuild of all
scheduling domains when cpu topology changed. This hook got
removed errorneously. So cpu topology change notifications
got lost.
Solution: Readd the hook. This patch also removes some unused code
from the s390 specific cpu topology code.
Acked-by: John Jolly <jjolly@suse.de>
---
arch/s390/kernel/topology.c | 35 ++++++++++-------------------------
include/linux/topology.h | 2 +-
kernel/sched.c | 16 +++++++++++++---
3 files changed, 24 insertions(+), 29 deletions(-)
--- a/arch/s390/kernel/topology.c
+++ b/arch/s390/kernel/topology.c
@@ -14,6 +14,7 @@
#include <linux/workqueue.h>
#include <linux/cpu.h>
#include <linux/smp.h>
+#include <linux/cpuset.h>
#include <asm/delay.h>
#include <asm/s390_ext.h>
#include <asm/sysinfo.h>
@@ -64,7 +65,6 @@ static void topology_work_fn(struct work
static struct tl_info *tl_info;
static struct core_info core_info;
static int machine_has_topology;
-static int machine_has_topology_irq;
static struct timer_list topology_timer;
static void set_topology_timer(void);
static DECLARE_WORK(topology_work, topology_work_fn);
@@ -81,7 +81,7 @@ cpumask_t cpu_coregroup_map(unsigned int
cpus_clear(mask);
if (!topology_enabled || !machine_has_topology)
- return cpu_present_map;
+ return cpu_possible_map;
spin_lock_irqsave(&topology_lock, flags);
while (core) {
if (cpu_isset(cpu, core->mask)) {
@@ -171,7 +171,7 @@ static void topology_update_polarization
int cpu;
mutex_lock(&smp_cpu_state_mutex);
- for_each_present_cpu(cpu)
+ for_each_possible_cpu(cpu)
smp_cpu_polarization[cpu] = POLARIZATION_HRZ;
mutex_unlock(&smp_cpu_state_mutex);
}
@@ -202,7 +202,7 @@ int topology_set_cpu_management(int fc)
rc = ptf(PTF_HORIZONTAL);
if (rc)
return -EBUSY;
- for_each_present_cpu(cpu)
+ for_each_possible_cpu(cpu)
smp_cpu_polarization[cpu] = POLARIZATION_UNKNWN;
return rc;
}
@@ -211,11 +211,11 @@ static void update_cpu_core_map(void)
{
int cpu;
- for_each_present_cpu(cpu)
+ for_each_possible_cpu(cpu)
cpu_core_map[cpu] = cpu_coregroup_map(cpu);
}
-void arch_update_cpu_topology(void)
+int arch_update_cpu_topology(void)
{
struct tl_info *info = tl_info;
struct sys_device *sysdev;
@@ -224,7 +224,7 @@ void arch_update_cpu_topology(void)
if (!machine_has_topology) {
update_cpu_core_map();
topology_update_polarization_simple();
- return;
+ return 0;
}
stsi(info, 15, 1, 2);
tl_to_cores(info);
@@ -233,11 +233,12 @@ void arch_update_cpu_topology(void)
sysdev = get_cpu_sysdev(cpu);
kobject_uevent(&sysdev->kobj, KOBJ_CHANGE);
}
+ return 1;
}
static void topology_work_fn(struct work_struct *work)
{
- arch_reinit_sched_domains();
+ rebuild_sched_domains();
}
void topology_schedule_update(void)
@@ -260,11 +261,6 @@ static void set_topology_timer(void)
add_timer(&topology_timer);
}
-static void topology_interrupt(__u16 code)
-{
- schedule_work(&topology_work);
-}
-
static int __init early_parse_topology(char *p)
{
if (strncmp(p, "on", 2))
@@ -284,14 +280,7 @@ static int __init init_topology_update(v
goto out;
}
init_timer_deferrable(&topology_timer);
- if (machine_has_topology_irq) {
- rc = register_external_interrupt(0x2005, topology_interrupt);
- if (rc)
- goto out;
- ctl_set_bit(0, 8);
- }
- else
- set_topology_timer();
+ set_topology_timer();
out:
update_cpu_core_map();
return rc;
@@ -312,9 +301,6 @@ void __init s390_init_cpu_topology(void)
return;
machine_has_topology = 1;
- if (facility_bits & (1ULL << 51))
- machine_has_topology_irq = 1;
-
tl_info = alloc_bootmem_pages(PAGE_SIZE);
info = tl_info;
stsi(info, 15, 1, 2);
@@ -338,5 +324,4 @@ void __init s390_init_cpu_topology(void)
return;
error:
machine_has_topology = 0;
- machine_has_topology_irq = 0;
}
--- a/include/linux/topology.h
+++ b/include/linux/topology.h
@@ -49,7 +49,7 @@
for_each_online_node(node) \
if (nr_cpus_node(node))
-void arch_update_cpu_topology(void);
+int arch_update_cpu_topology(void);
/* Conform to ACPI 2.0 SLIT distance definitions */
#define LOCAL_DISTANCE 10
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -7640,8 +7640,14 @@ static struct sched_domain_attr *dattr_c
*/
static cpumask_t fallback_doms;
-void __attribute__((weak)) arch_update_cpu_topology(void)
+/*
+ * arch_update_cpu_topology lets virtualized architectures update the
+ * cpu core maps. It is supposed to return 1 if the topology changed
+ * or 0 if it stayed the same.
+ */
+int __attribute__((weak)) arch_update_cpu_topology(void)
{
+ return 0;
}
/*
@@ -7735,17 +7741,21 @@ void partition_sched_domains(int ndoms_n
struct sched_domain_attr *dattr_new)
{
int i, j, n;
+ int top_changed;
mutex_lock(&sched_domains_mutex);
/* always unregister in case we don't destroy any domains */
unregister_sched_domain_sysctl();
+ /* Let architecture update cpu core mappings. */
+ top_changed = arch_update_cpu_topology();
+
n = doms_new ? ndoms_new : 0;
/* Destroy deleted domains */
for (i = 0; i < ndoms_cur; i++) {
- for (j = 0; j < n; j++) {
+ for (j = 0; j < n && !top_changed; j++) {
if (cpus_equal(doms_cur[i], doms_new[j])
&& dattrs_equal(dattr_cur, i, dattr_new, j))
goto match1;
@@ -7765,7 +7775,7 @@ match1:
/* Build new domains */
for (i = 0; i < ndoms_new; i++) {
- for (j = 0; j < ndoms_cur; j++) {
+ for (j = 0; j < ndoms_cur && !top_changed; j++) {
if (cpus_equal(doms_new[i], doms_cur[j])
&& dattrs_equal(dattr_new, i, dattr_cur, j))
goto match2;

View File

@@ -0,0 +1,61 @@
From: Gerald Schaefer <geraldsc@de.ibm.com>
Subject: cio: fix subchannel multipath mode setup
References: bnc#466462,LTC#51047
Symptom: Undefined behavior when trying to access DASD devices with more
than one CHPID: e.g. I/O errors due to timeouts after missing
interrupts, slow access to DASDs because single path mode is used.
Problem: Setup of subchannel multipath mode is not performed correctly
because changes to a local buffer are lost before they are sent
to the channel subsystem. In this state, the control unit assumes
multipath mode while the channel subsystem expects single path
mode. As a result, interrupts may not be correctly recognized
which leads to timeout situations and eventually I/O errors.
Also single path processing may slow down DASD access.
Solution: Apply changes to the subchannel configuration after modifying
the local buffer.
Acked-by: John Jolly <jjolly@suse.de>
---
drivers/s390/cio/device.c | 6 ++++++
drivers/s390/cio/device_fsm.c | 2 ++
2 files changed, 8 insertions(+)
Index: linux-sles11/drivers/s390/cio/device.c
===================================================================
--- linux-sles11.orig/drivers/s390/cio/device.c
+++ linux-sles11/drivers/s390/cio/device.c
@@ -1246,6 +1246,9 @@ static int io_subchannel_probe(struct su
return 0;
}
io_subchannel_init_fields(sch);
+ rc = cio_modify(sch);
+ if (rc)
+ goto out_schedule;
/*
* First check if a fitting device may be found amongst the
* disconnected devices or in the orphanage.
@@ -1676,6 +1679,9 @@ static int ccw_device_console_enable(str
sch->private = cio_get_console_priv();
memset(sch->private, 0, sizeof(struct io_subchannel_private));
io_subchannel_init_fields(sch);
+ rc = cio_modify(sch);
+ if (rc)
+ return rc;
sch->driver = &io_subchannel_driver;
/* Initialize the ccw_device structure. */
cdev->dev.parent= &sch->dev;
Index: linux-sles11/drivers/s390/cio/device_fsm.c
===================================================================
--- linux-sles11.orig/drivers/s390/cio/device_fsm.c
+++ linux-sles11/drivers/s390/cio/device_fsm.c
@@ -1028,6 +1028,8 @@ void ccw_device_trigger_reprobe(struct c
sch->schib.pmcw.ena = 0;
if ((sch->lpm & (sch->lpm - 1)) != 0)
sch->schib.pmcw.mp = 1;
+ if (cio_modify(sch))
+ return;
/* We should also udate ssd info, but this has to wait. */
/* Check if this is another device which appeared on the same sch. */
if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {

View File

@@ -0,0 +1,109 @@
From: Gerald Schaefer <geraldsc@de.ibm.com>
Subject: zfcp: fix memory alignment for GPN_FT requests.
References: bnc#466462
Symptom: An unexpected adapter reopen can be triggered in case
of a wrongly aligned GPN_FT nameserver request.
Problem: A request which is stored across a page is not allowed.
The standard memory allocation does not guarantee to have
all requested memory within one page.
Solution: Make sure the requested memory is always within one page.
Acked-by: John Jolly <jjolly@suse.de>
---
drivers/s390/scsi/zfcp_aux.c | 7 +++++++
drivers/s390/scsi/zfcp_def.h | 9 +++++++++
drivers/s390/scsi/zfcp_fc.c | 13 +++----------
3 files changed, 19 insertions(+), 10 deletions(-)
Index: linux-sles11/drivers/s390/scsi/zfcp_aux.c
===================================================================
--- linux-sles11.orig/drivers/s390/scsi/zfcp_aux.c
+++ linux-sles11/drivers/s390/scsi/zfcp_aux.c
@@ -175,6 +175,11 @@ static int __init zfcp_module_init(void)
if (!zfcp_data.gid_pn_cache)
goto out_gid_cache;
+ zfcp_data.gpn_ft_cache = zfcp_cache_create(
+ sizeof(struct ct_iu_gpn_ft_req), "zfcp_gpn");
+ if (!zfcp_data.gpn_ft_cache)
+ goto out_gpn_cache;
+
zfcp_data.work_queue = create_singlethread_workqueue("zfcp_wq");
INIT_LIST_HEAD(&zfcp_data.adapter_list_head);
@@ -209,6 +214,8 @@ out_ccw_register:
out_misc:
fc_release_transport(zfcp_data.scsi_transport_template);
out_transport:
+ kmem_cache_destroy(zfcp_data.gpn_ft_cache);
+out_gpn_cache:
kmem_cache_destroy(zfcp_data.gid_pn_cache);
out_gid_cache:
kmem_cache_destroy(zfcp_data.sr_buffer_cache);
Index: linux-sles11/drivers/s390/scsi/zfcp_def.h
===================================================================
--- linux-sles11.orig/drivers/s390/scsi/zfcp_def.h
+++ linux-sles11/drivers/s390/scsi/zfcp_def.h
@@ -333,6 +333,14 @@ struct ct_iu_gid_pn_resp {
u32 d_id;
} __attribute__ ((packed));
+struct ct_iu_gpn_ft_req {
+ struct ct_hdr header;
+ u8 flags;
+ u8 domain_id_scope;
+ u8 area_id_scope;
+ u8 fc4_type;
+} __attribute__ ((packed));
+
/**
* struct zfcp_send_ct - used to pass parameters to function zfcp_fsf_send_ct
* @wka_port: port where the request is sent to
@@ -595,6 +603,7 @@ struct zfcp_data {
struct kmem_cache *fsf_req_qtcb_cache;
struct kmem_cache *sr_buffer_cache;
struct kmem_cache *gid_pn_cache;
+ struct kmem_cache *gpn_ft_cache;
struct workqueue_struct *work_queue;
};
Index: linux-sles11/drivers/s390/scsi/zfcp_fc.c
===================================================================
--- linux-sles11.orig/drivers/s390/scsi/zfcp_fc.c
+++ linux-sles11/drivers/s390/scsi/zfcp_fc.c
@@ -10,14 +10,6 @@
#include "zfcp_ext.h"
-struct ct_iu_gpn_ft_req {
- struct ct_hdr header;
- u8 flags;
- u8 domain_id_scope;
- u8 area_id_scope;
- u8 fc4_type;
-} __attribute__ ((packed));
-
struct gpn_ft_resp_acc {
u8 control;
u8 port_id[3];
@@ -450,7 +442,8 @@ static void zfcp_free_sg_env(struct zfcp
{
struct scatterlist *sg = &gpn_ft->sg_req;
- kfree(sg_virt(sg)); /* free request buffer */
+ /* free request buffer */
+ kmem_cache_free(zfcp_data.gpn_ft_cache, sg_virt(sg));
zfcp_sg_free_table(gpn_ft->sg_resp, buf_num);
kfree(gpn_ft);
@@ -465,7 +458,7 @@ static struct zfcp_gpn_ft *zfcp_alloc_sg
if (!gpn_ft)
return NULL;
- req = kzalloc(sizeof(struct ct_iu_gpn_ft_req), GFP_KERNEL);
+ req = kmem_cache_alloc(zfcp_data.gpn_ft_cache, GFP_KERNEL);
if (!req) {
kfree(gpn_ft);
gpn_ft = NULL;

View File

@@ -0,0 +1,83 @@
From: Gerald Schaefer <geraldsc@de.ibm.com>
Subject: iucv: failing cpu hot remove for inactive iucv
References: bnc#466462,LTC#51104
Symptom: cpu hot remove rejected with NOTIFY_BAD
Problem: If the iucv module is compiled in / loaded but no user
is registered, cpu hot remove doesn't work. The iucv
cpu hotplug notifier on CPU_DOWN_PREPARE checks, if
the iucv_buffer_cpumask would be empty after the
corresponding bit would be cleared. However the bit
was never set since iucv wasn't enabled. That causes
all cpu hot unplug operations to fail in this scenario.
Solution: Use iucv_path_table as an indicator whether iucv is
enabled or not.
Acked-by: John Jolly <jjolly@suse.de>
---
net/iucv/iucv.c | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
Index: linux-sles11/net/iucv/iucv.c
===================================================================
--- linux-sles11.orig/net/iucv/iucv.c
+++ linux-sles11/net/iucv/iucv.c
@@ -516,6 +516,7 @@ static int iucv_enable(void)
size_t alloc_size;
int cpu, rc;
+ get_online_cpus();
rc = -ENOMEM;
alloc_size = iucv_max_pathid * sizeof(struct iucv_path);
iucv_path_table = kzalloc(alloc_size, GFP_KERNEL);
@@ -523,19 +524,17 @@ static int iucv_enable(void)
goto out;
/* Declare per cpu buffers. */
rc = -EIO;
- get_online_cpus();
for_each_online_cpu(cpu)
smp_call_function_single(cpu, iucv_declare_cpu, NULL, 1);
if (cpus_empty(iucv_buffer_cpumask))
/* No cpu could declare an iucv buffer. */
- goto out_path;
+ goto out;
put_online_cpus();
return 0;
-
-out_path:
- put_online_cpus();
- kfree(iucv_path_table);
out:
+ kfree(iucv_path_table);
+ iucv_path_table = NULL;
+ put_online_cpus();
return rc;
}
@@ -550,8 +549,9 @@ static void iucv_disable(void)
{
get_online_cpus();
on_each_cpu(iucv_retrieve_cpu, NULL, 1);
- put_online_cpus();
kfree(iucv_path_table);
+ iucv_path_table = NULL;
+ put_online_cpus();
}
static int __cpuinit iucv_cpu_notify(struct notifier_block *self,
@@ -588,10 +588,14 @@ static int __cpuinit iucv_cpu_notify(str
case CPU_ONLINE_FROZEN:
case CPU_DOWN_FAILED:
case CPU_DOWN_FAILED_FROZEN:
+ if (!iucv_path_table)
+ break;
smp_call_function_single(cpu, iucv_declare_cpu, NULL, 1);
break;
case CPU_DOWN_PREPARE:
case CPU_DOWN_PREPARE_FROZEN:
+ if (!iucv_path_table)
+ break;
cpumask = iucv_buffer_cpumask;
cpu_clear(cpu, cpumask);
if (cpus_empty(cpumask))

View File

@@ -0,0 +1,31 @@
From: Gerald Schaefer <geraldsc@de.ibm.com>
Subject: kernel: 31 bit compat sigaltstack syscall fails with -EFAULT.
References: bnc#466462,LTC#50888
Symptom: When 31 bit user space programs call sigaltstack on a 64 bit Linux
OS, the system call returns -1 with errno=EFAULT.
Problem: The 31 bit pointer passed to the system call is extended
to 64 bit, but the high order bits are not set to zero.
The kernel detects the invalid user space pointer and
returns -EFAULT.
Solution: Call sys32_sigaltstack_wrapper() instead of sys32_sigaltstack().
The wrapper function sets the high order bits to zero.
Acked-by: John Jolly <jjolly@suse.de>
---
arch/s390/kernel/syscalls.S | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
Index: linux-sles11/arch/s390/kernel/syscalls.S
===================================================================
--- linux-sles11.orig/arch/s390/kernel/syscalls.S
+++ linux-sles11/arch/s390/kernel/syscalls.S
@@ -194,7 +194,7 @@ SYSCALL(sys_chown16,sys_ni_syscall,sys32
SYSCALL(sys_getcwd,sys_getcwd,sys32_getcwd_wrapper)
SYSCALL(sys_capget,sys_capget,sys32_capget_wrapper)
SYSCALL(sys_capset,sys_capset,sys32_capset_wrapper) /* 185 */
-SYSCALL(sys_sigaltstack,sys_sigaltstack,sys32_sigaltstack)
+SYSCALL(sys_sigaltstack,sys_sigaltstack,sys32_sigaltstack_wrapper)
SYSCALL(sys_sendfile,sys_sendfile64,sys32_sendfile_wrapper)
NI_SYSCALL /* streams1 */
NI_SYSCALL /* streams2 */

View File

@@ -0,0 +1,30 @@
From: Gerald Schaefer <geraldsc@de.ibm.com>
Subject: topology: introduce arch specific SD_MC_INIT initializer
References: bnc#477666,LTC#51049
Symptom: Up to 30% more cpu usage for some workloads.
Problem: For some workloads the extra multicore scheduling domain causes
additional cpu usage because of too optimistic assumptions when
it is ok to migrate processes from one cpu to another. The default
values for SD_MC_INIT don't work well on s390.
Solution: Define an architecure specific SD_MC_INIT scheduling domain
initializer which fixes the regression.
Acked-by: John Jolly <jjolly@suse.de>
---
arch/s390/include/asm/topology.h | 2 ++
1 file changed, 2 insertions(+)
Index: linux-sles11/arch/s390/include/asm/topology.h
===================================================================
--- linux-sles11.orig/arch/s390/include/asm/topology.h
+++ linux-sles11/arch/s390/include/asm/topology.h
@@ -28,6 +28,8 @@ static inline void s390_init_cpu_topolog
};
#endif
+#define SD_MC_INIT SD_CPU_INIT
+
#include <asm-generic/topology.h>
#endif /* _ASM_S390_TOPOLOGY_H */

View File

@@ -0,0 +1,23 @@
From: Yi Zou <yi.zou@intel.com>
Subject: [FcOE] change fcoe_sw sg_tablesize to SG_ALL
References: bnc #459142
Signed-off-by: Yi Zou <yi.zou@intel.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/fcoe/fcoe_sw.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/scsi/fcoe/fcoe_sw.c
+++ b/drivers/scsi/fcoe/fcoe_sw.c
@@ -100,7 +100,7 @@ static struct scsi_host_template fcoe_sw
.cmd_per_lun = 32,
.can_queue = FCOE_MAX_OUTSTANDING_COMMANDS,
.use_clustering = ENABLE_CLUSTERING,
- .sg_tablesize = 4,
+ .sg_tablesize = SG_ALL,
.max_sectors = 0xffff,
};

View File

@@ -0,0 +1,24 @@
From: Yi Zou <yi.zou@intel.com>
Subject: [FcOE] check return for fc_set_mfs
References: bnc #459142
Signed-off-by: Yi Zou <yi.zou@intel.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/fcoe/fcoe_sw.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
--- a/drivers/scsi/fcoe/fcoe_sw.c
+++ b/drivers/scsi/fcoe/fcoe_sw.c
@@ -178,7 +178,8 @@ static int fcoe_sw_netdev_config(struct
*/
mfs = fc->real_dev->mtu - (sizeof(struct fcoe_hdr) +
sizeof(struct fcoe_crc_eof));
- fc_set_mfs(lp, mfs);
+ if (fc_set_mfs(lp, mfs))
+ return -EINVAL;
lp->link_status = ~FC_PAUSE & ~FC_LINK_UP;
if (!fcoe_link_ok(lp))

View File

@@ -0,0 +1,149 @@
From: Chris Leech <christopher.leech@intel.com>
Subject: [FcOE] fix frame length validation in the early receive path
References: bnc #459142
Validation of the frame length was missing before accessing the FC and FCoE
headers. Some of the later checks were bogus, because of the way the fr_len
variable and skb->len were being manipulated they could never fail.
Signed-off-by: Chris Leech <christopher.leech@intel.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/fcoe/libfcoe.c | 48 +++++++++++++++++++++-----------------------
include/scsi/fc/fc_fcoe.h | 12 +++++++++++
include/scsi/fc_frame.h | 2 -
3 files changed, 36 insertions(+), 26 deletions(-)
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -184,7 +184,6 @@ int fcoe_rcv(struct sk_buff *skb, struct
struct fcoe_rcv_info *fr;
struct fcoe_softc *fc;
struct fcoe_dev_stats *stats;
- u8 *data;
struct fc_frame_header *fh;
unsigned short oxid;
int cpu_idx;
@@ -211,9 +210,18 @@ int fcoe_rcv(struct sk_buff *skb, struct
FC_DBG("wrong FC type frame");
goto err;
}
- data = skb->data;
- data += sizeof(struct fcoe_hdr);
- fh = (struct fc_frame_header *)data;
+
+ /*
+ * Check for minimum frame length, and make sure required FCoE
+ * and FC headers are pulled into the linear data area.
+ */
+ if (unlikely((skb->len < FCOE_MIN_FRAME) ||
+ !pskb_may_pull(skb, FCOE_HEADER_LEN)))
+ goto err;
+
+ skb_set_transport_header(skb, sizeof(struct fcoe_hdr));
+ fh = (struct fc_frame_header *) skb_transport_header(skb);
+
oxid = ntohs(fh->fh_ox_id);
fr = fcoe_dev_from_skb(skb);
@@ -514,8 +522,6 @@ int fcoe_percpu_receive_thread(void *arg
{
struct fcoe_percpu_s *p = arg;
u32 fr_len;
- unsigned int hlen;
- unsigned int tlen;
struct fc_lport *lp;
struct fcoe_rcv_info *fr;
struct fcoe_dev_stats *stats;
@@ -572,10 +578,12 @@ int fcoe_percpu_receive_thread(void *arg
skb_linearize(skb); /* not ideal */
/*
- * Check the header and pull it off.
+ * Frame length checks and setting up the header pointers
+ * was done in fcoe_rcv already.
*/
- hlen = sizeof(struct fcoe_hdr);
- hp = (struct fcoe_hdr *)skb->data;
+ hp = (struct fcoe_hdr *) skb_network_header(skb);
+ fh = (struct fc_frame_header *) skb_transport_header(skb);
+
if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
if (stats) {
if (stats->ErrorFrames < 5)
@@ -586,22 +594,10 @@ int fcoe_percpu_receive_thread(void *arg
kfree_skb(skb);
continue;
}
+
skb_pull(skb, sizeof(struct fcoe_hdr));
- tlen = sizeof(struct fcoe_crc_eof);
- fr_len = skb->len - tlen;
- skb_trim(skb, fr_len);
+ fr_len = skb->len - sizeof(struct fcoe_crc_eof);
- if (unlikely(fr_len > skb->len)) {
- if (stats) {
- if (stats->ErrorFrames < 5)
- FC_DBG("length error fr_len 0x%x "
- "skb->len 0x%x", fr_len,
- skb->len);
- stats->ErrorFrames++;
- }
- kfree_skb(skb);
- continue;
- }
if (stats) {
stats->RxFrames++;
stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
@@ -610,9 +606,11 @@ int fcoe_percpu_receive_thread(void *arg
fp = (struct fc_frame *)skb;
cp = (struct fcoe_crc_eof *)(skb->data + fr_len);
fc_frame_init(fp);
- fr_eof(fp) = cp->fcoe_eof;
- fr_sof(fp) = hp->fcoe_sof;
fr_dev(fp) = lp;
+ fr_sof(fp) = hp->fcoe_sof;
+ fr_eof(fp) = cp->fcoe_eof;
+ /* trim off the CRC and EOF trailer*/
+ skb_trim(skb, fr_len);
/*
* We only check CRC if no offload is available and if it is
--- a/include/scsi/fc/fc_fcoe.h
+++ b/include/scsi/fc/fc_fcoe.h
@@ -85,6 +85,18 @@ struct fcoe_crc_eof {
} __attribute__((packed));
/*
+ * Minimum FCoE + FC header length
+ * 14 bytes FCoE header + 24 byte FC header = 38 bytes
+ */
+#define FCOE_HEADER_LEN 38
+
+/*
+ * Minimum FCoE frame size
+ * 14 bytes FCoE header + 24 byte FC header + 8 byte FCoE trailer = 46 bytes
+ */
+#define FCOE_MIN_FRAME 46
+
+/*
* fc_fcoe_set_mac - Store OUI + DID into MAC address field.
* @mac: mac address to be set
* @did: fc dest id to use
--- a/include/scsi/fc_frame.h
+++ b/include/scsi/fc_frame.h
@@ -66,10 +66,10 @@ struct fcoe_rcv_info {
struct fc_lport *fr_dev; /* transport layer private pointer */
struct fc_seq *fr_seq; /* for use with exchange manager */
struct scsi_cmnd *fr_cmd; /* for use of scsi command */
+ u16 fr_max_payload; /* max FC payload */
enum fc_sof fr_sof; /* start of frame delimiter */
enum fc_eof fr_eof; /* end of frame delimiter */
u8 fr_flags; /* flags - see below */
- u16 fr_max_payload; /* max FC payload */
};
/*

View File

@@ -0,0 +1,51 @@
From: James Bottomley <James.Bottomley@HansenPartnership.com>
Subject: fcoe: fix incorrect use of struct module
Patch-mainline: 9296e519538b77b5070d49f2f9d66032733c76d4
References: bnc #468051
This structure may not be defined if CONFIG_MODULE=n, so never deref it. Change
uses of module->name to module_name(module) and corrects some dyslexic printks
and docbook comments.
Reported-by: Randy Dunlap <randy.dunlap@oracle.com>
Cc: Robert Love <robert.w.love@intel.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/fcoe/libfcoe.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -167,7 +167,7 @@ static int fcoe_cpu_callback(struct noti
#endif /* CONFIG_HOTPLUG_CPU */
/**
- * foce_rcv - this is the fcoe receive function called by NET_RX_SOFTIRQ
+ * fcoe_rcv - this is the fcoe receive function called by NET_RX_SOFTIRQ
* @skb: the receive skb
* @dev: associated net device
* @ptype: context
@@ -992,8 +992,8 @@ static int fcoe_ethdrv_get(const struct
owner = fcoe_netdev_to_module_owner(netdev);
if (owner) {
- printk(KERN_DEBUG "foce:hold driver module %s for %s\n",
- owner->name, netdev->name);
+ printk(KERN_DEBUG "fcoe:hold driver module %s for %s\n",
+ module_name(owner), netdev->name);
return try_module_get(owner);
}
return -ENODEV;
@@ -1012,8 +1012,8 @@ static int fcoe_ethdrv_put(const struct
owner = fcoe_netdev_to_module_owner(netdev);
if (owner) {
- printk(KERN_DEBUG "foce:release driver module %s for %s\n",
- owner->name, netdev->name);
+ printk(KERN_DEBUG "fcoe:release driver module %s for %s\n",
+ module_name(owner), netdev->name);
module_put(owner);
return 0;
}

View File

@@ -0,0 +1,43 @@
From: Vasu Dev <vasu.dev@intel.com>
Subject: [FcOE] improved load balancing in rx path
References: bnc #459142
Currently incoming frame exchange id ANDing with total number of bits
in online CPU bits mask, resulted only at most two CPUs selection in
rx path, so instead used online CPU bits mask to direct incoming frame
to a all cpus for better load balancing.
Added code to default to first CPU in case selected CPU is offline or
its rx thread not present.
Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/fcoe/libfcoe.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -230,13 +230,14 @@ int fcoe_rcv(struct sk_buff *skb, struct
cpu_idx = 0;
#ifdef CONFIG_SMP
/*
- * The exchange ID are ANDed with num of online CPUs,
- * so that will have the least lock contention in
- * handling the exchange. if there is no thread
- * for a given idx then use first online cpu.
+ * The incoming frame exchange id(oxid) is ANDed with num of online
+ * cpu bits to get cpu_idx and then this cpu_idx is used for selecting
+ * a per cpu kernel thread from fcoe_percpu. In case the cpu is
+ * offline or no kernel thread for derived cpu_idx then cpu_idx is
+ * initialize to first online cpu index.
*/
- cpu_idx = oxid & (num_online_cpus() >> 1);
- if (fcoe_percpu[cpu_idx] == NULL)
+ cpu_idx = oxid & (num_online_cpus() - 1);
+ if (!fcoe_percpu[cpu_idx] || !cpu_online(cpu_idx))
cpu_idx = first_cpu(cpu_online_map);
#endif
fps = fcoe_percpu[cpu_idx];

View File

@@ -0,0 +1,26 @@
From: Robert Love <robert.w.love@intel.com>
Subject: [FcOE] Logoff of the fabric when destroying interface
References: bnc #459142
This line was accidentally removed by a previous patch.
Signed-off-by: Robert Love <robert.w.love@intel.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/fcoe/fcoe_sw.c | 3 +++
1 file changed, 3 insertions(+)
--- a/drivers/scsi/fcoe/fcoe_sw.c
+++ b/drivers/scsi/fcoe/fcoe_sw.c
@@ -302,6 +302,9 @@ static int fcoe_sw_destroy(struct net_de
fc = fcoe_softc(lp);
+ /* Logout of the fabric */
+ fc_fabric_logoff(lp);
+
/* Remove the instance from fcoe's list */
fcoe_hostlist_remove(lp);

View File

@@ -0,0 +1,46 @@
From: Yi Zou <yi.zou@intel.com>
Subject: [FcOE] remove WARN_ON in fc_set_mfs
References: bnc #459142
remove WARN_ON in fc_set_mfs(), also adde comments.
Signed-off-by: Yi Zou <yi.zou@intel.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/libfc/fc_lport.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -656,10 +656,20 @@ int fc_lport_destroy(struct fc_lport *lp
}
EXPORT_SYMBOL(fc_lport_destroy);
+/**
+ * fc_set_mfs - sets up the mfs for the corresponding fc_lport
+ * @lport: fc_lport pointer to unregister
+ * @mfs: the new mfs for fc_lport
+ *
+ * Set mfs for the given fc_lport to the new mfs.
+ *
+ * Return: 0 for success
+ *
+ **/
int fc_set_mfs(struct fc_lport *lport, u32 mfs)
{
unsigned int old_mfs;
- int rc = -1;
+ int rc = -EINVAL;
mutex_lock(&lport->lp_mutex);
@@ -667,7 +677,6 @@ int fc_set_mfs(struct fc_lport *lport, u
if (mfs >= FC_MIN_MAX_FRAME) {
mfs &= ~3;
- WARN_ON((size_t) mfs < FC_MIN_MAX_FRAME);
if (mfs > FC_MAX_FRAME)
mfs = FC_MAX_FRAME;
mfs -= sizeof(struct fc_frame_header);

View File

@@ -0,0 +1,34 @@
From: Yi Zou <yi.zou@intel.com>
Subject: [FcOE] user_mfs is never used
References: bnc #459142
Signed-off-by: Yi Zou <yi.zou@intel.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/fcoe/libfcoe.c | 2 --
include/scsi/libfcoe.h | 1 -
2 files changed, 3 deletions(-)
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -900,8 +900,6 @@ static int fcoe_device_notification(stru
mfs = fc->real_dev->mtu -
(sizeof(struct fcoe_hdr) +
sizeof(struct fcoe_crc_eof));
- if (fc->user_mfs && fc->user_mfs < mfs)
- mfs = fc->user_mfs;
if (mfs >= FC_MIN_MAX_FRAME)
fc_set_mfs(lp, mfs);
new_status &= ~FC_LINK_UP;
--- a/include/scsi/libfcoe.h
+++ b/include/scsi/libfcoe.h
@@ -46,7 +46,6 @@ struct fcoe_softc {
struct net_device *phys_dev; /* device with ethtool_ops */
struct packet_type fcoe_packet_type;
struct sk_buff_head fcoe_pending_queue;
- u16 user_mfs; /* configured max frame size */
u8 dest_addr[ETH_ALEN];
u8 ctl_src_addr[ETH_ALEN];

View File

@@ -0,0 +1,29 @@
From: Robert Love <robert.w.love@intel.com>
Subject: [FcOE] Add fc_disc.c locking comment block
References: bnc #459142
Signed-off-by: Robert Love <robert.w.love@intel.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/libfc/fc_disc.c | 8 ++++++++
1 file changed, 8 insertions(+)
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -24,6 +24,14 @@
* also handles RSCN events and re-discovery if necessary.
*/
+/*
+ * DISC LOCKING
+ *
+ * The disc mutex is can be locked when acquiring rport locks, but may not
+ * be held when acquiring the lport lock. Refer to fc_lport.c for more
+ * details.
+ */
+
#include <linux/timer.h>
#include <linux/err.h>
#include <asm/unaligned.h>

View File

@@ -0,0 +1,218 @@
From 251b8184b1bd4e17656d72ba9cffcba733092064 Mon Sep 17 00:00:00 2001
From: Robert Love <robert.w.love@intel.com>
Date: Mon, 2 Feb 2009 10:13:06 -0800
Subject: [PATCH] libfc: check for err when recv and state is incorrect
References: bnc#473602
If we've just created an interface and the an rport is
logging in we may have a request on the wire (say PRLI).
If we destroy the interface, we'll go through each rport
on the disc->rports list and set each rport's state to NONE.
Then the lport will reset the EM. The EM reset will send a
CLOSED event to the prli_resp() handler which will notice
that the state != PRLI. In this case it frees the frame
pointer, decrements the refcount and unlocks the rport.
The problem is that there isn't a frame in this case. It's
just a pointer with an embedded error code. The free causes
an Oops.
This patch moves the error checking to be before the state
checking.
Signed-off-by: Robert Love <robert.w.love@intel.com>
Signed-off-by: Hannes Reinecke <hare@suse.de>
---
drivers/scsi/libfc/fc_lport.c | 50 +++++++++++++++++++++---------------------
drivers/scsi/libfc/fc_rport.c | 30 ++++++++++++-------------
2 files changed, 40 insertions(+), 40 deletions(-)
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -1031,17 +1031,17 @@ static void fc_lport_rft_id_resp(struct
FC_DEBUG_LPORT("Received a RFT_ID response\n");
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
if (lport->state != LPORT_ST_RFT_ID) {
FC_DBG("Received a RFT_ID response, but in state %s\n",
fc_lport_state(lport));
goto out;
}
- if (IS_ERR(fp)) {
- fc_lport_error(lport, fp);
- goto err;
- }
-
fh = fc_frame_header_get(fp);
ct = fc_frame_payload_get(fp, sizeof(*ct));
@@ -1083,17 +1083,17 @@ static void fc_lport_rpn_id_resp(struct
FC_DEBUG_LPORT("Received a RPN_ID response\n");
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
if (lport->state != LPORT_ST_RPN_ID) {
FC_DBG("Received a RPN_ID response, but in state %s\n",
fc_lport_state(lport));
goto out;
}
- if (IS_ERR(fp)) {
- fc_lport_error(lport, fp);
- goto err;
- }
-
fh = fc_frame_header_get(fp);
ct = fc_frame_payload_get(fp, sizeof(*ct));
if (fh && ct && fh->fh_type == FC_TYPE_CT &&
@@ -1133,17 +1133,17 @@ static void fc_lport_scr_resp(struct fc_
FC_DEBUG_LPORT("Received a SCR response\n");
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
if (lport->state != LPORT_ST_SCR) {
FC_DBG("Received a SCR response, but in state %s\n",
fc_lport_state(lport));
goto out;
}
- if (IS_ERR(fp)) {
- fc_lport_error(lport, fp);
- goto err;
- }
-
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC)
fc_lport_enter_ready(lport);
@@ -1359,17 +1359,17 @@ static void fc_lport_logo_resp(struct fc
FC_DEBUG_LPORT("Received a LOGO response\n");
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
if (lport->state != LPORT_ST_LOGO) {
FC_DBG("Received a LOGO response, but in state %s\n",
fc_lport_state(lport));
goto out;
}
- if (IS_ERR(fp)) {
- fc_lport_error(lport, fp);
- goto err;
- }
-
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC)
fc_lport_enter_reset(lport);
@@ -1443,17 +1443,17 @@ static void fc_lport_flogi_resp(struct f
FC_DEBUG_LPORT("Received a FLOGI response\n");
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
if (lport->state != LPORT_ST_FLOGI) {
FC_DBG("Received a FLOGI response, but in state %s\n",
fc_lport_state(lport));
goto out;
}
- if (IS_ERR(fp)) {
- fc_lport_error(lport, fp);
- goto err;
- }
-
fh = fc_frame_header_get(fp);
did = ntoh24(fh->fh_d_id);
if (fc_frame_payload_op(fp) == ELS_LS_ACC && did != 0) {
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -505,17 +505,17 @@ static void fc_rport_plogi_resp(struct f
FC_DEBUG_RPORT("Received a PLOGI response from port (%6x)\n",
rport->port_id);
+ if (IS_ERR(fp)) {
+ fc_rport_error_retry(rport, fp);
+ goto err;
+ }
+
if (rdata->rp_state != RPORT_ST_PLOGI) {
FC_DBG("Received a PLOGI response, but in state %s\n",
fc_rport_state(rport));
goto out;
}
- if (IS_ERR(fp)) {
- fc_rport_error_retry(rport, fp);
- goto err;
- }
-
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC &&
(plp = fc_frame_payload_get(fp, sizeof(*plp))) != NULL) {
@@ -614,17 +614,17 @@ static void fc_rport_prli_resp(struct fc
FC_DEBUG_RPORT("Received a PRLI response from port (%6x)\n",
rport->port_id);
+ if (IS_ERR(fp)) {
+ fc_rport_error_retry(rport, fp);
+ goto err;
+ }
+
if (rdata->rp_state != RPORT_ST_PRLI) {
FC_DBG("Received a PRLI response, but in state %s\n",
fc_rport_state(rport));
goto out;
}
- if (IS_ERR(fp)) {
- fc_rport_error_retry(rport, fp);
- goto err;
- }
-
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC) {
pp = fc_frame_payload_get(fp, sizeof(*pp));
@@ -764,17 +764,17 @@ static void fc_rport_rtv_resp(struct fc_
FC_DEBUG_RPORT("Received a RTV response from port (%6x)\n",
rport->port_id);
+ if (IS_ERR(fp)) {
+ fc_rport_error(rport, fp);
+ goto err;
+ }
+
if (rdata->rp_state != RPORT_ST_RTV) {
FC_DBG("Received a RTV response, but in state %s\n",
fc_rport_state(rport));
goto out;
}
- if (IS_ERR(fp)) {
- fc_rport_error(rport, fp);
- goto err;
- }
-
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC) {
struct fc_els_rtv_acc *rtv;

View File

@@ -0,0 +1,214 @@
From: Robert Love <robert.w.love@intel.com>
Subject: libfc: Ensure correct device_put/get usage (round 2)
References:
Reference counting was barely used and where used
it was incorrect. This patch creates a few simple
policies.
When the rport->dev [e.g. struct device] is initialized
it starts with a refcnt of 1. Whenever we're using the
rport we will increment the count. When we logoff we
should decrement the count to 0 and the 'release'
function will be called. The FC transport provides the
release function for real rports and libfc provides it
for rogue rports. When we switch from a rogue to real
rport we'll decrement the refcnt on the rogue rport
and increment it for the real rport, after we've created
it.
Any externally initiated action on an rport (login,
logoff) will not require the caller to increment and
decrement the refcnt.
For rport_login(), the rport will have just been created
and therefore no other thread would be able to access
this object.
For rport_logoff(), the rport will have been removed
from the list of rports and therefore no other thread
would be able to lookup() this rport.
This patch removes the get_device() from the rport_lookup
function. These are the places where it is called and why
we don't need a reference.
fc_disc_recv_rscn_req() - called for single port RSCNs
the disc mutex is held and
ensures that no other thread
will find this rport.
fc_disc_new_target() - Same. The rport cannot be looked up
so no other thread can free the rport.
This code looks buggy though, we
shouldn't be calling rport_login() on
a 'real' rport, which we could do.
fc_disc_single() - Same. disc mutex protects the list.
fc_lport_recv_req() - Similar, but this time the lport lock
ensures that no incoming requests are
processed until the current request
for an rport has returned.
When the rport layer needs to send a request it will
increment the count so that the EM can be confident that
the rport is present when making the callback. If
fc_remote_port_delete() is called before the response
callback, which is often the case for LOGO commands, the
refcnt will still have a value of 1 becuase we grabbed the
lock before the ctels_send() is called. The exchange would
have been removed and so the callback will be called with
an error code. After processing the error code we'll
decrement the refcnt for the last time and the rport will
be free'd.
Since point-to-point mode is not working this patch
does not consider point-to-point.
Signed-off-by: Robert Love <robert.w.love@intel.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/libfc/fc_disc.c | 5 +----
drivers/scsi/libfc/fc_lport.c | 5 ++---
drivers/scsi/libfc/fc_rport.c | 21 ++++++++++++++-------
3 files changed, 17 insertions(+), 14 deletions(-)
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -81,7 +81,6 @@ struct fc_rport *fc_disc_lookup_rport(co
if (rport->port_id == port_id) {
disc_found = 1;
found = rport;
- get_device(&found->dev);
break;
}
}
@@ -767,10 +766,8 @@ static void fc_disc_single(struct fc_dis
goto out;
rport = lport->tt.rport_lookup(lport, dp->ids.port_id);
- if (rport) {
+ if (rport)
fc_disc_del_target(disc, rport);
- put_device(&rport->dev); /* hold from lookup */
- }
new_rport = fc_rport_rogue_create(dp);
if (new_rport) {
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -908,10 +908,9 @@ static void fc_lport_recv_req(struct fc_
d_id = ntoh24(fh->fh_d_id);
rport = lport->tt.rport_lookup(lport, s_id);
- if (rport) {
+ if (rport)
lport->tt.rport_recv_req(sp, fp, rport);
- put_device(&rport->dev); /* hold from lookup */
- } else {
+ else {
rjt_data.fp = NULL;
rjt_data.reason = ELS_RJT_UNAB;
rjt_data.explan = ELS_EXPL_NONE;
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -111,16 +111,11 @@ struct fc_rport *fc_rport_rogue_create(s
rport->roles = dp->ids.roles;
rport->maxframe_size = FC_MIN_MAX_PAYLOAD;
/*
- * init the device, so other code can manipulate the rport as if
- * it came from the fc class. We also do an extra get because
- * libfc will free this rport instead of relying on the normal
- * refcounting.
- *
* Note: all this libfc rogue rport code will be removed for
* upstream so it fine that this is really ugly and hacky right now.
*/
device_initialize(&rport->dev);
- get_device(&rport->dev);
+ rport->dev.release = fc_rport_rogue_destroy; // XXX: bwalle
mutex_init(&rdata->rp_mutex);
rdata->local_port = dp->lp;
@@ -402,9 +397,9 @@ static void fc_rport_timeout(struct work
case RPORT_ST_NONE:
break;
}
- put_device(&rport->dev);
mutex_unlock(&rdata->rp_mutex);
+ put_device(&rport->dev);
}
/**
@@ -531,6 +526,7 @@ out:
fc_frame_free(fp);
err:
mutex_unlock(&rdata->rp_mutex);
+ put_device(&rport->dev);
}
/**
@@ -562,6 +558,8 @@ static void fc_rport_enter_plogi(struct
if (!lport->tt.elsct_send(lport, rport, fp, ELS_PLOGI,
fc_rport_plogi_resp, rport, lport->e_d_tov))
fc_rport_error(rport, fp);
+ else
+ get_device(&rport->dev);
}
/**
@@ -631,6 +629,7 @@ out:
fc_frame_free(fp);
err:
mutex_unlock(&rdata->rp_mutex);
+ put_device(&rport->dev);
}
/**
@@ -679,6 +678,7 @@ out:
fc_frame_free(fp);
err:
mutex_unlock(&rdata->rp_mutex);
+ put_device(&rport->dev);
}
/**
@@ -712,6 +712,8 @@ static void fc_rport_enter_prli(struct f
if (!lport->tt.elsct_send(lport, rport, fp, ELS_PRLI,
fc_rport_prli_resp, rport, lport->e_d_tov))
fc_rport_error(rport, fp);
+ else
+ get_device(&rport->dev);
}
/**
@@ -777,6 +779,7 @@ out:
fc_frame_free(fp);
err:
mutex_unlock(&rdata->rp_mutex);
+ put_device(&rport->dev);
}
/**
@@ -806,6 +809,8 @@ static void fc_rport_enter_rtv(struct fc
if (!lport->tt.elsct_send(lport, rport, fp, ELS_RTV,
fc_rport_rtv_resp, rport, lport->e_d_tov))
fc_rport_error(rport, fp);
+ else
+ get_device(&rport->dev);
}
/**
@@ -835,6 +840,8 @@ static void fc_rport_enter_logo(struct f
if (!lport->tt.elsct_send(lport, rport, fp, ELS_LOGO,
fc_rport_logo_resp, rport, lport->e_d_tov))
fc_rport_error(rport, fp);
+ else
+ get_device(&rport->dev);
}

View File

@@ -0,0 +1,27 @@
From: Vasu Dev <vasu.dev@intel.com>
Subject: libfc: handle RRQ exch timeout
References: bnc #465596
Cleanup exchange held due to RRQ when RRQ exch times out, in this case the
ABTS is already done causing RRQ req therefore proceeding with cleanup in
fc_exch_rrq_resp should be okay to restore exch resource.
Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/libfc/fc_exch.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -1605,7 +1605,7 @@ static void fc_exch_rrq_resp(struct fc_s
if (IS_ERR(fp)) {
int err = PTR_ERR(fp);
- if (err == -FC_EX_CLOSED)
+ if (err == -FC_EX_CLOSED || err == -FC_EX_TIMEOUT)
goto cleanup;
FC_DBG("Cannot process RRQ, because of frame error %d\n", err);
return;

View File

@@ -0,0 +1,114 @@
From: Robert Love <robert.w.love@intel.com>
Subject: [FcOE] Improve fc_lport.c locking comment block
References: bnc #459142
Signed-off-by: Robert Love <robert.w.love@intel.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/libfc/fc_lport.c | 76 ++++++++++++++++++++++++------------------
1 file changed, 45 insertions(+), 31 deletions(-)
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -18,34 +18,51 @@
*/
/*
- * General locking notes:
+ * PORT LOCKING NOTES
*
- * The lport and rport blocks both have mutexes that are used to protect
- * the port objects states. The main motivation for this protection is that
- * we don't want to be preparing a request/response in one context while
- * another thread "resets" the port in question. For example, if the lport
- * block is sending a SCR request to the directory server we don't want
- * the lport to be reset before we fill out the frame header's port_id. The
- * problem is that a reset would cause the lport's port_id to reset to 0.
- * If we don't protect the lport we'd spew incorrect frames.
- *
- * At the time of this writing there are two primary mutexes, one for the
- * lport and one for the rport. Since the lport uses the rport and makes
- * calls into that block the rport should never make calls that would cause
- * the lport's mutex to be locked. In other words, the lport's mutex is
- * considered the outer lock and the rport's lock is considered the inner
- * lock. The bottom line is that you can hold a lport's mutex and then
- * hold the rport's mutex, but not the other way around.
- *
- * The only complication to this rule is the callbacks from the rport to
- * the lport's rport_callback function. When rports become READY they make
- * a callback to the lport so that it can track them. In the case of the
- * directory server that callback might cause the lport to change its
- * state, implying that the lport mutex would need to be held. This problem
- * was solved by serializing the rport notifications to the lport and the
- * callback is made without holding the rport's lock.
+ * These comments only apply to the 'port code' which consists of the lport,
+ * disc and rport blocks.
*
- * lport locking notes:
+ * MOTIVATION
+ *
+ * The lport, disc and rport blocks all have mutexes that are used to protect
+ * those objects. The main motivation for these locks is to prevent from
+ * having an lport reset just before we send a frame. In that scenario the
+ * lport's FID would get set to zero and then we'd send a frame with an
+ * invalid SID. We also need to ensure that states don't change unexpectedly
+ * while processing another state.
+ *
+ * HEIRARCHY
+ *
+ * The following heirarchy defines the locking rules. A greater lock
+ * may be held before acquiring a lesser lock, but a lesser lock should never
+ * be held while attempting to acquire a greater lock. Here is the heirarchy-
+ *
+ * lport > disc, lport > rport, disc > rport
+ *
+ * CALLBACKS
+ *
+ * The callbacks cause complications with this scheme. There is a callback
+ * from the rport (to either lport or disc) and a callback from disc
+ * (to the lport).
+ *
+ * As rports exit the rport state machine a callback is made to the owner of
+ * the rport to notify success or failure. Since the callback is likely to
+ * cause the lport or disc to grab its lock we cannot hold the rport lock
+ * while making the callback. To ensure that the rport is not free'd while
+ * processing the callback the rport callbacks are serialized through a
+ * single-threaded workqueue. An rport would never be free'd while in a
+ * callback handler becuase no other rport work in this queue can be executed
+ * at the same time.
+ *
+ * When discovery succeeds or fails a callback is made to the lport as
+ * notification. Currently, succesful discovery causes the lport to take no
+ * action. A failure will cause the lport to reset. There is likely a circular
+ * locking problem with this implementation.
+ */
+
+/*
+ * LPORT LOCKING
*
* The critical sections protected by the lport's mutex are quite broad and
* may be improved upon in the future. The lport code and its locking doesn't
@@ -54,9 +71,9 @@
*
* The strategy is to lock whenever processing a request or response. Note
* that every _enter_* function corresponds to a state change. They generally
- * change the lports state and then sends a request out on the wire. We lock
+ * change the lports state and then send a request out on the wire. We lock
* before calling any of these functions to protect that state change. This
- * means that the entry points into the lport block to manage the locks while
+ * means that the entry points into the lport block manage the locks while
* the state machine can transition between states (i.e. _enter_* functions)
* while always staying protected.
*
@@ -68,9 +85,6 @@
* Retries also have to consider the locking. The retries occur from a work
* context and the work function will lock the lport and then retry the state
* (i.e. _enter_* function).
- *
- * The implication to all of this is that each lport can only process one
- * state at a time.
*/
#include <linux/timer.h>

View File

@@ -0,0 +1,50 @@
From: Robert Love <robert.w.love@intel.com>
Subject: Improve fc_rport.c locking comment block
References: 459142
checkpatch.pl was complaining about having spaces
after '*'s. It seemed to be a false positive. I split
the comment block into two blocks and it resolved the
ERROR.
Signed-off-by: Robert Love <robert.w.love@intel.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/libfc/fc_rport.c | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -18,20 +18,23 @@
*/
/*
+ * RPORT GENERAL INFO
+ *
* This file contains all processing regarding fc_rports. It contains the
* rport state machine and does all rport interaction with the transport class.
* There should be no other places in libfc that interact directly with the
* transport class in regards to adding and deleting rports.
*
* fc_rport's represent N_Port's within the fabric.
+ */
+
+/*
+ * RPORT LOCKING
*
- * rport locking notes:
- *
- * The rport should never hold the rport mutex and then lock the lport
- * mutex. The rport's mutex is considered lesser than the lport's mutex, so
- * the lport mutex can be held before locking the rport mutex, but not the
- * other way around. See the comment block at the top of fc_lport.c for more
- * details.
+ * The rport should never hold the rport mutex and then attempt to acquire
+ * either the lport or disc mutexes. The rport's mutex is considered lesser
+ * than both the lport's mutex and the disc mutex. Refer to fc_lport.c for
+ * more comments on the heirarchy.
*
* The locking strategy is similar to the lport's strategy. The lock protects
* the rport's states and is held and released by the entry points to the rport

View File

@@ -0,0 +1,217 @@
From: Chris Leech <christopher.leech@intel.com>
Subject: [FcOE] make fc_disc inline with the fc_lport structure
References: bnc #459142
The extra memory allocation we're not being checked for failure. Rather than
further complicating things, just make the discovery code required fields be
part of the lport structure.
Signed-off-by: Chris Leech <christopher.leech@intel.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/libfc/fc_disc.c | 80 +++++++-----------------------------------
drivers/scsi/libfc/fc_lport.c | 2 -
include/scsi/libfc.h | 22 ++++++++++-
3 files changed, 35 insertions(+), 69 deletions(-)
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -45,26 +45,6 @@ static int fc_disc_debug;
FC_DBG(fmt); \
} while (0)
-struct fc_disc {
- unsigned char retry_count;
- unsigned char delay;
- unsigned char pending;
- unsigned char requested;
- unsigned short seq_count;
- unsigned char buf_len;
- enum fc_disc_event event;
-
- void (*disc_callback)(struct fc_lport *,
- enum fc_disc_event);
-
- struct list_head rports;
- struct fc_lport *lport;
- struct mutex disc_mutex;
- struct fc_gpn_ft_resp partial_buf; /* partial name buffer */
- struct delayed_work disc_work;
-
-};
-
static void fc_disc_gpn_ft_req(struct fc_disc *);
static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *);
static int fc_disc_new_target(struct fc_disc *, struct fc_rport *,
@@ -83,14 +63,11 @@ static void fc_disc_restart(struct fc_di
struct fc_rport *fc_disc_lookup_rport(const struct fc_lport *lport,
u32 port_id)
{
- struct fc_disc *disc = lport->disc;
+ const struct fc_disc *disc = &lport->disc;
struct fc_rport *rport, *found = NULL;
struct fc_rport_libfc_priv *rdata;
int disc_found = 0;
- if (!disc)
- return NULL;
-
list_for_each_entry(rdata, &disc->rports, peers) {
rport = PRIV_TO_RPORT(rdata);
if (rport->port_id == port_id) {
@@ -108,27 +85,6 @@ struct fc_rport *fc_disc_lookup_rport(co
}
/**
- * fc_disc_alloc - Allocate a discovery work object
- * @lport: The FC lport associated with the discovery job
- */
-static inline struct fc_disc *fc_disc_alloc(struct fc_lport *lport)
-{
- struct fc_disc *disc;
-
- disc = kzalloc(sizeof(struct fc_disc), GFP_KERNEL);
- INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout);
- mutex_init(&disc->disc_mutex);
- INIT_LIST_HEAD(&disc->rports);
-
- disc->lport = lport;
- lport->disc = disc;
- disc->delay = FC_DISC_DELAY;
- disc->event = DISC_EV_NONE;
-
- return disc;
-}
-
-/**
* fc_disc_stop_rports - delete all the remote ports associated with the lport
* @disc: The discovery job to stop rports on
*
@@ -167,7 +123,7 @@ static void fc_disc_rport_callback(struc
enum fc_rport_event event)
{
struct fc_rport_libfc_priv *rdata = rport->dd_data;
- struct fc_disc *disc = lport->disc;
+ struct fc_disc *disc = &lport->disc;
int found = 0;
FC_DEBUG_DISC("Received a %d event for port (%6x)\n", event,
@@ -304,13 +260,7 @@ static void fc_disc_recv_req(struct fc_s
struct fc_lport *lport)
{
u8 op;
- struct fc_disc *disc = lport->disc;
-
- if (!disc) {
- FC_DBG("Received a request for an lport not managed "
- "by the discovery engine\n");
- return;
- }
+ struct fc_disc *disc = &lport->disc;
op = fc_frame_payload_op(fp);
switch (op) {
@@ -365,17 +315,7 @@ static void fc_disc_start(void (*disc_ca
{
struct fc_rport *rport;
struct fc_rport_identifiers ids;
- struct fc_disc *disc = lport->disc;
-
- if (!disc) {
- FC_DEBUG_DISC("No existing discovery job, "
- "creating one for lport (%6x)\n",
- fc_host_port_id(lport->host));
- disc = fc_disc_alloc(lport);
- } else
- FC_DEBUG_DISC("Found an existing discovery job "
- "for lport (%6x)\n",
- fc_host_port_id(lport->host));
+ struct fc_disc *disc = &lport->disc;
/*
* At this point we may have a new disc job or an existing
@@ -831,7 +771,7 @@ out:
*/
void fc_disc_stop(struct fc_lport *lport)
{
- struct fc_disc *disc = lport->disc;
+ struct fc_disc *disc = &lport->disc;
if (disc) {
cancel_delayed_work_sync(&disc->disc_work);
@@ -858,6 +798,7 @@ void fc_disc_stop_final(struct fc_lport
*/
int fc_disc_init(struct fc_lport *lport)
{
+ struct fc_disc *disc;
if (!lport->tt.disc_start)
lport->tt.disc_start = fc_disc_start;
@@ -874,6 +815,15 @@ int fc_disc_init(struct fc_lport *lport)
if (!lport->tt.rport_lookup)
lport->tt.rport_lookup = fc_disc_lookup_rport;
+ disc = &lport->disc;
+ INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout);
+ mutex_init(&disc->disc_mutex);
+ INIT_LIST_HEAD(&disc->rports);
+
+ disc->lport = lport;
+ disc->delay = FC_DISC_DELAY;
+ disc->event = DISC_EV_NONE;
+
return 0;
}
EXPORT_SYMBOL(fc_disc_init);
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -627,8 +627,6 @@ int fc_fabric_logoff(struct fc_lport *lp
{
lport->tt.disc_stop_final(lport);
mutex_lock(&lport->lp_mutex);
- kfree(lport->disc);
- lport->disc = NULL;
fc_lport_enter_logo(lport);
mutex_unlock(&lport->lp_mutex);
return 0;
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -572,7 +572,25 @@ struct libfc_function_template {
void (*disc_stop_final) (struct fc_lport *);
};
-struct fc_disc;
+/* information used by the discovery layer */
+struct fc_disc {
+ unsigned char retry_count;
+ unsigned char delay;
+ unsigned char pending;
+ unsigned char requested;
+ unsigned short seq_count;
+ unsigned char buf_len;
+ enum fc_disc_event event;
+
+ void (*disc_callback)(struct fc_lport *,
+ enum fc_disc_event);
+
+ struct list_head rports;
+ struct fc_lport *lport;
+ struct mutex disc_mutex;
+ struct fc_gpn_ft_resp partial_buf; /* partial name buffer */
+ struct delayed_work disc_work;
+};
struct fc_lport {
struct list_head list;
@@ -582,8 +600,8 @@ struct fc_lport {
struct fc_exch_mgr *emp;
struct fc_rport *dns_rp;
struct fc_rport *ptp_rp;
- struct fc_disc *disc;
void *scsi_priv;
+ struct fc_disc disc;
/* Operational Information */
struct libfc_function_template tt;

View File

@@ -0,0 +1,59 @@
From: Chris Leech <christopher.leech@intel.com>
Subject: [FcOE] make RSCN parsing more robust
References: bnc #459142
RSCN parsing needs to verify that the payload length specified in the RSCN ELS
message does not exceed the size of the actual frame received.
Signed-off-by: Chris Leech <christopher.leech@intel.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/libfc/fc_disc.c | 19 +++++++++++++++----
1 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
index 0416041..8b609e4 100644
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -173,17 +173,27 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
FC_DEBUG_DISC("Received an RSCN event on port (%6x)\n",
fc_host_port_id(lport->host));
+ /* make sure the frame contains an RSCN message */
rp = fc_frame_payload_get(fp, sizeof(*rp));
-
- if (!rp || rp->rscn_page_len != sizeof(*pp))
+ if (!rp)
goto reject;
-
+ /* make sure the page length is as expected (4 bytes) */
+ if (rp->rscn_page_len != sizeof(*pp))
+ goto reject;
+ /* get the RSCN payload length */
len = ntohs(rp->rscn_plen);
if (len < sizeof(*rp))
goto reject;
+ /* make sure the frame contains the expected payload */
+ rp = fc_frame_payload_get(fp, len);
+ if (!rp)
+ goto reject;
+ /* payload must be a multiple of the RSCN page size */
len -= sizeof(*rp);
+ if (len % sizeof(*pp))
+ goto reject;
- for (pp = (void *)(rp + 1); len; len -= sizeof(*pp), pp++) {
+ for (pp = (void *)(rp + 1); len > 0; len -= sizeof(*pp), pp++) {
ev_qual = pp->rscn_page_flags >> ELS_RSCN_EV_QUAL_BIT;
ev_qual &= ELS_RSCN_EV_QUAL_MASK;
fmt = pp->rscn_page_flags >> ELS_RSCN_ADDR_FMT_BIT;
@@ -239,6 +249,7 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
fc_frame_free(fp);
return;
reject:
+ FC_DEBUG_DISC("Received a bad RSCN frame\n");
rjt_data.fp = NULL;
rjt_data.reason = ELS_RJT_LOGIC;
rjt_data.explan = ELS_EXPL_NONE;

View File

@@ -0,0 +1,114 @@
From: Chris Leech <christopher.leech@intel.com>
Subject: [FcOE] make sure we access the CRC safely
References: bnc #459142
Even when fcoe verified that the EOF and CRC trailer bytes were there, when
the CRC check was delayed for solicited SCSI data libfc would look past what
was marked as valid data in the frame to find the CRC in the FCoE trailer.
Instead, pass the CRC to libfc in the context block.
Signed-off-by: Chris Leech <christopher.leech@intel.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/fcoe/libfcoe.c | 20 ++++++++++++++------
drivers/scsi/libfc/fc_fcp.c | 2 +-
drivers/scsi/libfc/fc_frame.c | 2 +-
include/scsi/fc_frame.h | 3 +++
4 files changed, 19 insertions(+), 8 deletions(-)
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -527,7 +527,7 @@ int fcoe_percpu_receive_thread(void *arg
struct fcoe_dev_stats *stats;
struct fc_frame_header *fh;
struct sk_buff *skb;
- struct fcoe_crc_eof *cp;
+ struct fcoe_crc_eof crc_eof;
struct fc_frame *fp;
u8 *mac = NULL;
struct fcoe_softc *fc;
@@ -604,13 +604,21 @@ int fcoe_percpu_receive_thread(void *arg
}
fp = (struct fc_frame *)skb;
- cp = (struct fcoe_crc_eof *)(skb->data + fr_len);
fc_frame_init(fp);
fr_dev(fp) = lp;
fr_sof(fp) = hp->fcoe_sof;
- fr_eof(fp) = cp->fcoe_eof;
- /* trim off the CRC and EOF trailer*/
- skb_trim(skb, fr_len);
+
+ /* Copy out the CRC and EOF trailer for access */
+ if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) {
+ kfree_skb(skb);
+ continue;
+ }
+ fr_eof(fp) = crc_eof.fcoe_eof;
+ fr_crc(fp) = crc_eof.fcoe_crc32;
+ if (pskb_trim(skb, fr_len)) {
+ kfree_skb(skb);
+ continue;
+ }
/*
* We only check CRC if no offload is available and if it is
@@ -629,7 +637,7 @@ int fcoe_percpu_receive_thread(void *arg
continue;
}
if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
- if (le32_to_cpu(cp->fcoe_crc32) !=
+ if (le32_to_cpu(fr_crc(fp)) !=
~crc32(~0, skb->data, fr_len)) {
if (debug_fcoe || stats->InvalidCRCCount < 5)
printk(KERN_WARNING "fcoe: dropping "
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -356,7 +356,7 @@ static void fc_fcp_recv_data(struct fc_f
len += 4 - (len % 4);
}
- if (~crc != le32_to_cpu(*(__le32 *)(buf + len))) {
+ if (~crc != le32_to_cpu(fr_crc(fp))) {
crc_err:
stats = lp->dev_stats[smp_processor_id()];
stats->ErrorFrames++;
--- a/drivers/scsi/libfc/fc_frame.c
+++ b/drivers/scsi/libfc/fc_frame.c
@@ -42,7 +42,7 @@ u32 fc_frame_crc_check(struct fc_frame *
len = (fr_len(fp) + 3) & ~3; /* round up length to include fill */
bp = (const u8 *) fr_hdr(fp);
crc = ~crc32(~0, bp, len);
- error = crc ^ *(u32 *) (bp + len);
+ error = crc ^ fr_crc(fp);
return error;
}
EXPORT_SYMBOL(fc_frame_crc_check);
--- a/include/scsi/fc_frame.h
+++ b/include/scsi/fc_frame.h
@@ -56,6 +56,7 @@
#define fr_max_payload(fp) (fr_cb(fp)->fr_max_payload)
#define fr_cmd(fp) (fr_cb(fp)->fr_cmd)
#define fr_dir(fp) (fr_cmd(fp)->sc_data_direction)
+#define fr_crc(fp) (fr_cb(fp)->fr_crc)
struct fc_frame {
struct sk_buff skb;
@@ -66,12 +67,14 @@ struct fcoe_rcv_info {
struct fc_lport *fr_dev; /* transport layer private pointer */
struct fc_seq *fr_seq; /* for use with exchange manager */
struct scsi_cmnd *fr_cmd; /* for use of scsi command */
+ u32 fr_crc;
u16 fr_max_payload; /* max FC payload */
enum fc_sof fr_sof; /* start of frame delimiter */
enum fc_eof fr_eof; /* end of frame delimiter */
u8 fr_flags; /* flags - see below */
};
+
/*
* Get fc_frame pointer for an skb that's already been imported.
*/

View File

@@ -0,0 +1,90 @@
From: Abhijeet Joglekar <abjoglek@cisco.com>
Subject: libfc: Pass lport in exch_mgr_reset
References: bnc #465596
fc_exch_mgr structure is private to fc_exch.c. To export exch_mgr_reset to
transport, transport needs access to the exch manager. Change
exch_mgr_reset to use lport param which is the shared structure between
libFC and transport.
Alternatively, fc_exch_mgr definition can be moved to libfc.h so that lport
can be accessed from mp*.
Signed-off-by: Abhijeet Joglekar <abjoglek@cisco.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/libfc/fc_exch.c | 3 ++-
drivers/scsi/libfc/fc_lport.c | 4 ++--
drivers/scsi/libfc/fc_rport.c | 4 ++--
include/scsi/libfc.h | 4 ++--
4 files changed, 8 insertions(+), 7 deletions(-)
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -1478,10 +1478,11 @@ static void fc_exch_reset(struct fc_exch
* If sid is non-zero, reset only exchanges we source from that FID.
* If did is non-zero, reset only exchanges destined to that FID.
*/
-void fc_exch_mgr_reset(struct fc_exch_mgr *mp, u32 sid, u32 did)
+void fc_exch_mgr_reset(struct fc_lport *lp, u32 sid, u32 did)
{
struct fc_exch *ep;
struct fc_exch *next;
+ struct fc_exch_mgr *mp = lp->emp;
spin_lock_bh(&mp->em_lock);
restart:
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -640,7 +640,7 @@ int fc_lport_destroy(struct fc_lport *lp
{
lport->tt.frame_send = fc_frame_drop;
lport->tt.fcp_abort_io(lport);
- lport->tt.exch_mgr_reset(lport->emp, 0, 0);
+ lport->tt.exch_mgr_reset(lport, 0, 0);
return 0;
}
EXPORT_SYMBOL(fc_lport_destroy);
@@ -951,7 +951,7 @@ static void fc_lport_enter_reset(struct
lport->tt.disc_stop(lport);
- lport->tt.exch_mgr_reset(lport->emp, 0, 0);
+ lport->tt.exch_mgr_reset(lport, 0, 0);
fc_host_fabric_name(lport->host) = 0;
fc_host_port_id(lport->host) = 0;
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -1302,7 +1302,7 @@ void fc_rport_terminate_io(struct fc_rpo
struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
- lport->tt.exch_mgr_reset(lport->emp, 0, rport->port_id);
- lport->tt.exch_mgr_reset(lport->emp, rport->port_id, 0);
+ lport->tt.exch_mgr_reset(lport, 0, rport->port_id);
+ lport->tt.exch_mgr_reset(lport, rport->port_id, 0);
}
EXPORT_SYMBOL(fc_rport_terminate_io);
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -469,7 +469,7 @@ struct libfc_function_template {
* If s_id is non-zero, reset only exchanges originating from that FID.
* If d_id is non-zero, reset only exchanges sending to that FID.
*/
- void (*exch_mgr_reset)(struct fc_exch_mgr *,
+ void (*exch_mgr_reset)(struct fc_lport *,
u32 s_id, u32 d_id);
void (*rport_flush_queue)(void);
@@ -908,7 +908,7 @@ struct fc_seq *fc_seq_start_next(struct
* If s_id is non-zero, reset only exchanges originating from that FID.
* If d_id is non-zero, reset only exchanges sending to that FID.
*/
-void fc_exch_mgr_reset(struct fc_exch_mgr *, u32 s_id, u32 d_id);
+void fc_exch_mgr_reset(struct fc_lport *, u32 s_id, u32 d_id);
/*
* Functions for fc_functions_template

View File

@@ -0,0 +1,91 @@
From: Robert Love <robert.w.love@intel.com>
Subject: [FcOE] Set the release function for the rport's kobject (round 2)
References: bnc #459142
We need to be better about reference counting. The first
step is to make use of the release function that is called
when the reference count drops to 0.
There was some inital push back by Joe on this patch. We
talked off-list and agreed that the benefit of not having
to check whether a rport is rogue or real overweighed the
fact that we might be using reference counting on objects
(rogue) that cannot be acted on by another thread.
There is likely room for improvement here, but this should
be a stable start.
Signed-off-by: Robert Love <robert.w.love@intel.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/libfc/fc_rport.c | 18 ++++++++++--------
include/scsi/libfc.h | 1 -
2 files changed, 10 insertions(+), 9 deletions(-)
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -93,6 +93,13 @@ static const char *fc_rport_state_names[
[RPORT_ST_LOGO] = "LOGO",
};
+static void fc_rport_rogue_destroy(struct device *dev)
+{
+ struct fc_rport *rport = dev_to_rport(dev);
+ FC_DEBUG_RPORT("Destroying rogue rport (%6x)\n", rport->port_id);
+ kfree(rport);
+}
+
struct fc_rport *fc_rport_rogue_create(struct fc_disc_port *dp)
{
struct fc_rport *rport;
@@ -115,7 +122,7 @@ struct fc_rport *fc_rport_rogue_create(s
* upstream so it fine that this is really ugly and hacky right now.
*/
device_initialize(&rport->dev);
- rport->dev.release = fc_rport_rogue_destroy; // XXX: bwalle
+ rport->dev.release = fc_rport_rogue_destroy;
mutex_init(&rdata->rp_mutex);
rdata->local_port = dp->lp;
@@ -137,11 +144,6 @@ struct fc_rport *fc_rport_rogue_create(s
return rport;
}
-void fc_rport_rogue_destroy(struct fc_rport *rport)
-{
- kfree(rport);
-}
-
/**
* fc_rport_state - return a string for the state the rport is in
* @rport: The rport whose state we want to get a string for
@@ -263,7 +265,7 @@ static void fc_rport_work(struct work_st
"(%6x).\n", ids.port_id);
event = RPORT_EV_FAILED;
}
- fc_rport_rogue_destroy(rport);
+ put_device(&rport->dev);
rport = new_rport;
rdata = new_rport->dd_data;
if (rport_ops->event_callback)
@@ -276,7 +278,7 @@ static void fc_rport_work(struct work_st
if (rport_ops->event_callback)
rport_ops->event_callback(lport, rport, event);
if (trans_state == FC_PORTSTATE_ROGUE)
- fc_rport_rogue_destroy(rport);
+ put_device(&rport->dev);
else
fc_remote_port_delete(rport);
} else
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -169,7 +169,6 @@ struct fc_rport_libfc_priv {
(struct fc_rport_libfc_priv *)((void *)x + sizeof(struct fc_rport));
struct fc_rport *fc_rport_rogue_create(struct fc_disc_port *);
-void fc_rport_rogue_destroy(struct fc_rport *);
static inline void fc_rport_set_name(struct fc_rport *rport, u64 wwpn, u64 wwnn)
{

View File

@@ -0,0 +1,56 @@
From: Vasu Dev <vasu.dev@intel.com>
Subject: [FcOE] updated comment for order of em and ex locks
References: bnc #459142
The fc_exch is public but em_lock is static to fc_exch.c,
so updated comment only in fc_exch.c on order of these locks.
Also removed seq.f_ctl from comments since this field is
already removed.
Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/libfc/fc_exch.c | 5 ++++-
include/scsi/libfc.h | 5 ++---
2 files changed, 6 insertions(+), 4 deletions(-)
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -68,7 +68,8 @@ static struct kmem_cache *fc_em_cachep;
*/
struct fc_exch_mgr {
enum fc_class class; /* default class for sequences */
- spinlock_t em_lock; /* exchange manager lock */
+ spinlock_t em_lock; /* exchange manager lock,
+ must be taken before ex_lock */
u16 last_xid; /* last allocated exchange ID */
u16 min_xid; /* min exchange ID */
u16 max_xid; /* max exchange ID */
@@ -179,6 +180,8 @@ static struct fc_seq *fc_seq_start_next_
* sequence allocation and deallocation must be locked.
* - exchange refcnt can be done atomicly without locks.
* - sequence allocation must be locked by exch lock.
+ * - If the em_lock and ex_lock must be taken at the same time, then the
+ * em_lock must be taken before the ex_lock.
*/
/*
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -299,11 +299,10 @@ struct fc_seq {
/*
* Exchange.
*
- * Locking notes: The ex_lock protects changes to the following fields:
- * esb_stat, f_ctl, seq.ssb_stat, seq.f_ctl.
+ * Locking notes: The ex_lock protects following items:
+ * state, esb_stat, f_ctl, seq.ssb_stat
* seq_id
* sequence allocation
- *
*/
struct fc_exch {
struct fc_exch_mgr *em; /* exchange manager */

View File

@@ -0,0 +1,35 @@
From: Vasu Dev <vasu.dev@intel.com>
Subject: [FcOE] updated libfc fcoe module ver to 1.0.6
References: bnc #459142
Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/fcoe/libfcoe.c | 2 +-
drivers/scsi/libfc/fc_fcp.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -59,7 +59,7 @@ static int debug_fcoe;
MODULE_AUTHOR("Open-FCoE.org");
MODULE_DESCRIPTION("FCoE");
MODULE_LICENSE("GPL");
-MODULE_VERSION("1.0.5");
+MODULE_VERSION("1.0.6");
/* fcoe host list */
LIST_HEAD(fcoe_hostlist);
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -42,7 +42,7 @@
MODULE_AUTHOR("Open-FCoE.org");
MODULE_DESCRIPTION("libfc");
MODULE_LICENSE("GPL");
-MODULE_VERSION("1.0.5");
+MODULE_VERSION("1.0.6");
static int fc_fcp_debug;

View File

@@ -0,0 +1,275 @@
From: Robert Love <robert.w.love@intel.com>
Subject: use an operations structure for rport callbacks
References: bnc #459142
This was called out for the disc callbacks in review
comments when submitting to linux-scsi. It needed to be
fixed for the rport callbacks too.
This patch also fixes some spacing in the fc_rport
structure definition as well as renaming the fc_lport_rport_event()
function to fc_lport_rport_callback() to more clearly
identify what it's doing.
Signed-off-by: Robert Love <robert.w.love@intel.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/libfc/fc_disc.c | 18 +++++++++-----
drivers/scsi/libfc/fc_lport.c | 16 ++++++++-----
drivers/scsi/libfc/fc_rport.c | 19 +++++++--------
include/scsi/libfc.h | 51 ++++++++++++++++++++++--------------------
4 files changed, 57 insertions(+), 47 deletions(-)
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -154,7 +154,7 @@ void fc_disc_stop_rports(struct fc_disc
}
/**
- * fc_disc_rport_event - Event handler for rport events
+ * fc_disc_rport_callback - Event handler for rport events
* @lport: The lport which is receiving the event
* @rport: The rport which the event has occured on
* @event: The event that occured
@@ -162,9 +162,9 @@ void fc_disc_stop_rports(struct fc_disc
* Locking Note: The rport lock should not be held when calling
* this function.
*/
-static void fc_disc_rport_event(struct fc_lport *lport,
- struct fc_rport *rport,
- enum fc_lport_event event)
+static void fc_disc_rport_callback(struct fc_lport *lport,
+ struct fc_rport *rport,
+ enum fc_rport_event event)
{
struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_disc *disc = lport->disc;
@@ -420,6 +420,10 @@ static void fc_disc_start(void (*disc_ca
mutex_unlock(&disc->disc_mutex);
}
+static struct fc_rport_operations fc_disc_rport_ops = {
+ .event_callback = fc_disc_rport_callback,
+};
+
/**
* fc_disc_new_target - Handle new target found by discovery
* @lport: FC local port
@@ -475,7 +479,7 @@ static int fc_disc_new_target(struct fc_
}
if (rport) {
rp = rport->dd_data;
- rp->event_callback = fc_disc_rport_event;
+ rp->ops = &fc_disc_rport_ops;
rp->rp_state = RPORT_ST_INIT;
lport->tt.rport_login(rport);
}
@@ -658,7 +662,7 @@ static int fc_disc_gpn_ft_parse(struct f
rport = fc_rport_rogue_create(&dp);
if (rport) {
rdata = rport->dd_data;
- rdata->event_callback = fc_disc_rport_event;
+ rdata->ops = &fc_disc_rport_ops;
rdata->local_port = lport;
lport->tt.rport_login(rport);
} else
@@ -812,7 +816,7 @@ static void fc_disc_single(struct fc_dis
new_rport = fc_rport_rogue_create(dp);
if (new_rport) {
rdata = new_rport->dd_data;
- rdata->event_callback = fc_disc_rport_event;
+ rdata->ops = &fc_disc_rport_ops;
kfree(dp);
lport->tt.rport_login(new_rport);
}
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -38,7 +38,7 @@
* hold the rport's mutex, but not the other way around.
*
* The only complication to this rule is the callbacks from the rport to
- * the lport's event_callback function. When rports become READY they make
+ * the lport's rport_callback function. When rports become READY they make
* a callback to the lport so that it can track them. In the case of the
* directory server that callback might cause the lport to change its
* state, implying that the lport mutex would need to be held. This problem
@@ -125,7 +125,7 @@ static int fc_frame_drop(struct fc_lport
}
/**
- * fc_lport_rport_event - Event handler for rport events
+ * fc_lport_rport_callback - Event handler for rport events
* @lport: The lport which is receiving the event
* @rport: The rport which the event has occured on
* @event: The event that occured
@@ -133,9 +133,9 @@ static int fc_frame_drop(struct fc_lport
* Locking Note: The rport lock should not be held when calling
* this function.
*/
-static void fc_lport_rport_event(struct fc_lport *lport,
- struct fc_rport *rport,
- enum fc_lport_event event)
+static void fc_lport_rport_callback(struct fc_lport *lport,
+ struct fc_rport *rport,
+ enum fc_rport_event event)
{
FC_DEBUG_LPORT("Received a %d event for port (%6x)\n", event,
rport->port_id);
@@ -1265,6 +1265,10 @@ static void fc_lport_enter_rpn_id(struct
fc_lport_error(lport, fp);
}
+static struct fc_rport_operations fc_lport_rport_ops = {
+ .event_callback = fc_lport_rport_callback,
+};
+
/**
* fc_rport_enter_dns - Create a rport to the name server
* @lport: Fibre Channel local port requesting a rport for the name server
@@ -1294,7 +1298,7 @@ static void fc_lport_enter_dns(struct fc
goto err;
rdata = rport->dd_data;
- rdata->event_callback = fc_lport_rport_event;
+ rdata->ops = &fc_lport_rport_ops;
lport->tt.rport_login(rport);
return;
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -125,7 +125,7 @@ struct fc_rport *fc_rport_rogue_create(s
rdata->rp_state = RPORT_ST_INIT;
rdata->event = RPORT_EV_NONE;
rdata->flags = FC_RP_FLAGS_REC_SUPPORTED;
- rdata->event_callback = NULL;
+ rdata->ops = NULL;
rdata->e_d_tov = dp->lp->e_d_tov;
rdata->r_a_tov = dp->lp->r_a_tov;
INIT_DELAYED_WORK(&rdata->retry_work, fc_rport_timeout);
@@ -216,16 +216,15 @@ static void fc_rport_work(struct work_st
{
struct fc_rport_libfc_priv *rdata =
container_of(work, struct fc_rport_libfc_priv, event_work);
- enum fc_lport_event event;
+ enum fc_rport_event event;
enum fc_rport_trans_state trans_state;
struct fc_lport *lport = rdata->local_port;
- void (*event_callback)(struct fc_lport *, struct fc_rport *,
- enum fc_lport_event);
+ struct fc_rport_operations *rport_ops;
struct fc_rport *rport = PRIV_TO_RPORT(rdata);
mutex_lock(&rdata->rp_mutex);
event = rdata->event;
- event_callback = rdata->event_callback;
+ rport_ops = rdata->ops;
if (event == RPORT_EV_CREATED) {
struct fc_rport *new_rport;
@@ -250,7 +249,7 @@ static void fc_rport_work(struct work_st
new_rdata = new_rport->dd_data;
new_rdata->e_d_tov = rdata->e_d_tov;
new_rdata->r_a_tov = rdata->r_a_tov;
- new_rdata->event_callback = rdata->event_callback;
+ new_rdata->ops = rdata->ops;
new_rdata->local_port = rdata->local_port;
new_rdata->flags = FC_RP_FLAGS_REC_SUPPORTED;
new_rdata->trans_state = FC_PORTSTATE_REAL;
@@ -269,15 +268,15 @@ static void fc_rport_work(struct work_st
fc_rport_rogue_destroy(rport);
rport = new_rport;
rdata = new_rport->dd_data;
- if (event_callback)
- event_callback(lport, rport, event);
+ if (rport_ops->event_callback)
+ rport_ops->event_callback(lport, rport, event);
} else if ((event == RPORT_EV_FAILED) ||
(event == RPORT_EV_LOGO) ||
(event == RPORT_EV_STOP)) {
trans_state = rdata->trans_state;
mutex_unlock(&rdata->rp_mutex);
- if (event_callback)
- event_callback(lport, rport, event);
+ if (rport_ops->event_callback)
+ rport_ops->event_callback(lport, rport, event);
if (trans_state == FC_PORTSTATE_ROGUE)
fc_rport_rogue_destroy(rport);
else
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -89,14 +89,6 @@ enum fc_disc_event {
DISC_EV_FAILED
};
-enum fc_lport_event {
- RPORT_EV_NONE = 0,
- RPORT_EV_CREATED,
- RPORT_EV_FAILED,
- RPORT_EV_STOP,
- RPORT_EV_LOGO
-};
-
enum fc_rport_state {
RPORT_ST_NONE = 0,
RPORT_ST_INIT, /* initialized */
@@ -126,6 +118,19 @@ struct fc_disc_port {
struct work_struct rport_work;
};
+enum fc_rport_event {
+ RPORT_EV_NONE = 0,
+ RPORT_EV_CREATED,
+ RPORT_EV_FAILED,
+ RPORT_EV_STOP,
+ RPORT_EV_LOGO
+};
+
+struct fc_rport_operations {
+ void (*event_callback)(struct fc_lport *, struct fc_rport *,
+ enum fc_rport_event);
+};
+
/**
* struct fc_rport_libfc_priv - libfc internal information about a remote port
* @local_port: Fibre Channel host port instance
@@ -140,24 +145,22 @@ struct fc_disc_port {
* @event_callback: Callback for rport READY, FAILED or LOGO
*/
struct fc_rport_libfc_priv {
- struct fc_lport *local_port;
- enum fc_rport_state rp_state;
- u16 flags;
+ struct fc_lport *local_port;
+ enum fc_rport_state rp_state;
+ u16 flags;
#define FC_RP_FLAGS_REC_SUPPORTED (1 << 0)
#define FC_RP_FLAGS_RETRY (1 << 1)
- u16 max_seq;
- unsigned int retries;
- unsigned int e_d_tov;
- unsigned int r_a_tov;
- enum fc_rport_trans_state trans_state;
- struct mutex rp_mutex;
- struct delayed_work retry_work;
- enum fc_lport_event event;
- void (*event_callback)(struct fc_lport *,
- struct fc_rport *,
- enum fc_lport_event);
- struct list_head peers;
- struct work_struct event_work;
+ u16 max_seq;
+ unsigned int retries;
+ unsigned int e_d_tov;
+ unsigned int r_a_tov;
+ enum fc_rport_trans_state trans_state;
+ struct mutex rp_mutex;
+ struct delayed_work retry_work;
+ enum fc_rport_event event;
+ struct fc_rport_operations *ops;
+ struct list_head peers;
+ struct work_struct event_work;
};
#define PRIV_TO_RPORT(x) \

View File

@@ -0,0 +1,41 @@
From: Abhijeet Joglekar <abjoglek@cisco.com>
Subject: libfc: when rport goes away (re-plogi), clean up exchanges to/from rport
References: bnc #465596
When a rport goes away, libFC does a plogi which will reset exchanges
at the rport. Clean exchanges at our end, both in transport and libFC.
If transport hooks into exch_mgr_reset, it will call back into
fc_exch_mgr_reset() to clean up libFC exchanges.
Signed-off-by: Abhijeet Joglekar <abjoglek@cisco.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/libfc/fc_rport.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -215,6 +215,7 @@ static void fc_rport_state_enter(struct
static void fc_rport_work(struct work_struct *work)
{
+ u32 port_id;
struct fc_rport_libfc_priv *rdata =
container_of(work, struct fc_rport_libfc_priv, event_work);
enum fc_rport_event event;
@@ -280,8 +281,12 @@ static void fc_rport_work(struct work_st
rport_ops->event_callback(lport, rport, event);
if (trans_state == FC_PORTSTATE_ROGUE)
put_device(&rport->dev);
- else
+ else {
+ port_id = rport->port_id;
fc_remote_port_delete(rport);
+ lport->tt.exch_mgr_reset(lport, 0, port_id);
+ lport->tt.exch_mgr_reset(lport, port_id, 0);
+ }
} else
mutex_unlock(&rdata->rp_mutex);
}

View File

@@ -0,0 +1,377 @@
From: Vasu Dev <vasu.dev@intel.com>
Subject: libfc, fcoe: fixed locking issues with lport->lp_mutex around lport->link_status
Patch-mainline: 6d235742e63f6b8912d8b200b75f9aa6d48f3e07
References: bnc #468053
The fcoe_xmit could call fc_pause in case the pending skb queue len is larger
than FCOE_MAX_QUEUE_DEPTH, the fc_pause was trying to grab lport->lp_muex to
change lport->link_status and that had these issues :-
1. The fcoe_xmit was getting called with bh disabled, thus causing
"BUG: scheduling while atomic" when grabbing lport->lp_muex with bh disabled.
2. fc_linkup and fc_linkdown function calls lport_enter function with
lport->lp_mutex held and these enter function in turn calls fcoe_xmit to send
lport related FC frame, e.g. fc_linkup => fc_lport_enter_flogi to send flogi
req. In this case grabbing the same lport->lp_mutex again in fc_puase from
fcoe_xmit would cause deadlock.
The lport->lp_mutex was used for setting FC_PAUSE in fcoe_xmit path but
FC_PAUSE bit was not used anywhere beside just setting and clear this
bit in lport->link_status, instead used a separate field qfull in fc_lport
to eliminate need for lport->lp_mutex to track pending queue full condition
and in turn avoid above described two locking issues.
Also added check for lp->qfull in fc_fcp_lport_queue_ready to trigger
SCSI_MLQUEUE_HOST_BUSY when lp->qfull is set to prevent more scsi-ml cmds
while lp->qfull is set.
This patch eliminated FC_LINK_UP and FC_PAUSE and instead used dedicated
fields in fc_lport for this, this simplified all related conditional
code.
Also removed fc_pause and fc_unpause functions and instead used newly added
lport->qfull directly in fcoe.
Also fixed a circular locking in fc_exch_recv_abts.
These issues were blocking large file copy to a 2TB lun.
Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/fcoe/fcoe_sw.c | 6 +++---
drivers/scsi/fcoe/libfcoe.c | 41 +++++++++++++++++------------------------
drivers/scsi/libfc/fc_exch.c | 2 +-
drivers/scsi/libfc/fc_fcp.c | 6 +++---
drivers/scsi/libfc/fc_lport.c | 38 +++++++-------------------------------
drivers/scsi/libfc/fc_rport.c | 2 +-
include/scsi/libfc.h | 12 ++----------
7 files changed, 34 insertions(+), 73 deletions(-)
--- a/drivers/scsi/fcoe/fcoe_sw.c
+++ b/drivers/scsi/fcoe/fcoe_sw.c
@@ -116,7 +116,8 @@ static int fcoe_sw_lport_config(struct f
{
int i = 0;
- lp->link_status = 0;
+ lp->link_up = 0;
+ lp->qfull = 0;
lp->max_retry_count = 3;
lp->e_d_tov = 2 * 1000; /* FC-FS default */
lp->r_a_tov = 2 * 2 * 1000;
@@ -181,9 +182,8 @@ static int fcoe_sw_netdev_config(struct
if (fc_set_mfs(lp, mfs))
return -EINVAL;
- lp->link_status = ~FC_PAUSE & ~FC_LINK_UP;
if (!fcoe_link_ok(lp))
- lp->link_status |= FC_LINK_UP;
+ lp->link_up = 1;
/* offload features support */
if (fc->real_dev->features & NETIF_F_SG)
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -505,7 +505,7 @@ int fcoe_xmit(struct fc_lport *lp, struc
if (rc) {
fcoe_insert_wait_queue(lp, skb);
if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
- fc_pause(lp);
+ lp->qfull = 1;
}
return 0;
@@ -719,7 +719,7 @@ static void fcoe_recv_flogi(struct fcoe_
* fcoe_watchdog - fcoe timer callback
* @vp:
*
- * This checks the pending queue length for fcoe and put fcoe to be paused state
+ * This checks the pending queue length for fcoe and set lport qfull
* if the FCOE_MAX_QUEUE_DEPTH is reached. This is done for all fc_lport on the
* fcoe_hostlist.
*
@@ -729,17 +729,17 @@ void fcoe_watchdog(ulong vp)
{
struct fc_lport *lp;
struct fcoe_softc *fc;
- int paused = 0;
+ int qfilled = 0;
read_lock(&fcoe_hostlist_lock);
list_for_each_entry(fc, &fcoe_hostlist, list) {
lp = fc->lp;
if (lp) {
if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
- paused = 1;
+ qfilled = 1;
if (fcoe_check_wait_queue(lp) < FCOE_MAX_QUEUE_DEPTH) {
- if (paused)
- fc_unpause(lp);
+ if (qfilled)
+ lp->qfull = 0;
}
}
}
@@ -768,8 +768,7 @@ void fcoe_watchdog(ulong vp)
**/
static int fcoe_check_wait_queue(struct fc_lport *lp)
{
- int rc, unpause = 0;
- int paused = 0;
+ int rc;
struct sk_buff *skb;
struct fcoe_softc *fc;
@@ -777,10 +776,10 @@ static int fcoe_check_wait_queue(struct
spin_lock_bh(&fc->fcoe_pending_queue.lock);
/*
- * is this interface paused?
+ * if interface pending queue full then set qfull in lport.
*/
if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
- paused = 1;
+ lp->qfull = 1;
if (fc->fcoe_pending_queue.qlen) {
while ((skb = __skb_dequeue(&fc->fcoe_pending_queue)) != NULL) {
spin_unlock_bh(&fc->fcoe_pending_queue.lock);
@@ -792,11 +791,9 @@ static int fcoe_check_wait_queue(struct
spin_lock_bh(&fc->fcoe_pending_queue.lock);
}
if (fc->fcoe_pending_queue.qlen < FCOE_MAX_QUEUE_DEPTH)
- unpause = 1;
+ lp->qfull = 0;
}
spin_unlock_bh(&fc->fcoe_pending_queue.lock);
- if ((unpause) && (paused))
- fc_unpause(lp);
return fc->fcoe_pending_queue.qlen;
}
@@ -874,7 +871,7 @@ static int fcoe_device_notification(stru
struct net_device *real_dev = ptr;
struct fcoe_softc *fc;
struct fcoe_dev_stats *stats;
- u16 new_status;
+ u32 new_link_up;
u32 mfs;
int rc = NOTIFY_OK;
@@ -891,17 +888,15 @@ static int fcoe_device_notification(stru
goto out;
}
- new_status = lp->link_status;
+ new_link_up = lp->link_up;
switch (event) {
case NETDEV_DOWN:
case NETDEV_GOING_DOWN:
- new_status &= ~FC_LINK_UP;
+ new_link_up = 0;
break;
case NETDEV_UP:
case NETDEV_CHANGE:
- new_status &= ~FC_LINK_UP;
- if (!fcoe_link_ok(lp))
- new_status |= FC_LINK_UP;
+ new_link_up = !fcoe_link_ok(lp);
break;
case NETDEV_CHANGEMTU:
mfs = fc->real_dev->mtu -
@@ -909,17 +904,15 @@ static int fcoe_device_notification(stru
sizeof(struct fcoe_crc_eof));
if (mfs >= FC_MIN_MAX_FRAME)
fc_set_mfs(lp, mfs);
- new_status &= ~FC_LINK_UP;
- if (!fcoe_link_ok(lp))
- new_status |= FC_LINK_UP;
+ new_link_up = !fcoe_link_ok(lp);
break;
case NETDEV_REGISTER:
break;
default:
FC_DBG("unknown event %ld call", event);
}
- if (lp->link_status != new_status) {
- if ((new_status & FC_LINK_UP) == FC_LINK_UP)
+ if (lp->link_up != new_link_up) {
+ if (new_link_up)
fc_linkup(lp);
else {
stats = lp->dev_stats[smp_processor_id()];
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -1096,7 +1096,7 @@ static void fc_exch_recv_abts(struct fc_
ap->ba_high_seq_cnt = fh->fh_seq_cnt;
ap->ba_low_seq_cnt = htons(sp->cnt);
}
- sp = fc_seq_start_next(sp);
+ sp = fc_seq_start_next_locked(sp);
spin_unlock_bh(&ep->ex_lock);
fc_seq_send_last(sp, fp, FC_RCTL_BA_ACC, FC_TYPE_BLS);
fc_frame_free(rx_fp);
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -20,13 +20,13 @@
*/
#include <linux/module.h>
+#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/scatterlist.h>
#include <linux/err.h>
#include <linux/crc32.h>
-#include <linux/delay.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi.h>
@@ -1622,7 +1622,7 @@ out:
static inline int fc_fcp_lport_queue_ready(struct fc_lport *lp)
{
/* lock ? */
- return (lp->state == LPORT_ST_READY) && (lp->link_status & FC_LINK_UP);
+ return (lp->state == LPORT_ST_READY) && lp->link_up && !lp->qfull;
}
/**
@@ -1891,7 +1891,7 @@ int fc_eh_abort(struct scsi_cmnd *sc_cmd
lp = shost_priv(sc_cmd->device->host);
if (lp->state != LPORT_ST_READY)
return rc;
- else if (!(lp->link_status & FC_LINK_UP))
+ else if (!lp->link_up)
return rc;
spin_lock_irqsave(lp->host->host_lock, flags);
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -250,7 +250,7 @@ void fc_get_host_port_state(struct Scsi_
{
struct fc_lport *lp = shost_priv(shost);
- if ((lp->link_status & FC_LINK_UP) == FC_LINK_UP)
+ if (lp->link_up)
fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
else
fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
@@ -484,7 +484,7 @@ static void fc_lport_recv_rnid_req(struc
* @sp: current sequence in the ADISC exchange
* @fp: ADISC request frame
*
- * Locking Note: The lport lock is exected to be held before calling
+ * Locking Note: The lport lock is expected to be held before calling
* this function.
*/
static void fc_lport_recv_adisc_req(struct fc_seq *sp, struct fc_frame *in_fp,
@@ -577,8 +577,8 @@ void fc_linkup(struct fc_lport *lport)
fc_host_port_id(lport->host));
mutex_lock(&lport->lp_mutex);
- if ((lport->link_status & FC_LINK_UP) != FC_LINK_UP) {
- lport->link_status |= FC_LINK_UP;
+ if (!lport->link_up) {
+ lport->link_up = 1;
if (lport->state == LPORT_ST_RESET)
fc_lport_enter_flogi(lport);
@@ -597,8 +597,8 @@ void fc_linkdown(struct fc_lport *lport)
FC_DEBUG_LPORT("Link is down for port (%6x)\n",
fc_host_port_id(lport->host));
- if ((lport->link_status & FC_LINK_UP) == FC_LINK_UP) {
- lport->link_status &= ~(FC_LINK_UP);
+ if (lport->link_up) {
+ lport->link_up = 0;
fc_lport_enter_reset(lport);
lport->tt.fcp_cleanup(lport);
}
@@ -607,30 +607,6 @@ void fc_linkdown(struct fc_lport *lport)
EXPORT_SYMBOL(fc_linkdown);
/**
- * fc_pause - Pause the flow of frames
- * @lport: The lport to be paused
- */
-void fc_pause(struct fc_lport *lport)
-{
- mutex_lock(&lport->lp_mutex);
- lport->link_status |= FC_PAUSE;
- mutex_unlock(&lport->lp_mutex);
-}
-EXPORT_SYMBOL(fc_pause);
-
-/**
- * fc_unpause - Unpause the flow of frames
- * @lport: The lport to be unpaused
- */
-void fc_unpause(struct fc_lport *lport)
-{
- mutex_lock(&lport->lp_mutex);
- lport->link_status &= ~(FC_PAUSE);
- mutex_unlock(&lport->lp_mutex);
-}
-EXPORT_SYMBOL(fc_unpause);
-
-/**
* fc_fabric_logoff - Logout of the fabric
* @lport: fc_lport pointer to logoff the fabric
*
@@ -977,7 +953,7 @@ static void fc_lport_enter_reset(struct
fc_host_fabric_name(lport->host) = 0;
fc_host_port_id(lport->host) = 0;
- if ((lport->link_status & FC_LINK_UP) == FC_LINK_UP)
+ if (lport->link_up)
fc_lport_enter_flogi(lport);
}
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -425,7 +425,7 @@ static void fc_rport_error(struct fc_rpo
PTR_ERR(fp), fc_rport_state(rport), rdata->retries);
if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) {
- /*
+ /*
* Memory allocation failure, or the exchange timed out.
* Retry after delay
*/
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -68,9 +68,6 @@
/*
* FC HBA status
*/
-#define FC_PAUSE (1 << 1)
-#define FC_LINK_UP (1 << 0)
-
enum fc_lport_state {
LPORT_ST_NONE = 0,
LPORT_ST_FLOGI,
@@ -603,7 +600,8 @@ struct fc_lport {
/* Operational Information */
struct libfc_function_template tt;
- u16 link_status;
+ u8 link_up;
+ u8 qfull;
enum fc_lport_state state;
unsigned long boot_time;
@@ -704,12 +702,6 @@ void fc_linkup(struct fc_lport *);
void fc_linkdown(struct fc_lport *);
/*
- * Pause and unpause traffic.
- */
-void fc_pause(struct fc_lport *);
-void fc_unpause(struct fc_lport *);
-
-/*
* Configure the local port.
*/
int fc_lport_config(struct fc_lport *);

View File

@@ -0,0 +1,265 @@
From: Chris Leech <christopher.leech@intel.com>
Subject: libfc: rport retry on LS_RJT from certain ELS
Patch-mainline: 6147a1194ba86af4266f36c9522a7b0040af98fe
References: bnc #468054
This allows any rport ELS to retry on LS_RJT.
The rport error handling would only retry on resource allocation failures
and exchange timeouts. I have a target that will occasionally reject PLOGI
when we do a quick LOGO/PLOGI. When a critical ELS was rejected, libfc would
fail silently leaving the rport in a dead state.
The retry count and delay are managed by fc_rport_error_retry. If the retry
count is exceeded fc_rport_error will be called. When retrying is not the
correct course of action, fc_rport_error can be called directly.
Signed-off-by: Chris Leech <christopher.leech@intel.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
drivers/scsi/libfc/fc_exch.c | 2
drivers/scsi/libfc/fc_rport.c | 111 ++++++++++++++++++++++++------------------
include/scsi/fc/fc_fs.h | 5 +
3 files changed, 69 insertions(+), 49 deletions(-)
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -32,8 +32,6 @@
#include <scsi/libfc.h>
#include <scsi/fc_encode.h>
-#define FC_DEF_R_A_TOV (10 * 1000) /* resource allocation timeout */
-
/*
* fc_exch_debug can be set in debugger or at compile time to get more logs.
*/
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -81,6 +81,7 @@ static void fc_rport_recv_logo_req(struc
struct fc_seq *, struct fc_frame *);
static void fc_rport_timeout(struct work_struct *);
static void fc_rport_error(struct fc_rport *, struct fc_frame *);
+static void fc_rport_error_retry(struct fc_rport *, struct fc_frame *);
static void fc_rport_work(struct work_struct *);
static const char *fc_rport_state_names[] = {
@@ -405,55 +406,71 @@ static void fc_rport_timeout(struct work
}
/**
- * fc_rport_error - Handler for any errors
+ * fc_rport_error - Error handler, called once retries have been exhausted
* @rport: The fc_rport object
* @fp: The frame pointer
*
- * If the error was caused by a resource allocation failure
- * then wait for half a second and retry, otherwise retry
- * immediately.
- *
* Locking Note: The rport lock is expected to be held before
* calling this routine
*/
static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp)
{
struct fc_rport_libfc_priv *rdata = rport->dd_data;
- unsigned long delay = 0;
FC_DEBUG_RPORT("Error %ld in state %s, retries %d\n",
PTR_ERR(fp), fc_rport_state(rport), rdata->retries);
- if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) {
- /*
- * Memory allocation failure, or the exchange timed out.
- * Retry after delay
- */
- if (rdata->retries < rdata->local_port->max_retry_count) {
- rdata->retries++;
- if (!fp)
- delay = msecs_to_jiffies(500);
- get_device(&rport->dev);
- schedule_delayed_work(&rdata->retry_work, delay);
- } else {
- switch (rdata->rp_state) {
- case RPORT_ST_PLOGI:
- case RPORT_ST_PRLI:
- case RPORT_ST_LOGO:
- rdata->event = RPORT_EV_FAILED;
- queue_work(rport_event_queue,
- &rdata->event_work);
- break;
- case RPORT_ST_RTV:
- fc_rport_enter_ready(rport);
- break;
- case RPORT_ST_NONE:
- case RPORT_ST_READY:
- case RPORT_ST_INIT:
- break;
- }
- }
+ switch (rdata->rp_state) {
+ case RPORT_ST_PLOGI:
+ case RPORT_ST_PRLI:
+ case RPORT_ST_LOGO:
+ rdata->event = RPORT_EV_FAILED;
+ queue_work(rport_event_queue,
+ &rdata->event_work);
+ break;
+ case RPORT_ST_RTV:
+ fc_rport_enter_ready(rport);
+ break;
+ case RPORT_ST_NONE:
+ case RPORT_ST_READY:
+ case RPORT_ST_INIT:
+ break;
+ }
+}
+
+/**
+ * fc_rport_error_retry - Error handler when retries are desired
+ * @rport: The fc_rport object
+ * @fp: The frame pointer
+ *
+ * If the error was an exchange timeout retry immediately,
+ * otherwise wait for E_D_TOV.
+ *
+ * Locking Note: The rport lock is expected to be held before
+ * calling this routine
+ */
+static void fc_rport_error_retry(struct fc_rport *rport, struct fc_frame *fp)
+{
+ struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ unsigned long delay = FC_DEF_E_D_TOV;
+
+ /* make sure this isn't an FC_EX_CLOSED error, never retry those */
+ if (PTR_ERR(fp) == -FC_EX_CLOSED)
+ return fc_rport_error(rport, fp);
+
+ if (rdata->retries < rdata->local_port->max_retry_count) {
+ FC_DEBUG_RPORT("Error %ld in state %s, retrying\n",
+ PTR_ERR(fp), fc_rport_state(rport));
+ rdata->retries++;
+ /* no additional delay on exchange timeouts */
+ if (PTR_ERR(fp) == -FC_EX_TIMEOUT)
+ delay = 0;
+ get_device(&rport->dev);
+ schedule_delayed_work(&rdata->retry_work, delay);
+ return;
}
+
+ return fc_rport_error(rport, fp);
}
/**
@@ -490,7 +507,7 @@ static void fc_rport_plogi_resp(struct f
}
if (IS_ERR(fp)) {
- fc_rport_error(rport, fp);
+ fc_rport_error_retry(rport, fp);
goto err;
}
@@ -522,7 +539,7 @@ static void fc_rport_plogi_resp(struct f
else
fc_rport_enter_prli(rport);
} else
- fc_rport_error(rport, fp);
+ fc_rport_error_retry(rport, fp);
out:
fc_frame_free(fp);
@@ -552,14 +569,14 @@ static void fc_rport_enter_plogi(struct
rport->maxframe_size = FC_MIN_MAX_PAYLOAD;
fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi));
if (!fp) {
- fc_rport_error(rport, fp);
+ fc_rport_error_retry(rport, fp);
return;
}
rdata->e_d_tov = lport->e_d_tov;
if (!lport->tt.elsct_send(lport, rport, fp, ELS_PLOGI,
fc_rport_plogi_resp, rport, lport->e_d_tov))
- fc_rport_error(rport, fp);
+ fc_rport_error_retry(rport, fp);
else
get_device(&rport->dev);
}
@@ -599,7 +616,7 @@ static void fc_rport_prli_resp(struct fc
}
if (IS_ERR(fp)) {
- fc_rport_error(rport, fp);
+ fc_rport_error_retry(rport, fp);
goto err;
}
@@ -657,7 +674,7 @@ static void fc_rport_logo_resp(struct fc
rport->port_id);
if (IS_ERR(fp)) {
- fc_rport_error(rport, fp);
+ fc_rport_error_retry(rport, fp);
goto err;
}
@@ -707,13 +724,13 @@ static void fc_rport_enter_prli(struct f
fp = fc_frame_alloc(lport, sizeof(*pp));
if (!fp) {
- fc_rport_error(rport, fp);
+ fc_rport_error_retry(rport, fp);
return;
}
if (!lport->tt.elsct_send(lport, rport, fp, ELS_PRLI,
fc_rport_prli_resp, rport, lport->e_d_tov))
- fc_rport_error(rport, fp);
+ fc_rport_error_retry(rport, fp);
else
get_device(&rport->dev);
}
@@ -804,13 +821,13 @@ static void fc_rport_enter_rtv(struct fc
fp = fc_frame_alloc(lport, sizeof(struct fc_els_rtv));
if (!fp) {
- fc_rport_error(rport, fp);
+ fc_rport_error_retry(rport, fp);
return;
}
if (!lport->tt.elsct_send(lport, rport, fp, ELS_RTV,
fc_rport_rtv_resp, rport, lport->e_d_tov))
- fc_rport_error(rport, fp);
+ fc_rport_error_retry(rport, fp);
else
get_device(&rport->dev);
}
@@ -835,13 +852,13 @@ static void fc_rport_enter_logo(struct f
fp = fc_frame_alloc(lport, sizeof(struct fc_els_logo));
if (!fp) {
- fc_rport_error(rport, fp);
+ fc_rport_error_retry(rport, fp);
return;
}
if (!lport->tt.elsct_send(lport, rport, fp, ELS_LOGO,
fc_rport_logo_resp, rport, lport->e_d_tov))
- fc_rport_error(rport, fp);
+ fc_rport_error_retry(rport, fp);
else
get_device(&rport->dev);
}
--- a/include/scsi/fc/fc_fs.h
+++ b/include/scsi/fc/fc_fs.h
@@ -337,4 +337,9 @@ enum fc_pf_rjt_reason {
FC_RJT_VENDOR = 0xff, /* vendor specific reject */
};
+/* default timeout values */
+
+#define FC_DEF_E_D_TOV 2000UL
+#define FC_DEF_R_A_TOV 10000UL
+
#endif /* _FC_FS_H_ */

View File

@@ -0,0 +1,54 @@
From: Danny Kukawka <dkukawka@suse.de>
Subject: b43legacy: fix led naming
Fixed led device naming for the b43legacy driver. Due to the
documentation of the led subsystem/class the naming should be
"devicename:colour:function" while not applying sections
should be left blank.
This should lead to e.g. "b43legacy-%s::rx" instead of
"b43legacy-%s:rx".
Signed-off-by: Danny Kukawka <dkukawka@suse.de>
--
leds.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/b43legacy/leds.c b/drivers/net/wireless/b43legacy/leds.c
index cacb786..cb4511f 100644
--- a/drivers/net/wireless/b43legacy/leds.c
+++ b/drivers/net/wireless/b43legacy/leds.c
@@ -146,12 +146,12 @@ static void b43legacy_map_led(struct b43legacy_wldev *dev,
case B43legacy_LED_TRANSFER:
case B43legacy_LED_APTRANSFER:
snprintf(name, sizeof(name),
- "b43legacy-%s:tx", wiphy_name(hw->wiphy));
+ "b43legacy-%s::tx", wiphy_name(hw->wiphy));
b43legacy_register_led(dev, &dev->led_tx, name,
ieee80211_get_tx_led_name(hw),
led_index, activelow);
snprintf(name, sizeof(name),
- "b43legacy-%s:rx", wiphy_name(hw->wiphy));
+ "b43legacy-%s::rx", wiphy_name(hw->wiphy));
b43legacy_register_led(dev, &dev->led_rx, name,
ieee80211_get_rx_led_name(hw),
led_index, activelow);
@@ -161,7 +161,7 @@ static void b43legacy_map_led(struct b43legacy_wldev *dev,
case B43legacy_LED_RADIO_B:
case B43legacy_LED_MODE_BG:
snprintf(name, sizeof(name),
- "b43legacy-%s:radio", wiphy_name(hw->wiphy));
+ "b43legacy-%s::radio", wiphy_name(hw->wiphy));
b43legacy_register_led(dev, &dev->led_radio, name,
b43legacy_rfkill_led_name(dev),
led_index, activelow);
@@ -172,7 +172,7 @@ static void b43legacy_map_led(struct b43legacy_wldev *dev,
case B43legacy_LED_WEIRD:
case B43legacy_LED_ASSOC:
snprintf(name, sizeof(name),
- "b43legacy-%s:assoc", wiphy_name(hw->wiphy));
+ "b43legacy-%s::assoc", wiphy_name(hw->wiphy));
b43legacy_register_led(dev, &dev->led_assoc, name,
ieee80211_get_assoc_led_name(hw),
led_index, activelow);

View File

@@ -0,0 +1,31 @@
From: Jan Kara <jack@suse.cz>
Subject: [PATCH] ext2: Do not update mtime of a move directory when parent has not changed
References: bnc#493392
Patch-mainline: 2.6.30
If the parent of the moved directory has not changed, there's no real
reason to change mtime. Specs doesn't seem to say anything about this
particular case and e.g. ext3 does not change mtime in this case.
So we become a tiny bit more consistent.
Spotted by ronny.pretzsch@dfs.de, initial fix by Jörn Engel <joern@logfs.org>.
Signed-off-by: Jan Kara <jack@suse.cz>
---
fs/ext2/namei.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
--- a/fs/ext2/namei.c
+++ b/fs/ext2/namei.c
@@ -355,7 +355,10 @@ static int ext2_rename (struct inode * o
inode_dec_link_count(old_inode);
if (dir_de) {
- ext2_set_link(old_inode, dir_de, dir_page, new_dir);
+ /* Set link only if parent has changed and thus avoid setting
+ * of mtime of the moved directory on a pure rename. */
+ if (old_dir != new_dir)
+ ext2_set_link(old_inode, dir_de, dir_page, new_dir);
inode_dec_link_count(old_dir);
}
return 0;

View File

@@ -0,0 +1,172 @@
From: Jan Kara <jack@suse.cz>
Subject: [PATCH] ext3: Avoid false EIO errors
References: bnc#479730
Sometimes block_write_begin() can map buffers in a page but later we fail to
copy data into those buffers (because the source page has been paged out in the
mean time). We then end up with !uptodate mapped buffers. To add a bit more to
the confusion, block_write_end() does not commit any data (and thus does not
any mark buffers as uptodate) if we didn't succeed with copying all the data.
Commit f4fc66a894546bdc88a775d0e83ad20a65210bcb (ext3: convert to new aops)
missed these cases and thus we were inserting non-uptodate buffers to
transaction's list which confuses JBD code and it reports IO errors, aborts
a transaction and generally makes users afraid about their data ;-P.
This patch fixes the problem by reorganizing ext3_..._write_end() code to
first call block_write_end() to mark buffers with valid data uptodate and
after that we file only uptodate buffers to transaction's lists. Also
fix a problem where we could leave blocks allocated beyond i_size (i_disksize
in fact).
Signed-off-by: Jan Kara <jack@suse.cz>
---
fs/ext3/inode.c | 99 +++++++++++++++++++++++---------------------------------
1 file changed, 42 insertions(+), 57 deletions(-)
--- a/fs/ext3/inode.c
+++ b/fs/ext3/inode.c
@@ -1195,6 +1195,18 @@ int ext3_journal_dirty_data(handle_t *ha
return err;
}
+/* For ordered writepage and write_end functions */
+static int journal_dirty_data_fn(handle_t *handle, struct buffer_head *bh)
+{
+ /*
+ * Write could have mapped the buffer but it didn't copy the data in
+ * yet. So avoid filing such buffer into a transaction.
+ */
+ if (buffer_mapped(bh) && buffer_uptodate(bh))
+ return ext3_journal_dirty_data(handle, bh);
+ return 0;
+}
+
/* For write_end() in data=journal mode */
static int write_end_fn(handle_t *handle, struct buffer_head *bh)
{
@@ -1205,26 +1217,29 @@ static int write_end_fn(handle_t *handle
}
/*
- * Generic write_end handler for ordered and writeback ext3 journal modes.
- * We can't use generic_write_end, because that unlocks the page and we need to
- * unlock the page after ext3_journal_stop, but ext3_journal_stop must run
- * after block_write_end.
+ * This is nasty and subtle: ext3_write_begin() could have allocated blocks
+ * for the whole page but later we failed to copy the data in. So the disk
+ * size we really have allocated is pos + len (block_write_end() has zeroed
+ * the freshly allocated buffers so we aren't going to write garbage). But we
+ * want to keep i_size at the place where data copying finished so that we
+ * don't confuse readers. The worst what can happen is that we expose a page
+ * of zeros at the end of file after a crash...
*/
-static int ext3_generic_write_end(struct file *file,
- struct address_space *mapping,
- loff_t pos, unsigned len, unsigned copied,
- struct page *page, void *fsdata)
+static void update_file_sizes(struct inode *inode, loff_t pos, unsigned len,
+ unsigned copied)
{
- struct inode *inode = file->f_mapping->host;
+ int mark_dirty = 0;
- copied = block_write_end(file, mapping, pos, len, copied, page, fsdata);
-
- if (pos+copied > inode->i_size) {
- i_size_write(inode, pos+copied);
- mark_inode_dirty(inode);
+ if (pos + len > EXT3_I(inode)->i_disksize) {
+ mark_dirty = 1;
+ EXT3_I(inode)->i_disksize = pos + len;
}
-
- return copied;
+ if (pos + copied > inode->i_size) {
+ i_size_write(inode, pos + copied);
+ mark_dirty = 1;
+ }
+ if (mark_dirty)
+ mark_inode_dirty(inode);
}
/*
@@ -1244,29 +1259,17 @@ static int ext3_ordered_write_end(struct
unsigned from, to;
int ret = 0, ret2;
+ copied = block_write_end(file, mapping, pos, len, copied, page, fsdata);
+
+ /* See comment at update_file_sizes() for why we check buffers upto
+ * from + len */
from = pos & (PAGE_CACHE_SIZE - 1);
to = from + len;
-
ret = walk_page_buffers(handle, page_buffers(page),
- from, to, NULL, ext3_journal_dirty_data);
+ from, to, NULL, journal_dirty_data_fn);
- if (ret == 0) {
- /*
- * generic_write_end() will run mark_inode_dirty() if i_size
- * changes. So let's piggyback the i_disksize mark_inode_dirty
- * into that.
- */
- loff_t new_i_size;
-
- new_i_size = pos + copied;
- if (new_i_size > EXT3_I(inode)->i_disksize)
- EXT3_I(inode)->i_disksize = new_i_size;
- ret2 = ext3_generic_write_end(file, mapping, pos, len, copied,
- page, fsdata);
- copied = ret2;
- if (ret2 < 0)
- ret = ret2;
- }
+ if (ret == 0)
+ update_file_sizes(inode, pos, len, copied);
ret2 = ext3_journal_stop(handle);
if (!ret)
ret = ret2;
@@ -1283,22 +1286,11 @@ static int ext3_writeback_write_end(stru
{
handle_t *handle = ext3_journal_current_handle();
struct inode *inode = file->f_mapping->host;
- int ret = 0, ret2;
- loff_t new_i_size;
+ int ret;
- new_i_size = pos + copied;
- if (new_i_size > EXT3_I(inode)->i_disksize)
- EXT3_I(inode)->i_disksize = new_i_size;
-
- ret2 = ext3_generic_write_end(file, mapping, pos, len, copied,
- page, fsdata);
- copied = ret2;
- if (ret2 < 0)
- ret = ret2;
-
- ret2 = ext3_journal_stop(handle);
- if (!ret)
- ret = ret2;
+ copied = block_write_end(file, mapping, pos, len, copied, page, fsdata);
+ update_file_sizes(inode, pos, len, copied);
+ ret = ext3_journal_stop(handle);
unlock_page(page);
page_cache_release(page);
@@ -1412,13 +1404,6 @@ static int bput_one(handle_t *handle, st
return 0;
}
-static int journal_dirty_data_fn(handle_t *handle, struct buffer_head *bh)
-{
- if (buffer_mapped(bh))
- return ext3_journal_dirty_data(handle, bh);
- return 0;
-}
-
/*
* Note that we always start a transaction even if we're not journalling
* data. This is to preserve ordering: any hole instantiation within

View File

@@ -0,0 +1,54 @@
From: Jan Blunck <jblunck@suse.de>
Subject: ia64-kvm: fix sparse warnings
This patch fixes some sparse warning about dubious one-bit signed bitfield.
Signed-off-by: Jan Blunck <jblunck@suse.de>
---
arch/ia64/kvm/vti.h | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
Index: b/arch/ia64/kvm/vti.h
===================================================================
--- a/arch/ia64/kvm/vti.h
+++ b/arch/ia64/kvm/vti.h
@@ -83,13 +83,13 @@
union vac {
unsigned long value;
struct {
- int a_int:1;
- int a_from_int_cr:1;
- int a_to_int_cr:1;
- int a_from_psr:1;
- int a_from_cpuid:1;
- int a_cover:1;
- int a_bsw:1;
+ unsigned int a_int:1;
+ unsigned int a_from_int_cr:1;
+ unsigned int a_to_int_cr:1;
+ unsigned int a_from_psr:1;
+ unsigned int a_from_cpuid:1;
+ unsigned int a_cover:1;
+ unsigned int a_bsw:1;
long reserved:57;
};
};
@@ -97,12 +97,12 @@ union vac {
union vdc {
unsigned long value;
struct {
- int d_vmsw:1;
- int d_extint:1;
- int d_ibr_dbr:1;
- int d_pmc:1;
- int d_to_pmd:1;
- int d_itm:1;
+ unsigned int d_vmsw:1;
+ unsigned int d_extint:1;
+ unsigned int d_ibr_dbr:1;
+ unsigned int d_pmc:1;
+ unsigned int d_to_pmd:1;
+ unsigned int d_itm:1;
long reserved:58;
};
};

View File

@@ -0,0 +1,35 @@
From: Russ Anderson <rja@sgi.com>
Subject: Add partition id, coherence id, and region size to UV
References: bnc#442455
Add partition id, coherence id, and region size to UV.
The SGI xp drivers (drivers/misc/sgi-xp) are used on both
sn (Itanium) and uv (Tukwilla). Using the same names
(sn_partition_id, sn_coherency_id, sn_region_size)
simplifies the driver code.
Signed-off-by: Russ Anderson <rja@sgi.com>
Acked-by: Bernhard Walle <bwalle@suse.de>
---
arch/ia64/uv/kernel/setup.c | 6 ++++++
1 file changed, 6 insertions(+)
--- a/arch/ia64/uv/kernel/setup.c
+++ b/arch/ia64/uv/kernel/setup.c
@@ -19,6 +19,12 @@ EXPORT_PER_CPU_SYMBOL_GPL(__uv_hub_info)
#ifdef CONFIG_IA64_SGI_UV
int sn_prom_type;
+long sn_partition_id;
+EXPORT_SYMBOL(sn_partition_id);
+long sn_coherency_id;
+EXPORT_SYMBOL_GPL(sn_coherency_id);
+long sn_region_size;
+EXPORT_SYMBOL(sn_region_size);
#endif
struct redir_addr {

View File

@@ -0,0 +1,78 @@
From: Bernhard Walle <bwalle@suse.de>
Subject: Add UV watchlist support
References: bnc#442455
Add UV watchlist support.
This is used by SGI xp drivers (drivers/misc/sgi-xp).
Signed-off-by: Russ Anderson <rja@sgi.com>
---
arch/ia64/include/asm/sn/sn_sal.h | 45 ++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)
Index: linux/arch/ia64/include/asm/sn/sn_sal.h
===================================================================
--- linux.orig/arch/ia64/include/asm/sn/sn_sal.h 2008-11-05 09:21:48.690243174 -0600
+++ linux/arch/ia64/include/asm/sn/sn_sal.h 2008-11-05 09:22:01.847928152 -0600
@@ -90,6 +90,8 @@
#define SN_SAL_SET_CPU_NUMBER 0x02000068
#define SN_SAL_KERNEL_LAUNCH_EVENT 0x02000069
+#define SN_SAL_WATCHLIST_ALLOC 0x02000070
+#define SN_SAL_WATCHLIST_FREE 0x02000071
/*
* Service-specific constants
@@ -1183,6 +1185,49 @@ ia64_sn_kernel_launch_event(void)
{
struct ia64_sal_retval rv;
SAL_CALL_NOLOCK(rv, SN_SAL_KERNEL_LAUNCH_EVENT, 0, 0, 0, 0, 0, 0, 0);
+ return rv.status;
+}
+
+union sn_watchlist_u {
+ u64 val;
+ struct {
+ u64 blade : 16,
+ size : 32,
+ filler : 16;
+ };
+};
+
+static inline int
+sn_mq_watchlist_alloc(int blade, void *mq, unsigned int mq_size,
+ unsigned long *intr_mmr_offset)
+{
+ struct ia64_sal_retval rv;
+ unsigned long addr;
+ union sn_watchlist_u size_blade;
+ int watchlist;
+
+ addr = (unsigned long)mq;
+ size_blade.size = mq_size;
+ size_blade.blade = blade;
+
+ /*
+ * bios returns watchlist number or negative error number.
+ */
+ ia64_sal_oemcall_nolock(&rv, SN_SAL_WATCHLIST_ALLOC, addr,
+ size_blade.val, (u64)intr_mmr_offset,
+ (u64)&watchlist, 0, 0, 0);
+ if (rv.status < 0)
+ return rv.status;
+
+ return watchlist;
+}
+
+static inline int
+sn_mq_watchlist_free(int blade, int watchlist_num)
+{
+ struct ia64_sal_retval rv;
+ ia64_sal_oemcall_nolock(&rv, SN_SAL_WATCHLIST_FREE, blade,
+ watchlist_num, 0, 0, 0, 0, 0);
return rv.status;
}
#endif /* _ASM_IA64_SN_SN_SAL_H */

View File

@@ -0,0 +1,57 @@
From: Danny Kukawka <dkukawka@suse.de>
Subject: iwlwifi: another led naming fix
Fixed led device naming for the iwlwifi (iwl-3945) driver. Due
to the documentation of the led subsystem/class the naming should
be "devicename:colour:function" while not applying sections
should be left blank.
This should lead to e.g. "iwl-%s::RX" instead of "iwl-%s:RX".
Signed-off-by: Danny Kukawka <dkukawka@suse.de>
Acked-by: Reinette Chatre <reinette.chatre@intel.com>
--
iwl-led.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-led.c b/drivers/net/wireless/iwlwifi/iwl-3945-led.c
index 4c63890..09f9350 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945-led.c
+++ b/drivers/net/wireless/iwlwifi/iwl-3945-led.c
@@ -317,7 +317,7 @@ int iwl3945_led_register(struct iwl3945_priv *priv)
trigger = ieee80211_get_radio_led_name(priv->hw);
snprintf(priv->led[IWL_LED_TRG_RADIO].name,
- sizeof(priv->led[IWL_LED_TRG_RADIO].name), "iwl-%s:radio",
+ sizeof(priv->led[IWL_LED_TRG_RADIO].name), "iwl-%s::radio",
wiphy_name(priv->hw->wiphy));
priv->led[IWL_LED_TRG_RADIO].led_on = iwl3945_led_on;
@@ -333,7 +333,7 @@ int iwl3945_led_register(struct iwl3945_priv *priv)
trigger = ieee80211_get_assoc_led_name(priv->hw);
snprintf(priv->led[IWL_LED_TRG_ASSOC].name,
- sizeof(priv->led[IWL_LED_TRG_ASSOC].name), "iwl-%s:assoc",
+ sizeof(priv->led[IWL_LED_TRG_ASSOC].name), "iwl-%s::assoc",
wiphy_name(priv->hw->wiphy));
ret = iwl3945_led_register_led(priv,
@@ -350,7 +350,7 @@ int iwl3945_led_register(struct iwl3945_priv *priv)
trigger = ieee80211_get_rx_led_name(priv->hw);
snprintf(priv->led[IWL_LED_TRG_RX].name,
- sizeof(priv->led[IWL_LED_TRG_RX].name), "iwl-%s:RX",
+ sizeof(priv->led[IWL_LED_TRG_RX].name), "iwl-%s::RX",
wiphy_name(priv->hw->wiphy));
ret = iwl3945_led_register_led(priv,
@@ -366,7 +366,7 @@ int iwl3945_led_register(struct iwl3945_priv *priv)
trigger = ieee80211_get_tx_led_name(priv->hw);
snprintf(priv->led[IWL_LED_TRG_TX].name,
- sizeof(priv->led[IWL_LED_TRG_TX].name), "iwl-%s:TX",
+ sizeof(priv->led[IWL_LED_TRG_TX].name), "iwl-%s::TX",
wiphy_name(priv->hw->wiphy));
ret = iwl3945_led_register_led(priv,

View File

@@ -0,0 +1,54 @@
From: Danny Kukawka <dkukawka@suse.de>
Subject: iwlwifi: fix led naming
Fixed led device naming for the iwl driver. Due to the
documentation of the led subsystem/class the naming should be
"devicename:colour:function" while not applying sections
should be left blank.
This should lead to e.g. "iwl-phy0::RX" instead of "iwl-phy0:RX".
Signed-off-by: Danny Kukawka <dkukawka@suse.de>
Acked-by: Reinette Chatre <reinette.chatre@intel.com>
--
drivers/net/wireless/iwlwifi/iwl-led.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
--- a/drivers/net/wireless/iwlwifi/iwl-led.c
+++ b/drivers/net/wireless/iwlwifi/iwl-led.c
@@ -353,7 +353,7 @@ int iwl_leds_register(struct iwl_priv *p
trigger = ieee80211_get_radio_led_name(priv->hw);
snprintf(priv->led[IWL_LED_TRG_RADIO].name,
- sizeof(priv->led[IWL_LED_TRG_RADIO].name), "iwl-%s:radio",
+ sizeof(priv->led[IWL_LED_TRG_RADIO].name), "iwl-%s::radio",
wiphy_name(priv->hw->wiphy));
priv->led[IWL_LED_TRG_RADIO].led_on = iwl4965_led_on_reg;
@@ -367,7 +367,7 @@ int iwl_leds_register(struct iwl_priv *p
trigger = ieee80211_get_assoc_led_name(priv->hw);
snprintf(priv->led[IWL_LED_TRG_ASSOC].name,
- sizeof(priv->led[IWL_LED_TRG_ASSOC].name), "iwl-%s:assoc",
+ sizeof(priv->led[IWL_LED_TRG_ASSOC].name), "iwl-%s::assoc",
wiphy_name(priv->hw->wiphy));
ret = iwl_leds_register_led(priv, &priv->led[IWL_LED_TRG_ASSOC],
@@ -383,7 +383,7 @@ int iwl_leds_register(struct iwl_priv *p
trigger = ieee80211_get_rx_led_name(priv->hw);
snprintf(priv->led[IWL_LED_TRG_RX].name,
- sizeof(priv->led[IWL_LED_TRG_RX].name), "iwl-%s:RX",
+ sizeof(priv->led[IWL_LED_TRG_RX].name), "iwl-%s::RX",
wiphy_name(priv->hw->wiphy));
ret = iwl_leds_register_led(priv, &priv->led[IWL_LED_TRG_RX],
@@ -398,7 +398,7 @@ int iwl_leds_register(struct iwl_priv *p
trigger = ieee80211_get_tx_led_name(priv->hw);
snprintf(priv->led[IWL_LED_TRG_TX].name,
- sizeof(priv->led[IWL_LED_TRG_TX].name), "iwl-%s:TX",
+ sizeof(priv->led[IWL_LED_TRG_TX].name), "iwl-%s::TX",
wiphy_name(priv->hw->wiphy));
ret = iwl_leds_register_led(priv, &priv->led[IWL_LED_TRG_TX],

View File

@@ -0,0 +1,60 @@
From: Jay Lan <jlan@sgi.com>
Subject: Fix CONFIG_KDB_KDUMP on xSeries
Patch-mainline: not yet
References: bnc#436454
This patch fixes a problem that the capture kernel crashes with various
backtraces after the machine has been crashed (both sysrq-trigger and panic()).
Machines were that problem could reproduced at SUSE were molitor.suse.de and
korner.suse.de.
KDB was turned off in that scenarios.
That patch succeeds in following scenarios:
a) kdb=0
modprobe crasher call_panic
b) kdb=1/0
echo c > /proc/sysrq-trigger
b) kdb=1
ESC KDB
kdb> kdump
But it fails in:
kdb=1
modprobe crasher call_panic
That has to be investigated. But I think that's unrelated to that patch,
and it's no regression.
Signed-off-by: Jay Lan <jlan@sgi.com>
Signed-off-by: Bernhard Walle <bwalle@suse.de>
---
arch/x86/kdb/kdba_support.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
--- a/arch/x86/kdb/kdba_support.c
+++ b/arch/x86/kdb/kdba_support.c
@@ -35,8 +35,6 @@ void kdba_kdump_prepare(struct pt_regs *
if (regs == NULL)
regs = &r;
- machine_crash_shutdown_begin();
-
for (i = 1; i < NR_CPUS; ++i) {
if (!cpu_online(i))
continue;
@@ -44,7 +42,7 @@ void kdba_kdump_prepare(struct pt_regs *
KDB_STATE_SET_CPU(KEXEC, i);
}
- machine_crash_shutdown_end(regs);
+ machine_crash_shutdown(regs);
}
extern void halt_current_cpu(struct pt_regs *);

Some files were not shown because too many files have changed in this diff Show More