Merge branch 'android-msm-angler-3.10-nyc-mr2' into android-msm-angler-3.10-oc
November 2017.1
Bug: 65558908
Change-Id: I1e61ece3bb2a4b7f88517b0ccfb00c0392c44d48
Signed-off-by: Steve Pfetsch <spfetsch@google.com>
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index 352c03e..d6edcda 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -1167,11 +1167,20 @@
Functional default: enabled if accept_ra is enabled.
disabled if accept_ra is disabled.
+accept_ra_rt_info_min_plen - INTEGER
+ Minimum prefix length of Route Information in RA.
+
+ Route Information w/ prefix smaller than this variable shall
+ be ignored.
+
+ Functional default: 0 if accept_ra_rtr_pref is enabled.
+ -1 if accept_ra_rtr_pref is disabled.
+
accept_ra_rt_info_max_plen - INTEGER
Maximum prefix length of Route Information in RA.
- Route Information w/ prefix larger than or equal to this
- variable shall be ignored.
+ Route Information w/ prefix larger than this variable shall
+ be ignored.
Functional default: 0 if accept_ra_rtr_pref is enabled.
-1 if accept_ra_rtr_pref is disabled.
diff --git a/Documentation/printk-formats.txt b/Documentation/printk-formats.txt
index 3af5ae6..3f35e2d 100644
--- a/Documentation/printk-formats.txt
+++ b/Documentation/printk-formats.txt
@@ -45,6 +45,11 @@
users. The behaviour of %pK depends on the kptr_restrict sysctl - see
Documentation/sysctl/kernel.txt for more details.
+ %pP 0x01234567 or 0x0123456789abcdef
+
+ For printing kernel pointers which should always be shown, even to
+ unprivileged users.
+
Struct Resources:
%pr [mem 0x60000000-0x6fffffff flags 0x2200] or
diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt
index 6c46487..8e7209b 100644
--- a/Documentation/sysctl/kernel.txt
+++ b/Documentation/sysctl/kernel.txt
@@ -334,6 +334,15 @@
When kptr_restrict is set to (2), kernel pointers printed using
%pK will be replaced with 0's regardless of privileges.
+When kptr_restrict is set to (3), kernel pointers printed using
+%p and %pK will be replaced with 0's regardless of privileges,
+however kernel pointers printed using %pP will continue to be printed.
+
+When kptr_restrict is set to (4), kernel pointers printed with
+%p, %pK, %pa, and %p[rR] will be replaced with 0's regardless of
+privileges. Kernel pointers printed using %pP will continue to be
+printed.
+
==============================================================
kstack_depth_to_print: (X86 only)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 792a521..e8ed5c0 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -687,6 +687,24 @@
endmenu
+menu "Expermimental Security Enhancements"
+
+config KUSER_HELPERS_SELECTIVE_DISABLE
+ bool "Disable KUSER_HELPERS selectively"
+ depends on ARM64
+ help
+ Kuser_Helpers is a set of functions in the vector page at a fixed address that
+ are used to support applications compiled for legacy ARM <7. These functions
+ being at a fixed address creates an ASLR bypass.
+
+ Do not enable this feature unless you know what you are doing, this is
+ experimental, and will break any application that uses the Kuser_Helpers or
+ registers a signal handler and uses the kernel trampolines.
+
+ If unsure, say N.
+
+endmenu
+
source "net/Kconfig"
source "drivers/Kconfig"
diff --git a/arch/arm64/configs/angler_defconfig b/arch/arm64/configs/angler_defconfig
index d2a5c57..53e1093 100644
--- a/arch/arm64/configs/angler_defconfig
+++ b/arch/arm64/configs/angler_defconfig
@@ -213,6 +213,7 @@
CONFIG_CMA=y
CONFIG_CMA_SIZE_MBYTES=32
CONFIG_ARM_CCI=y
+CONFIG_PROC_DEVICETREE=y
CONFIG_ZRAM=y
CONFIG_ZRAM_LZ4_COMPRESS=y
CONFIG_BLK_DEV_LOOP=y
@@ -514,6 +515,7 @@
CONFIG_STAGING=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ANDROID_BINDER_DEVICES="binder,hwbinder"
CONFIG_ASHMEM=y
CONFIG_ANDROID_LOW_MEMORY_KILLER=y
CONFIG_ANDROID_INTF_ALARM_DEV=y
@@ -595,6 +597,8 @@
CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_SECURITY=y
CONFIG_EXT4_FS_ENCRYPTION=y
+CONFIG_QUOTA=y
+# CONFIG_PRINT_QUOTA_WARNING is not set
CONFIG_FUSE_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h
index 5487269..159743c 100644
--- a/arch/arm64/include/asm/cputype.h
+++ b/arch/arm64/include/asm/cputype.h
@@ -83,6 +83,8 @@
return read_cpuid(CTR_EL0);
}
+void cpuinfo_store_cpu(void);
+
#endif /* __ASSEMBLY__ */
#endif
diff --git a/arch/arm64/include/asm/hw_breakpoint.h b/arch/arm64/include/asm/hw_breakpoint.h
index 52b484b..77667c3 100644
--- a/arch/arm64/include/asm/hw_breakpoint.h
+++ b/arch/arm64/include/asm/hw_breakpoint.h
@@ -65,7 +65,11 @@
/* Lengths */
#define ARM_BREAKPOINT_LEN_1 0x1
#define ARM_BREAKPOINT_LEN_2 0x3
+#define ARM_BREAKPOINT_LEN_3 0x7
#define ARM_BREAKPOINT_LEN_4 0xf
+#define ARM_BREAKPOINT_LEN_5 0x1f
+#define ARM_BREAKPOINT_LEN_6 0x3f
+#define ARM_BREAKPOINT_LEN_7 0x7f
#define ARM_BREAKPOINT_LEN_8 0xff
/* Kernel stepping */
@@ -107,7 +111,7 @@
struct pmu;
extern int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,
- int *gen_len, int *gen_type);
+ int *gen_len, int *gen_type, int *offset);
extern int arch_check_bp_in_kernelspace(struct perf_event *bp);
extern int arch_validate_hwbkpt_settings(struct perf_event *bp);
extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c
index de7b854..14a2a51 100644
--- a/arch/arm64/kernel/hw_breakpoint.c
+++ b/arch/arm64/kernel/hw_breakpoint.c
@@ -311,9 +311,21 @@
case ARM_BREAKPOINT_LEN_2:
len_in_bytes = 2;
break;
+ case ARM_BREAKPOINT_LEN_3:
+ len_in_bytes = 3;
+ break;
case ARM_BREAKPOINT_LEN_4:
len_in_bytes = 4;
break;
+ case ARM_BREAKPOINT_LEN_5:
+ len_in_bytes = 5;
+ break;
+ case ARM_BREAKPOINT_LEN_6:
+ len_in_bytes = 6;
+ break;
+ case ARM_BREAKPOINT_LEN_7:
+ len_in_bytes = 7;
+ break;
case ARM_BREAKPOINT_LEN_8:
len_in_bytes = 8;
break;
@@ -343,7 +355,7 @@
* to generic breakpoint descriptions.
*/
int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,
- int *gen_len, int *gen_type)
+ int *gen_len, int *gen_type, int *offset)
{
/* Type */
switch (ctrl.type) {
@@ -363,17 +375,33 @@
return -EINVAL;
}
+ if (!ctrl.len)
+ return -EINVAL;
+ *offset = __ffs(ctrl.len);
+
/* Len */
- switch (ctrl.len) {
+ switch (ctrl.len >> *offset) {
case ARM_BREAKPOINT_LEN_1:
*gen_len = HW_BREAKPOINT_LEN_1;
break;
case ARM_BREAKPOINT_LEN_2:
*gen_len = HW_BREAKPOINT_LEN_2;
break;
+ case ARM_BREAKPOINT_LEN_3:
+ *gen_len = HW_BREAKPOINT_LEN_3;
+ break;
case ARM_BREAKPOINT_LEN_4:
*gen_len = HW_BREAKPOINT_LEN_4;
break;
+ case ARM_BREAKPOINT_LEN_5:
+ *gen_len = HW_BREAKPOINT_LEN_5;
+ break;
+ case ARM_BREAKPOINT_LEN_6:
+ *gen_len = HW_BREAKPOINT_LEN_6;
+ break;
+ case ARM_BREAKPOINT_LEN_7:
+ *gen_len = HW_BREAKPOINT_LEN_7;
+ break;
case ARM_BREAKPOINT_LEN_8:
*gen_len = HW_BREAKPOINT_LEN_8;
break;
@@ -417,9 +445,21 @@
case HW_BREAKPOINT_LEN_2:
info->ctrl.len = ARM_BREAKPOINT_LEN_2;
break;
+ case HW_BREAKPOINT_LEN_3:
+ info->ctrl.len = ARM_BREAKPOINT_LEN_3;
+ break;
case HW_BREAKPOINT_LEN_4:
info->ctrl.len = ARM_BREAKPOINT_LEN_4;
break;
+ case HW_BREAKPOINT_LEN_5:
+ info->ctrl.len = ARM_BREAKPOINT_LEN_5;
+ break;
+ case HW_BREAKPOINT_LEN_6:
+ info->ctrl.len = ARM_BREAKPOINT_LEN_6;
+ break;
+ case HW_BREAKPOINT_LEN_7:
+ info->ctrl.len = ARM_BREAKPOINT_LEN_7;
+ break;
case HW_BREAKPOINT_LEN_8:
info->ctrl.len = ARM_BREAKPOINT_LEN_8;
break;
@@ -511,18 +551,17 @@
default:
return -EINVAL;
}
-
- info->address &= ~alignment_mask;
- info->ctrl.len <<= offset;
} else {
if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE)
alignment_mask = 0x3;
else
alignment_mask = 0x7;
- if (info->address & alignment_mask)
- return -EINVAL;
+ offset = info->address & alignment_mask;
}
+ info->address &= ~alignment_mask;
+ info->ctrl.len <<= offset;
+
/*
* Disallow per-task kernel breakpoints since these would
* complicate the stepping code.
@@ -653,12 +692,47 @@
return 0;
}
+/*
+ * Arm64 hardware does not always report a watchpoint hit address that matches
+ * one of the watchpoints set. It can also report an address "near" the
+ * watchpoint if a single instruction access both watched and unwatched
+ * addresses. There is no straight-forward way, short of disassembling the
+ * offending instruction, to map that address back to the watchpoint. This
+ * function computes the distance of the memory access from the watchpoint as a
+ * heuristic for the likelyhood that a given access triggered the watchpoint.
+ *
+ * See Section D2.10.5 "Determining the memory location that caused a Watchpoint
+ * exception" of ARMv8 Architecture Reference Manual for details.
+ *
+ * The function returns the distance of the address from the bytes watched by
+ * the watchpoint. In case of an exact match, it returns 0.
+ */
+static u64 get_distance_from_watchpoint(unsigned long addr, u64 val,
+ struct arch_hw_breakpoint_ctrl *ctrl)
+{
+ u64 wp_low, wp_high;
+ u32 lens, lene;
+
+ lens = __ffs(ctrl->len);
+ lene = __fls(ctrl->len);
+
+ wp_low = val + lens;
+ wp_high = val + lene;
+ if (addr < wp_low)
+ return wp_low - addr;
+ else if (addr > wp_high)
+ return addr - wp_high;
+ else
+ return 0;
+}
+
static int watchpoint_handler(unsigned long addr, unsigned int esr,
struct pt_regs *regs)
{
- int i, step = 0, *kernel_step, access;
+ int i, step = 0, *kernel_step, access, closest_match = 0;
+ u64 min_dist = -1, dist;
u32 ctrl_reg;
- u64 val, alignment_mask;
+ u64 val;
struct perf_event *wp, **slots;
struct debug_info *debug_info;
struct arch_hw_breakpoint *info;
@@ -667,35 +741,15 @@
slots = this_cpu_ptr(wp_on_reg);
debug_info = ¤t->thread.debug;
+ /*
+ * Find all watchpoints that match the reported address. If no exact
+ * match is found. Attribute the hit to the closest watchpoint.
+ */
+ rcu_read_lock();
for (i = 0; i < core_num_wrps; ++i) {
- rcu_read_lock();
-
wp = slots[i];
-
if (wp == NULL)
- goto unlock;
-
- info = counter_arch_bp(wp);
- /* AArch32 watchpoints are either 4 or 8 bytes aligned. */
- if (is_compat_task()) {
- if (info->ctrl.len == ARM_BREAKPOINT_LEN_8)
- alignment_mask = 0x7;
- else
- alignment_mask = 0x3;
- } else {
- alignment_mask = 0x7;
- }
-
- /* Check if the watchpoint value matches. */
- val = read_wb_reg(AARCH64_DBG_REG_WVR, i);
- if (val != (addr & ~alignment_mask))
- goto unlock;
-
- /* Possible match, check the byte address select to confirm. */
- ctrl_reg = read_wb_reg(AARCH64_DBG_REG_WCR, i);
- decode_ctrl_reg(ctrl_reg, &ctrl);
- if (!((1 << (addr & alignment_mask)) & ctrl.len))
- goto unlock;
+ continue;
/*
* Check that the access type matches.
@@ -704,18 +758,41 @@
access = (esr & AARCH64_ESR_ACCESS_MASK) ? HW_BREAKPOINT_W :
HW_BREAKPOINT_R;
if (!(access & hw_breakpoint_type(wp)))
- goto unlock;
+ continue;
+ /* Check if the watchpoint value and byte select match. */
+ val = read_wb_reg(AARCH64_DBG_REG_WVR, i);
+ ctrl_reg = read_wb_reg(AARCH64_DBG_REG_WCR, i);
+ decode_ctrl_reg(ctrl_reg, &ctrl);
+ dist = get_distance_from_watchpoint(addr, val, &ctrl);
+ if (dist < min_dist) {
+ min_dist = dist;
+ closest_match = i;
+ }
+ /* Is this an exact match? */
+ if (dist != 0)
+ continue;
+
+ info = counter_arch_bp(wp);
info->trigger = addr;
perf_bp_event(wp, regs);
/* Do we need to handle the stepping? */
if (!wp->overflow_handler)
step = 1;
-
-unlock:
- rcu_read_unlock();
}
+ if (min_dist > 0 && min_dist != -1) {
+ /* No exact match found. */
+ wp = slots[closest_match];
+ info = counter_arch_bp(wp);
+ info->trigger = addr;
+ perf_bp_event(wp, regs);
+
+ /* Do we need to handle the stepping? */
+ if (!wp->overflow_handler)
+ step = 1;
+ }
+ rcu_read_unlock();
if (!step)
return 0;
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index 7b1f45c..da1b32d 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -220,13 +220,13 @@
struct arch_hw_breakpoint_ctrl ctrl,
struct perf_event_attr *attr)
{
- int err, len, type, disabled = !ctrl.enabled;
+ int err, len, type, offset, disabled = !ctrl.enabled;
attr->disabled = disabled;
if (disabled)
return 0;
- err = arch_bp_generic_fields(ctrl, &len, &type);
+ err = arch_bp_generic_fields(ctrl, &len, &type, &offset);
if (err)
return err;
@@ -245,6 +245,7 @@
attr->bp_len = len;
attr->bp_type = type;
+ attr->bp_addr += offset;
return 0;
}
@@ -297,7 +298,7 @@
if (IS_ERR(bp))
return PTR_ERR(bp);
- *addr = bp ? bp->attr.bp_addr : 0;
+ *addr = bp ? counter_arch_bp(bp)->address : 0;
return 0;
}
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index 85c5235..f220e18 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -45,6 +45,7 @@
#include <linux/of_platform.h>
#include <linux/dma-mapping.h>
#include <linux/efi.h>
+#include <linux/personality.h>
#include <asm/fixmap.h>
#include <asm/cputype.h>
@@ -124,6 +125,19 @@
printk("%s", buf);
}
+struct cpuinfo_arm64 {
+ struct cpu cpu;
+ u32 reg_midr;
+};
+
+static DEFINE_PER_CPU(struct cpuinfo_arm64, cpu_data);
+
+void cpuinfo_store_cpu(void)
+{
+ struct cpuinfo_arm64 *info = this_cpu_ptr(&cpu_data);
+ info->reg_midr = read_cpuid_id();
+}
+
void __init smp_setup_processor_id(void)
{
/*
@@ -308,6 +322,8 @@
static void __init setup_machine_fdt(phys_addr_t dt_phys)
{
+ cpuinfo_store_cpu();
+
if (!dt_phys || !early_init_dt_scan(phys_to_virt(dt_phys))) {
early_print("\n"
"Error: invalid device tree blob at physical address 0x%p (virtual address 0x%p)\n"
@@ -434,14 +450,12 @@
}
arch_initcall(arm64_device_init);
-static DEFINE_PER_CPU(struct cpu, cpu_data);
-
static int __init topology_init(void)
{
int i;
for_each_possible_cpu(i) {
- struct cpu *cpu = &per_cpu(cpu_data, i);
+ struct cpu *cpu = &per_cpu(cpu_data.cpu, i);
cpu->hotpluggable = 1;
register_cpu(cpu, i);
}
@@ -462,14 +476,49 @@
NULL
};
+#ifdef CONFIG_COMPAT
+static const char *compat_hwcap_str[] = {
+ "swp",
+ "half",
+ "thumb",
+ "26bit",
+ "fastmult",
+ "fpa",
+ "vfp",
+ "edsp",
+ "java",
+ "iwmmxt",
+ "crunch",
+ "thumbee",
+ "neon",
+ "vfpv3",
+ "vfpv3d16",
+ "tls",
+ "vfpv4",
+ "idiva",
+ "idivt",
+ "vfpd32",
+ "lpae",
+ "evtstrm"
+};
+
+static const char *compat_hwcap2_str[] = {
+ "aes",
+ "pmull",
+ "sha1",
+ "sha2",
+ "crc32",
+ NULL
+};
+#endif /* CONFIG_COMPAT */
static int c_show(struct seq_file *m, void *v)
{
- int i;
-
- seq_printf(m, "Processor\t: %s rev %d (%s)\n",
- cpu_name, read_cpuid_id() & 15, ELF_PLATFORM);
+ int i, j;
for_each_present_cpu(i) {
+ struct cpuinfo_arm64 *cpuinfo = &per_cpu(cpu_data, i);
+ u32 midr = cpuinfo->reg_midr;
+
/*
* glibc reads /proc/cpuinfo to determine the number of
* online processors, looking for lines beginning with
@@ -478,30 +527,39 @@
#ifdef CONFIG_SMP
seq_printf(m, "processor\t: %d\n", i);
#endif
+
+ /*
+ * Dump out the common processor features in a single line.
+ * Userspace should read the hwcaps with getauxval(AT_HWCAP)
+ * rather than attempting to parse this, but there's a body of
+ * software which does already (at least for 32-bit).
+ */
+ seq_puts(m, "Features\t:");
+
+ if (personality(current->personality) == PER_LINUX32) {
+#ifdef CONFIG_COMPAT
+ for (j = 0; compat_hwcap_str[j]; j++)
+ if (COMPAT_ELF_HWCAP & (1 << j))
+ seq_printf(m, " %s", compat_hwcap_str[j]);
+
+ for (j = 0; compat_hwcap2_str[j]; j++)
+ if (compat_elf_hwcap2 & (1 << j))
+ seq_printf(m, " %s", compat_hwcap2_str[j]);
+#endif /* CONFIG_COMPAT */
+ } else {
+ for (j = 0; hwcap_str[j]; j++)
+ if (elf_hwcap & (1 << j))
+ seq_printf(m, " %s", hwcap_str[j]);
+ }
+ seq_puts(m, "\n");
+
+ seq_printf(m, "CPU implementer\t: 0x%02x\n", (midr >> 24));
+ seq_printf(m, "CPU architecture: 8\n");
+ seq_printf(m, "CPU variant\t: 0x%x\n", ((midr >> 20) & 0xf));
+ seq_printf(m, "CPU part\t: 0x%03x\n", ((midr >> 4) & 0xfff));
+ seq_printf(m, "CPU revision\t: %d\n\n", (midr & 0xf));
}
- /* dump out the processor features */
- seq_puts(m, "Features\t: ");
-
- for (i = 0; hwcap_str[i]; i++)
- if (elf_hwcap & (1 << i))
- seq_printf(m, "%s ", hwcap_str[i]);
-#ifdef CONFIG_ARMV7_COMPAT_CPUINFO
- if (is_compat_task()) {
- /* Print out the non-optional ARMv8 HW capabilities */
- seq_printf(m, "wp half thumb fastmult vfp edsp neon vfpv3 tlsi ");
- seq_printf(m, "vfpv4 idiva idivt ");
- }
-#endif
-
- seq_printf(m, "\nCPU implementer\t: 0x%02x\n", read_cpuid_id() >> 24);
- seq_printf(m, "CPU architecture: 8\n");
- seq_printf(m, "CPU variant\t: 0x%x\n", (read_cpuid_id() >> 20) & 15);
- seq_printf(m, "CPU part\t: 0x%03x\n", (read_cpuid_id() >> 4) & 0xfff);
- seq_printf(m, "CPU revision\t: %d\n", read_cpuid_id() & 15);
-
- seq_puts(m, "\n");
-
if (!arch_read_hardware_id)
seq_printf(m, "Hardware\t: %s\n", machine_name);
else
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index f7829153..797585e 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -162,6 +162,11 @@
cpu_ops[cpu]->cpu_postboot();
/*
+ * Log the CPU info before it is marked online and might get read.
+ */
+ cpuinfo_store_cpu();
+
+ /*
* Enable GIC and timers.
*/
smp_store_cpu_info(cpu);
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index 2198961..619344d 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -139,7 +139,7 @@
struct stackframe frame;
const register unsigned long current_sp asm ("sp");
- pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk);
+ pr_debug("%s(regs = %pP tsk = %pP)\n", __func__, regs, tsk);
if (!tsk)
tsk = current;
@@ -207,7 +207,7 @@
print_modules();
__show_regs(regs);
- pr_emerg("Process %.*s (pid: %d, stack limit = 0x%p)\n",
+ pr_emerg("Process %.*s (pid: %d, stack limit = 0x%pP)\n",
TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk), thread + 1);
if (!user_mode(regs) || in_interrupt()) {
diff --git a/build.config b/build.config
index 8897d40..34ef690 100644
--- a/build.config
+++ b/build.config
@@ -4,6 +4,7 @@
DEFCONFIG=angler_defconfig
EXTRA_CMDS=''
KERNEL_DIR=private/msm-huawei
+POST_DEFCONFIG_CMDS="check_defconfig"
LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin
FILES="
arch/arm64/boot/Image.gz-dtb
diff --git a/drivers/clk/qcom/clock-cpu-8994.c b/drivers/clk/qcom/clock-cpu-8994.c
index 6eb346b..7928f2e 100644
--- a/drivers/clk/qcom/clock-cpu-8994.c
+++ b/drivers/clk/qcom/clock-cpu-8994.c
@@ -987,6 +987,8 @@
case AUX_CLK_SEL:
rate = sys_apcsaux_clk.c.rate;
break;
+ default:
+ return;
};
break;
case PLL0_EARLY_SEL:
@@ -997,6 +999,8 @@
rate = readl_relaxed(base + C0_PLLA_L_VAL);
rate *= xo_ao.c.rate;
break;
+ default:
+ return;
};
/* One regulator */
@@ -1556,7 +1560,7 @@
struct platform_device *apc0_dev, *apc1_dev;
struct device_node *apc0_node, *apc1_node;
unsigned long apc0_fmax, apc1_fmax;
- int cpu, a53_cpu, a57_cpu;
+ int cpu, a53_cpu = 0, a57_cpu = 0;
apc0_node = of_parse_phandle(pdev->dev.of_node, "vdd-a53-supply", 0);
apc1_node = of_parse_phandle(pdev->dev.of_node, "vdd-a57-supply", 0);
@@ -1957,7 +1961,6 @@
u64 pte_efuse;
char a57speedbinstr[] = "qcom,a57-speedbinXX-vXX";
char a53speedbinstr[] = "qcom,a53-speedbinXX-vXX";
-
v2 = msm8994_v2 | msm8992;
a53_pll0_main.c.flags = CLKFLAG_NO_RATE_CACHE;
@@ -1984,7 +1987,7 @@
snprintf(a53speedbinstr, ARRAY_SIZE(a53speedbinstr),
"qcom,a53-speedbin%d-v%d", a53speedbin, pvs_ver);
- } else if (v2)
+ } else
pte_efuse = readl_relaxed(vbases[EFUSE_BASE]);
snprintf(a53speedbinstr, ARRAY_SIZE(a53speedbinstr),
diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c
index 6fe0d14..4f2b481 100644
--- a/drivers/cpufreq/cpufreq_stats.c
+++ b/drivers/cpufreq/cpufreq_stats.c
@@ -574,7 +574,7 @@
static int cpufreq_stat_notifier_policy(struct notifier_block *nb,
unsigned long val, void *data)
{
- int ret, count = 0, i;
+ int ret = 0, count = 0, i;
struct cpufreq_policy *policy = data;
struct cpufreq_frequency_table *table;
unsigned int cpu_num, cpu = policy->cpu;
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index f5fb59a..c728d9d 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2015,2017 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1024,6 +1024,13 @@
if (drawctxt->base.flags & KGSL_CONTEXT_NO_FAULT_TOLERANCE)
set_bit(KGSL_FT_DISABLE, &cmdbatch->fault_policy);
+ /*
+ * Set the fault tolerance policy to FT_REPLAY - As context wants
+ * to invalidate it after a replay attempt fails. This doesn't
+ * require to execute the default FT policy.
+ */
+ else if (drawctxt->base.flags & KGSL_CONTEXT_INVALIDATE_ON_FAULT)
+ set_bit(KGSL_FT_REPLAY, &cmdbatch->fault_policy);
else
cmdbatch->fault_policy = adreno_dev->ft_policy;
@@ -1904,7 +1911,7 @@
*
* Process expired commands and send new ones.
*/
-static void adreno_dispatcher_work(struct work_struct *work)
+static void adreno_dispatcher_work(struct kthread_work *work)
{
struct adreno_dispatcher *dispatcher =
container_of(work, struct adreno_dispatcher, work);
@@ -1981,7 +1988,7 @@
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher;
- queue_work(device->work_queue, &dispatcher->work);
+ queue_kthread_work(&kgsl_driver.worker, &dispatcher->work);
}
/**
@@ -2272,7 +2279,7 @@
setup_timer(&dispatcher->fault_timer, adreno_dispatcher_fault_timer,
(unsigned long) adreno_dev);
- INIT_WORK(&dispatcher->work, adreno_dispatcher_work);
+ init_kthread_work(&dispatcher->work, adreno_dispatcher_work);
init_completion(&dispatcher->idle_gate);
complete_all(&dispatcher->idle_gate);
diff --git a/drivers/gpu/msm/adreno_dispatch.h b/drivers/gpu/msm/adreno_dispatch.h
index c2721ad..d763714 100644
--- a/drivers/gpu/msm/adreno_dispatch.h
+++ b/drivers/gpu/msm/adreno_dispatch.h
@@ -72,7 +72,7 @@
atomic_t fault;
struct plist_head pending;
spinlock_t plist_lock;
- struct work_struct work;
+ struct kthread_work work;
struct kobject kobj;
struct completion idle_gate;
};
diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c
index f1b7231..a9107c8 100644
--- a/drivers/gpu/msm/adreno_drawctxt.c
+++ b/drivers/gpu/msm/adreno_drawctxt.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2007-2015,2017 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -333,6 +333,7 @@
KGSL_CONTEXT_PER_CONTEXT_TS |
KGSL_CONTEXT_USER_GENERATED_TS |
KGSL_CONTEXT_NO_FAULT_TOLERANCE |
+ KGSL_CONTEXT_INVALIDATE_ON_FAULT |
KGSL_CONTEXT_CTX_SWITCH |
KGSL_CONTEXT_PRIORITY_MASK |
KGSL_CONTEXT_TYPE_MASK |
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 44f981c..87a6b9e 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -4866,6 +4866,8 @@
static int __init kgsl_core_init(void)
{
int result = 0;
+ struct sched_param param = { .sched_priority = 2 };
+
/* alloc major and minor device numbers */
result = alloc_chrdev_region(&kgsl_driver.major, 0, KGSL_DEVICE_MAX,
"kgsl");
@@ -4927,6 +4929,18 @@
kgsl_mmu_set_mmutype(ksgl_mmu_type);
+ init_kthread_worker(&kgsl_driver.worker);
+
+ kgsl_driver.worker_thread = kthread_run(kthread_worker_fn,
+ &kgsl_driver.worker, "kgsl_worker_thread");
+
+ if (IS_ERR(kgsl_driver.worker_thread)) {
+ pr_err("unable to start kgsl thread\n");
+ goto err;
+ }
+
+ sched_setscheduler(kgsl_driver.worker_thread, SCHED_FIFO, ¶m);
+
kgsl_events_init();
/* create the memobjs kmem cache */
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index cb8b388..e4e53e3 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -25,6 +25,7 @@
#include <linux/regulator/consumer.h>
#include <linux/mm.h>
#include <linux/dma-attrs.h>
+#include <linux/kthread.h>
/* The number of memstore arrays limits the number of contexts allowed.
* If more contexts are needed, update multiple for MEMSTORE_SIZE
@@ -95,6 +96,8 @@
unsigned int mapped_max;
} stats;
unsigned int full_cache_threshold;
+ struct kthread_worker worker;
+ struct task_struct *worker_thread;
};
extern struct kgsl_driver kgsl_driver;
@@ -223,7 +226,7 @@
void *priv;
struct list_head node;
unsigned int created;
- struct work_struct work;
+ struct kthread_work work;
int result;
struct kgsl_event_group *group;
};
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index ab03192..424998a 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2007-2015,2017 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -87,6 +87,7 @@
{ KGSL_CONTEXT_PER_CONTEXT_TS, "PER_CONTEXT_TS" }, \
{ KGSL_CONTEXT_USER_GENERATED_TS, "USER_TS" }, \
{ KGSL_CONTEXT_NO_FAULT_TOLERANCE, "NO_FT" }, \
+ { KGSL_CONTEXT_INVALIDATE_ON_FAULT, "INVALIDATE_ON_FAULT" }, \
{ KGSL_CONTEXT_PWR_CONSTRAINT, "PWR" }, \
{ KGSL_CONTEXT_SAVE_GMEM, "SAVE_GMEM" }
diff --git a/drivers/gpu/msm/kgsl_events.c b/drivers/gpu/msm/kgsl_events.c
index 81fcbe6..f4bca88 100644
--- a/drivers/gpu/msm/kgsl_events.c
+++ b/drivers/gpu/msm/kgsl_events.c
@@ -32,7 +32,7 @@
{
list_del(&event->node);
event->result = result;
- queue_work(device->events_wq, &event->work);
+ queue_kthread_work(&kgsl_driver.worker, &event->work);
}
/**
@@ -42,7 +42,7 @@
* Each event callback has its own work struct and is run on a event specific
* workqeuue. This is the worker that queues up the event callback function.
*/
-static void _kgsl_event_worker(struct work_struct *work)
+static void _kgsl_event_worker(struct kthread_work *work)
{
struct kgsl_event *event = container_of(work, struct kgsl_event, work);
int id = KGSL_CONTEXT_ID(event->context);
@@ -261,7 +261,7 @@
event->created = jiffies;
event->group = group;
- INIT_WORK(&event->work, _kgsl_event_worker);
+ init_kthread_work(&event->work, _kgsl_event_worker);
trace_kgsl_register_event(KGSL_CONTEXT_ID(context), timestamp, func);
@@ -276,7 +276,7 @@
if (timestamp_cmp(retired, timestamp) >= 0) {
event->result = KGSL_EVENT_RETIRED;
- queue_work(device->events_wq, &event->work);
+ queue_kthread_work(&kgsl_driver.worker, &event->work);
spin_unlock(&group->lock);
return 0;
}
diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c
index 0bb3bb8..9b26c23 100644
--- a/drivers/hid/uhid.c
+++ b/drivers/hid/uhid.c
@@ -28,6 +28,8 @@
#define UHID_NAME "uhid"
#define UHID_BUFSIZE 32
+static DEFINE_MUTEX(uhid_open_mutex);
+
struct uhid_device {
struct mutex devlock;
bool running;
@@ -105,15 +107,26 @@
static int uhid_hid_open(struct hid_device *hid)
{
struct uhid_device *uhid = hid->driver_data;
+ int retval = 0;
- return uhid_queue_event(uhid, UHID_OPEN);
+ mutex_lock(&uhid_open_mutex);
+ if (!hid->open++) {
+ retval = uhid_queue_event(uhid, UHID_OPEN);
+ if (retval)
+ hid->open--;
+ }
+ mutex_unlock(&uhid_open_mutex);
+ return retval;
}
static void uhid_hid_close(struct hid_device *hid)
{
struct uhid_device *uhid = hid->driver_data;
- uhid_queue_event(uhid, UHID_CLOSE);
+ mutex_lock(&uhid_open_mutex);
+ if (!--hid->open)
+ uhid_queue_event(uhid, UHID_CLOSE);
+ mutex_unlock(&uhid_open_mutex);
}
static int uhid_hid_input(struct input_dev *input, unsigned int type,
diff --git a/drivers/input/touchscreen/cyttsp4_devtree.c b/drivers/input/touchscreen/cyttsp4_devtree.c
index 2be029d..c4648b6 100755
--- a/drivers/input/touchscreen/cyttsp4_devtree.c
+++ b/drivers/input/touchscreen/cyttsp4_devtree.c
@@ -1001,7 +1001,7 @@
{
struct cyttsp4_core_platform_data *pdata;
u32 value;
- int rc;
+ int rc = -EINVAL;
int i;
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
@@ -1228,4 +1228,3 @@
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Cypress Semiconductor");
-
diff --git a/drivers/leds/leds-qpnp-flash.c b/drivers/leds/leds-qpnp-flash.c
index f025d98..24413ca 100644
--- a/drivers/leds/leds-qpnp-flash.c
+++ b/drivers/leds/leds-qpnp-flash.c
@@ -398,6 +398,8 @@
return -EINVAL;
else
max_curr_avail_ma = (int)flash_node->max_current;
+ } else {
+ return -EINVAL;
}
return snprintf(buf, PAGE_SIZE, "%u\n", max_curr_avail_ma);
diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c
index 5a11f72..9ea33b8 100644
--- a/drivers/md/dm-android-verity.c
+++ b/drivers/md/dm-android-verity.c
@@ -299,9 +299,7 @@
struct block_device *bdev, u64 *metadata_offset)
{
u64 device_size;
-
device_size = i_size_read(bdev->bd_inode);
-
if (le32_to_cpu(fec->magic) == FEC_MAGIC)
*metadata_offset = le64_to_cpu(fec->inp_size) -
VERITY_METADATA_SIZE;
@@ -618,7 +616,7 @@
u32 data_block_size;
unsigned int major, minor,
no_of_args = VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS;
- struct fec_header fec;
+ struct fec_header uninitialized_var(fec);
struct fec_ecc_metadata uninitialized_var(ecc);
char buf[FEC_ARG_LENGTH], *buf_ptr;
unsigned long long tmpll;
diff --git a/drivers/media/platform/msm/vidc/msm_smem.c b/drivers/media/platform/msm/vidc/msm_smem.c
index 9469fb2..ff407ca 100644
--- a/drivers/media/platform/msm/vidc/msm_smem.c
+++ b/drivers/media/platform/msm/vidc/msm_smem.c
@@ -338,7 +338,7 @@
}
handle = ion_import_dma_buf(client->clnt, fd);
ret = handle == priv;
- handle ? ion_free(client->clnt, handle) : 0;
+ (!IS_ERR_OR_NULL(handle)) ? ion_free(client->clnt, handle) : 0;
return ret;
}
diff --git a/drivers/net/wireless/bcmdhd/Makefile b/drivers/net/wireless/bcmdhd/Makefile
index 251ea3d..a0d9191 100644
--- a/drivers/net/wireless/bcmdhd/Makefile
+++ b/drivers/net/wireless/bcmdhd/Makefile
@@ -147,6 +147,9 @@
# Android Packet Filter
DHDCFLAGS += -DAPF
+# FILTER ie from frames
+ DHDCFLAGS += -DFILTER_IE
+
ifneq ($(CONFIG_BCMDHD_SDIO),)
DHDCFLAGS += -DBDC -DOOB_INTR_ONLY -DHW_OOB -DDHD_BCMEVENTS -DMMC_SDIO_ABORT
DHDCFLAGS += -DBCMSDIO -DBCMLXSDMMC -DUSE_SDIOFIFO_IOVAR
diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h
index 4bfd7b9..747a285 100644
--- a/drivers/net/wireless/bcmdhd/dhd.h
+++ b/drivers/net/wireless/bcmdhd/dhd.h
@@ -683,6 +683,7 @@
#define WIFI_FEATURE_HAL_EPNO 0x40000 /* WiFi PNO enhanced */
#define WIFI_FEATURE_RSSI_MONITOR 0x80000 /* RSSI Monitor */
#define WIFI_FEATURE_CONFIG_NDO 0x200000 /* ND offload configure */
+#define WIFI_FEATURE_FILTER_IE 0x400000 /* Probe req ie filter */
#define MAX_FEATURE_SET_CONCURRRENT_GROUPS 3
@@ -788,6 +789,14 @@
extern int write_to_file(dhd_pub_t *dhd, uint8 *buf, int size);
#endif /* DHD_DEBUG */
+#ifdef FILTER_IE
+int dhd_read_from_file(dhd_pub_t *dhd);
+int dhd_parse_filter_ie(dhd_pub_t *dhd, uint8 *buf);
+int dhd_get_filter_ie_count(dhd_pub_t *dhd, uint8 *buf);
+int dhd_parse_oui(dhd_pub_t *dhd, uint8 *inbuf, uint8 *oui, int len);
+int dhd_check_valid_ie(dhd_pub_t *dhdp, uint8 *buf, int len);
+#endif /* FILTER_IE */
+
extern int dhd_dev_set_rssi_monitor_cfg(struct net_device *dev, int start,
int8 max_rssi, int8 min_rssi);
diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c
index 101b03a..5bf73f26 100644
--- a/drivers/net/wireless/bcmdhd/dhd_linux.c
+++ b/drivers/net/wireless/bcmdhd/dhd_linux.c
@@ -173,7 +173,18 @@
/* Maximum STA per radio */
#define DHD_MAX_STA 32
-
+#define FILE_BLOCK_READ_SIZE 256
+/* [FIX ME] Path define can be moved to Kconfig. Currently defined here */
+#define FILTER_IE_PATH "/etc/wifi/filter_ie"
+#define FILTER_IE_BUFSZ 1024 /* ioc buffsize for FILTER_IE */
+#define NULL_CHECK(p, s, err) \
+ do { \
+ if (!(p)) { \
+ printf("NULL POINTER (%s) : %s\n", __FUNCTION__, (s)); \
+ err = BCME_ERROR; \
+ return err; \
+ } \
+ } while (0)
const uint8 wme_fifo2ac[] = { 0, 1, 2, 3, 1, 1 };
const uint8 prio2fifo[8] = { 1, 0, 0, 1, 2, 2, 3, 3 };
@@ -4466,7 +4477,7 @@
dhd_if_t *ifp;
if((dhdinfo == NULL) || (ifidx >= DHD_MAX_IFS))
- goto fail;
+ return NULL;
ifp = dhdinfo->iflist[ifidx];
if (ifp != NULL) {
@@ -6214,6 +6225,12 @@
}
#endif
+#ifdef FILTER_IE
+ /* Failure to configure filter IE is not a fatal error, ignore it.*/
+ if (!(dhd->op_mode & (DHD_FLAG_HOSTAP_MODE | DHD_FLAG_MFG_MODE)))
+ dhd_read_from_file(dhd);
+#endif /* FILTER_IE */
+
#ifdef WL11U
dhd_interworking_enable(dhd);
#endif /* WL11U */
@@ -7960,6 +7977,11 @@
#ifdef NDO_CONFIG_SUPPORT
feature_set |= WIFI_FEATURE_CONFIG_NDO;
#endif /* NDO_CONFIG_SUPPORT */
+#ifdef FILTER_IE
+ if (FW_SUPPORTED(dhd, fie)) {
+ feature_set |= WIFI_FEATURE_FILTER_IE;
+ }
+#endif /* FILTER_IE */
return feature_set;
}
@@ -9220,7 +9242,7 @@
{
dhd_info_t *dhd = DHD_DEV_INFO(ndev);
dhd_pub_t *dhdp = &dhd->pub;
- int ret;
+ int ret = 0;
DHD_APF_LOCK(ndev);
@@ -9239,7 +9261,7 @@
{
dhd_info_t *dhd = DHD_DEV_INFO(ndev);
dhd_pub_t *dhdp = &dhd->pub;
- int ret;
+ int ret = 0;
DHD_APF_LOCK(ndev);
@@ -9258,7 +9280,7 @@
{
dhd_info_t *dhd = DHD_DEV_INFO(ndev);
dhd_pub_t *dhdp = &dhd->pub;
- int ret;
+ int ret = 0;
DHD_APF_LOCK(ndev);
@@ -9576,6 +9598,44 @@
}
#endif /* DHD_DEBUG */
+#ifdef FILTER_IE
+int dhd_read_from_file(dhd_pub_t *dhd)
+{
+ int ret = 0, nread = 0;
+ void *fd;
+ uint8 *buf;
+ NULL_CHECK(dhd, "dhd is NULL", ret);
+
+ buf = kzalloc(FILE_BLOCK_READ_SIZE, GFP_KERNEL);
+ if (!buf) {
+ DHD_ERROR(("error: failed to alllocate buf.\n"));
+ return BCME_NOMEM;
+ }
+
+ /* open file to read */
+ fd = dhd_os_open_image(FILTER_IE_PATH);
+ if (!fd) {
+ DHD_ERROR(("error: failed to open %s\n", FILTER_IE_PATH));
+ ret = BCME_EPERM;
+ goto exit;
+ }
+ nread = dhd_os_get_image_block(buf, (FILE_BLOCK_READ_SIZE - 1), fd);
+ if (nread > 0) {
+ buf[nread] = '\0';
+ if ((ret = dhd_parse_filter_ie(dhd, buf)) < 0) {
+ DHD_ERROR(("error: failed to parse filter ie\n"));
+ }
+ } else {
+ DHD_ERROR(("error: zero length file.failed to read\n"));
+ ret = BCME_ERROR;
+ }
+ dhd_os_close_image(fd);
+exit:
+ kfree(buf);
+ return ret;
+}
+#endif /* FILTER_IE */
+
int dhd_os_wake_lock_timeout(dhd_pub_t *pub)
{
dhd_info_t *dhd = (dhd_info_t *)(pub->info);
@@ -10882,3 +10942,217 @@
return NULL;
}
+#if defined FILTER_IE
+int dhd_get_filter_ie_count(dhd_pub_t *dhdp, uint8* buf)
+{
+ uint8* pstr = buf;
+ int element_count = 0;
+
+ if (buf == NULL) {
+ return BCME_ERROR;
+ }
+
+ while (*pstr != '\0') {
+ if (*pstr == '\n') {
+ element_count++;
+ }
+ pstr++;
+ }
+ /*
+ * New line character must not be present after last line.
+ * To count last line
+ */
+ element_count++;
+
+ return element_count;
+}
+
+int dhd_parse_oui(dhd_pub_t *dhd, uint8 *inbuf, uint8 *oui, int len)
+{
+ uint8 i, j, msb, lsb, oui_len = 0;
+ /*
+ * OUI can vary from 3 bytes to 5 bytes.
+ * While reading from file as ascii input it can
+ * take maximum size of 14 bytes and minumum size of
+ * 8 bytes including ":"
+ * Example 5byte OUI <AB:DE:BE:CD:FA>
+ * Example 3byte OUI <AB:DC:EF>
+ */
+
+ if ((inbuf == NULL) || (len < 8) || (len > 14 )) {
+ DHD_ERROR(("error: failed to parse OUI \n"));
+ return BCME_ERROR;
+ }
+
+ for (j = 0, i = 0; i < len; i += 3, ++j) {
+ if (!bcm_isxdigit(inbuf[i]) || !bcm_isxdigit(inbuf[i + 1])) {
+ DHD_ERROR(("error: invalid OUI format \n"));
+ return BCME_ERROR;
+ }
+ msb = inbuf[i] > '9' ? bcm_toupper(inbuf[i]) - 'A' + 10 : inbuf[i] - '0';
+ lsb = inbuf[i + 1] > '9' ? bcm_toupper(inbuf[i + 1]) - 'A' + 10 : inbuf[i + 1] - '0';
+ oui[j] = (msb << 4) | lsb;
+ }
+ /* Size of oui.It can vary from 3/4/5 */
+ oui_len = j;
+
+ return oui_len;
+}
+
+int dhd_check_valid_ie(dhd_pub_t *dhdp, uint8* buf, int len)
+{
+ int i = 0;
+
+ while (i < len) {
+ if (!bcm_isdigit(buf[i])) {
+ DHD_ERROR(("error: non digit value found in filter_ie \n"));
+ return BCME_ERROR;
+ }
+ i++;
+ }
+ if (bcm_atoi((char*)buf) > 255) {
+ DHD_ERROR(("error: element id cannot be greater than 255 \n"));
+ return BCME_ERROR;
+ }
+
+ return BCME_OK;
+}
+
+int dhd_parse_filter_ie(dhd_pub_t *dhd, uint8 *buf)
+{
+ int element_count = 0, i = 0, oui_size = 0, ret = 0;
+ uint16 kflags, bufsize, buf_space_left, id = 0, len = 0;
+ uint16 filter_iovsize, all_tlvsize;
+ wl_filter_ie_tlv_t *p_ie_tlv = NULL;
+ wl_filter_ie_iov_t *p_filter_iov = (wl_filter_ie_iov_t *) NULL;
+ char *token = NULL, *ele_token = NULL, *oui_token = NULL, *type = NULL;
+ uint8 data[20];
+
+ element_count = dhd_get_filter_ie_count(dhd, buf);
+ DHD_INFO(("total element count %d \n", element_count));
+ kflags = in_atomic() ? GFP_ATOMIC : GFP_KERNEL;
+ /* Calculate the whole buffer size */
+ filter_iovsize = sizeof(wl_filter_ie_iov_t) + FILTER_IE_BUFSZ;
+ p_filter_iov = kzalloc(filter_iovsize, kflags);
+
+ if (p_filter_iov == NULL) {
+ DHD_ERROR(("error: failed to allocate %d bytes of memory\n", filter_iovsize));
+ return BCME_ERROR;
+ }
+
+ /* setup filter iovar header */
+ p_filter_iov->version = WL_FILTER_IE_VERSION;
+ p_filter_iov->len = filter_iovsize;
+ p_filter_iov->fixed_length = p_filter_iov->len - FILTER_IE_BUFSZ;
+ p_filter_iov->pktflag = FC_PROBE_REQ;
+ p_filter_iov->option = WL_FILTER_IE_CHECK_SUB_OPTION;
+ /* setup TLVs */
+ bufsize = filter_iovsize - WL_FILTER_IE_IOV_HDR_SIZE; /* adjust available size for TLVs */
+ p_ie_tlv = &p_filter_iov->tlvs[0];
+ buf_space_left = bufsize;
+
+ while ((i < element_count) && (buf != NULL)) {
+ len = 0;
+ /* token contains one line of input data */
+ token = bcmstrtok((char**)&buf, "\n", NULL);
+ if (token == NULL) {
+ break;
+ }
+ if ((ele_token = bcmstrstr(token, ",")) == NULL) {
+ /* only element id is present */
+ if (dhd_check_valid_ie(dhd, token, strlen(token)) == BCME_ERROR) {
+ DHD_ERROR(("error: Invalid element id \n"));
+ ret = BCME_ERROR;
+ goto exit;
+ }
+ id = bcm_atoi((char*)token);
+ data[len++] = WL_FILTER_IE_SET;
+ } else {
+ /* oui is present */
+ ele_token = bcmstrtok(&token, ",", NULL);
+ if ((ele_token == NULL) || (dhd_check_valid_ie(dhd, ele_token,
+ strlen(ele_token)) == BCME_ERROR)) {
+ DHD_ERROR(("error: Invalid element id \n"));
+ ret = BCME_ERROR;
+ goto exit;
+ }
+ id = bcm_atoi((char*)ele_token);
+ data[len++] = WL_FILTER_IE_SET;
+ if ((oui_token = bcmstrstr(token, ",")) == NULL) {
+ oui_size = dhd_parse_oui(dhd, token, &(data[len]), strlen(token));
+ if (oui_size == BCME_ERROR) {
+ DHD_ERROR(("error: Invalid OUI \n"));
+ ret = BCME_ERROR;
+ goto exit;
+ }
+ len += oui_size;
+ } else {
+ /* type is present */
+ oui_token = bcmstrtok(&token, ",", NULL);
+ if ((oui_token == NULL) || ((oui_size = dhd_parse_oui(dhd, oui_token,
+ &(data[len]), strlen(oui_token))) == BCME_ERROR)) {
+ DHD_ERROR(("error: Invalid OUI \n"));
+ ret = BCME_ERROR;
+ goto exit;
+ }
+ len += oui_size;
+ if ((type = bcmstrstr(token, ",")) == NULL) {
+ if (dhd_check_valid_ie(dhd, token, strlen(token)) == BCME_ERROR) {
+ DHD_ERROR(("error: Invalid type \n"));
+ ret = BCME_ERROR;
+ goto exit;
+ }
+ data[len++] = bcm_atoi((char*)token);
+ } else {
+ /* subtype is present */
+ type = bcmstrtok(&token, ",", NULL);
+ if ((type == NULL) || (dhd_check_valid_ie(dhd, type,
+ strlen(type)) == BCME_ERROR)) {
+ DHD_ERROR(("error: Invalid type \n"));
+ ret = BCME_ERROR;
+ goto exit;
+ }
+ data[len++] = bcm_atoi((char*)type);
+ /* subtype is last element */
+ if ((token == NULL) || (*token == '\0') ||
+ (dhd_check_valid_ie(dhd, token, strlen(token)) == BCME_ERROR)) {
+ DHD_ERROR(("error: Invalid subtype \n"));
+ ret = BCME_ERROR;
+ goto exit;
+ }
+ data[len++] = bcm_atoi((char*)token);
+ }
+ }
+ }
+ ret = bcm_pack_xtlv_entry((uint8 **)&p_ie_tlv,
+ &buf_space_left, id, len, data, BCM_XTLV_OPTION_ALIGN32);
+ if (ret != BCME_OK) {
+ DHD_ERROR(("%s : bcm_pack_xtlv_entry() failed ,"
+ "status=%d\n", __FUNCTION__, ret));
+ kfree(p_filter_iov);
+ return ret;
+ }
+ i++;
+ }
+ if (i == 0) {
+ /* file is empty or first line is blank */
+ DHD_ERROR(("error: filter_ie file is empty or first line is blank \n"));
+ kfree(p_filter_iov);
+ return BCME_ERROR;
+ }
+ /* update the iov header, set len to include all TLVs + header */
+ all_tlvsize = (bufsize - buf_space_left);
+ p_filter_iov->len = htol16(all_tlvsize + WL_FILTER_IE_IOV_HDR_SIZE);
+ ret = dhd_iovar(dhd, 0, "filter_ie", (char *)p_filter_iov,
+ p_filter_iov->len, NULL, 0, TRUE);
+ if (ret != BCME_OK) {
+ DHD_ERROR(("error: IOVAR failed, status=%d\n", ret));
+ }
+exit:
+ /* clean up */
+ kfree(p_filter_iov);
+ return ret;
+}
+#endif /* FILTER_IE */
+
+
diff --git a/drivers/net/wireless/bcmdhd/dhd_pno.c b/drivers/net/wireless/bcmdhd/dhd_pno.c
index 1a8e4ee..31a3b7c 100644
--- a/drivers/net/wireless/bcmdhd/dhd_pno.c
+++ b/drivers/net/wireless/bcmdhd/dhd_pno.c
@@ -78,8 +78,14 @@
#define PNO_BESTNET_LEN 2048
#define PNO_ON 1
#define PNO_OFF 0
+#define CHANNEL_2G_MIN 1
#define CHANNEL_2G_MAX 14
-#define CHANNEL_5G_MAX 165
+#define CHANNEL_5G_MIN 34
+#define CHANNEL_5G_MAX 169
+#define IS_2G_CHANNEL(ch) (ch >= CHANNEL_2G_MIN) && \
+ (ch <= CHANNEL_2G_MAX)
+#define IS_5G_CHANNEL(ch) (ch >= CHANNEL_5G_MIN) && \
+ (ch <= CHANNEL_5G_MAX)
#define MAX_NODE_CNT 5
#define WLS_SUPPORTED(pno_state) (pno_state->wls_supported == TRUE)
#define TIME_DIFF(timestamp1, timestamp2) (abs((uint32)(timestamp1/1000) \
@@ -106,16 +112,28 @@
static int dhd_pno_set_legacy_pno(dhd_pub_t *dhd, uint16 scan_fr, int pno_repeat,
int pno_freq_expo_max, uint16 *channel_list, int nchan);
-static inline bool
-is_dfs(uint16 channel)
+static bool
+is_dfs(dhd_pub_t *dhd, uint16 channel)
{
- if (channel >= 52 && channel <= 64) /* class 2 */
- return TRUE;
- else if (channel >= 100 && channel <= 144) /* class 4 */
- return TRUE;
- else
+ u32 ch;
+ s32 err;
+ u8 buf[32];
+
+ ch = wl_ch_host_to_driver(channel);
+ err = dhd_iovar(dhd, 0, "per_chan_info", (char *)&ch,
+ sizeof(u32), buf, sizeof(buf), FALSE);
+ if (unlikely(err)) {
+ DHD_ERROR(("get per chan info failed:%d\n", err));
return FALSE;
+ }
+
+ /* Check the channel flags returned by fw */
+ if (*((u32 *)buf) & WL_CHAN_PASSIVE)
+ return TRUE;
+
+ return FALSE;
}
+
int
dhd_pno_clean(dhd_pub_t *dhd)
{
@@ -703,37 +721,42 @@
err = dhd_wl_ioctl_cmd(dhd, WLC_GET_VALID_CHANNELS, chan_buf, sizeof(chan_buf), FALSE, 0);
if (err < 0) {
DHD_ERROR(("failed to get channel list (err: %d)\n", err));
- goto exit;
+ return err;
}
for (i = 0, j = 0; i < dtoh32(list->count) && i < *nchan; i++) {
- if (band == WLC_BAND_2G) {
- if (dtoh32(list->element[i]) > CHANNEL_2G_MAX)
- continue;
- } else if (band == WLC_BAND_5G) {
- if (dtoh32(list->element[i]) <= CHANNEL_2G_MAX)
- continue;
- if (skip_dfs && is_dfs(dtoh32(list->element[i])))
- continue;
- } else if (band == WLC_BAND_AUTO) {
- if (skip_dfs || !is_dfs(dtoh32(list->element[i])))
+ if (IS_2G_CHANNEL(dtoh32(list->element[i]))) {
+ if (!(band & WLC_BAND_2G)) {
+ /* Skip, if not 2g */
continue;
+ }
+ /* fall through to include the channel */
+ } else if (IS_5G_CHANNEL(dtoh32(list->element[i]))) {
+ bool dfs_channel = is_dfs(dhd, dtoh32(list->element[i]));
- } else { /* All channels */
- if (skip_dfs && is_dfs(dtoh32(list->element[i])))
+ if ((skip_dfs && dfs_channel) || (!(band & WLC_BAND_5G) && !dfs_channel)) {
+ /* Skip the channel if:
+ * the DFS bit is NOT set and the channel is a dfs channel
+ * the band 5G is not set and the channel is a non DFS 5G channel.
+ */
continue;
- }
- if (dtoh32(list->element[i]) <= CHANNEL_5G_MAX) {
- d_chan_list[j++] = (uint16) dtoh32(list->element[i]);
+ }
+ /* fall through to include the channel */
} else {
- err = BCME_BADCHAN;
- goto exit;
+ /* Not in range. Bad channel */
+ DHD_ERROR(("Not in range. bad channel\n"));
+ *nchan = 0;
+ return BCME_BADCHAN;
}
+
+ /* Include the channel */
+ d_chan_list[j++] = (uint16) dtoh32(list->element[i]);
}
+
*nchan = j;
-exit:
return err;
}
+
static int
_dhd_pno_convert_format(dhd_pub_t *dhd, struct dhd_pno_batch_params *params_batch,
char *buf, int nbufsize)
@@ -2147,6 +2170,7 @@
pfn_gscan_cfg_t->flags =
(gscan_params->send_all_results_flag & GSCAN_SEND_ALL_RESULTS_MASK);
+ pfn_gscan_cfg_t->flags |= FORCE_ALL_CHANNEL_BUCKETS_IN_FIRST_SCAN;
pfn_gscan_cfg_t->count_of_channel_buckets = num_buckets_to_fw;
pfn_gscan_cfg_t->retry_threshold = GSCAN_RETRY_THRESHOLD;
diff --git a/drivers/net/wireless/bcmdhd/dhd_rtt.c b/drivers/net/wireless/bcmdhd/dhd_rtt.c
index 34b05be..7ad5df6 100644
--- a/drivers/net/wireless/bcmdhd/dhd_rtt.c
+++ b/drivers/net/wireless/bcmdhd/dhd_rtt.c
@@ -437,7 +437,7 @@
int
rate_rspec2rate(uint32 rspec)
{
- int rate = -1;
+ int rate = 0;
if (RSPEC_ISLEGACY(rspec)) {
rate = 500 * (rspec & WL_RSPEC_RATE_MASK);
@@ -465,7 +465,7 @@
DHD_WARN(0,);
}
- return (rate == 0) ? -1 : rate;
+ return rate;
}
char resp_buf[WLC_IOCTL_SMLEN];
@@ -1515,18 +1515,38 @@
dhd_rtt_convert_rate_to_host(uint32 rspec)
{
wifi_rate_t host_rate;
+ uint32 bandwidth;
memset(&host_rate, 0, sizeof(wifi_rate_t));
- if ((rspec & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_RATE) {
+ if (RSPEC_ISLEGACY(rspec)) {
host_rate.preamble = 0;
- } else if ((rspec & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_HT) {
+ } else if (RSPEC_ISHT(rspec)) {
host_rate.preamble = 2;
host_rate.rateMcsIdx = rspec & WL_RSPEC_RATE_MASK;
- } else if ((rspec & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_VHT) {
+ } else if (RSPEC_ISVHT(rspec)) {
host_rate.preamble = 3;
host_rate.rateMcsIdx = rspec & WL_RSPEC_VHT_MCS_MASK;
host_rate.nss = (rspec & WL_RSPEC_VHT_NSS_MASK) >> WL_RSPEC_VHT_NSS_SHIFT;
}
- host_rate.bw = (rspec & WL_RSPEC_BW_MASK) - 1;
+
+ bandwidth = RSPEC_BW(rspec);
+ switch (bandwidth) {
+ case WL_RSPEC_BW_20MHZ:
+ host_rate.bw = RTT_RATE_20M;
+ break;
+ case WL_RSPEC_BW_40MHZ:
+ host_rate.bw = RTT_RATE_40M;
+ break;
+ case WL_RSPEC_BW_80MHZ:
+ host_rate.bw = RTT_RATE_80M;
+ break;
+ case WL_RSPEC_BW_160MHZ:
+ host_rate.bw = RTT_RATE_160M;
+ break;
+ default:
+ host_rate.bw = RTT_RATE_20M;
+ break;
+ }
+
host_rate.bitrate = rate_rspec2rate(rspec) / 100; /* 100kbps */
DHD_RTT(("bit rate : %d\n", host_rate.bitrate));
return host_rate;
diff --git a/drivers/net/wireless/bcmdhd/dhd_rtt.h b/drivers/net/wireless/bcmdhd/dhd_rtt.h
index d5b000f..98b3edd 100644
--- a/drivers/net/wireless/bcmdhd/dhd_rtt.h
+++ b/drivers/net/wireless/bcmdhd/dhd_rtt.h
@@ -128,6 +128,14 @@
RTT_BW_80 = BIT(4),
RTT_BW_160 = BIT(5)
};
+
+enum rtt_rate_bw {
+ RTT_RATE_20M,
+ RTT_RATE_40M,
+ RTT_RATE_80M,
+ RTT_RATE_160M
+};
+
#define FTM_MAX_NUM_BURST_EXP 14
#define HAS_11MC_CAP(cap) (cap & RTT_CAP_FTM_WAY)
#define HAS_ONEWAY_CAP(cap) (cap & RTT_CAP_ONE_WAY)
diff --git a/drivers/net/wireless/bcmdhd/include/wlioctl.h b/drivers/net/wireless/bcmdhd/include/wlioctl.h
index b89c4be..ba1ad58 100644
--- a/drivers/net/wireless/bcmdhd/include/wlioctl.h
+++ b/drivers/net/wireless/bcmdhd/include/wlioctl.h
@@ -2750,6 +2750,7 @@
} wl_pfn_gscan_ch_bucket_cfg_t;
#define GSCAN_SEND_ALL_RESULTS_MASK (1 << 0)
+#define FORCE_ALL_CHANNEL_BUCKETS_IN_FIRST_SCAN (1 << 3) /* BIT1 and BIT2 reserved */
#define GSCAN_CFG_FLAGS_ONLY_MASK (1 << 7)
#define WL_GSCAN_CFG_VERSION 2
typedef struct wl_pfn_gscan_cfg {
@@ -2758,6 +2759,7 @@
* BIT1 Reserved
* BIT2 Reserved
* Add any future flags here
+ * BIT3 1 = scan all buckets in first scan
* BIT7 1 = no other useful cfg sent
*/
uint8 flags;
@@ -6670,6 +6672,34 @@
wl_roam_prof_t roam_prof[WL_MAX_ROAM_PROF_BRACKETS];
} wl_roam_prof_band_t;
+#ifdef FILTER_IE
+#define WL_FILTER_IE_VERSION 1
+#define WL_FILTER_IE_IOV_HDR_SIZE OFFSETOF(wl_filter_ie_iov_t, tlvs)
+enum wl_filter_ie_options {
+ WL_FILTER_IE_CLEAR = 0, /* allow the element id in the packet.It can be used only in suboption */
+ WL_FILTER_IE_SET = 1, /* filter the element id from the packet.It can be used only in suboption */
+ WL_FILTER_IE_LIST= 2, /* list the element ID's.It can be only set as option */
+ WL_FILTER_IE_CLEAR_ALL= 3, /* clear all the element ID's.It can be only set as option */
+ WL_FILTER_IE_CHECK_SUB_OPTION = 4 /* check for suboptions.It can be set only as option */
+};
+
+typedef struct wl_filter_ie_tlv {
+ uint16 id;
+ uint16 len; /* sub option length + pattern length */
+ uint8 data[1]; /* sub option + pattern for matching (like, OUI, type, sub type, extended element id, etc) */
+} wl_filter_ie_tlv_t;
+
+typedef struct wl_filter_ie_iov {
+ uint16 version; /* Structure version */
+ uint16 len; /* Total length of the structure */
+ uint16 fixed_length; /* Total length of fixed fields */
+ uint8 option; /* Filter action - check for sub option, list, clearall*/
+ uint8 pad[1]; /* Align to 4 bytes */
+ uint32 pktflag; /* frame type */
+ wl_filter_ie_tlv_t tlvs[0]; /* variable data (zero in case of options like list ,clearall) */
+} wl_filter_ie_iov_t;
+#endif /* FILTER_IE */
+
/* no default structure packing */
#include <packed_section_end.h>
diff --git a/drivers/net/wireless/bcmdhd/wl_android.c b/drivers/net/wireless/bcmdhd/wl_android.c
index 8d89d98..1410387 100644
--- a/drivers/net/wireless/bcmdhd/wl_android.c
+++ b/drivers/net/wireless/bcmdhd/wl_android.c
@@ -246,22 +246,17 @@
return -1;
if ((ssid.SSID_len == 0) || (ssid.SSID_len > DOT11_MAX_SSID_LEN)) {
DHD_ERROR(("%s: wldev_get_ssid failed\n", __FUNCTION__));
+ } else if (total_len <= ssid.SSID_len) {
+ return -ENOMEM;
} else {
- if (total_len > ssid.SSID_len) {
- memcpy(command, ssid.SSID, ssid.SSID_len);
- bytes_written = ssid.SSID_len;
- } else {
- return BCME_ERROR;
- }
+ memcpy(command, ssid.SSID, ssid.SSID_len);
+ bytes_written = ssid.SSID_len;
}
-
- if ((total_len - bytes_written) >= (strlen(" rssi -XXX") + 1)) {
- bytes_written += snprintf(&command[bytes_written], total_len - bytes_written,
- " rssi %d", rssi);
- command[bytes_written] = '\0';
- } else {
- return BCME_ERROR;
- }
+ if ((total_len - bytes_written) < (strlen(" rssi -XXX") + 1))
+ return -ENOMEM;
+ bytes_written += scnprintf(&command[bytes_written],
+ total_len - bytes_written, " rssi %d", rssi);
+ command[bytes_written] = '\0';
DHD_INFO(("%s: command result is %s (%d)\n", __FUNCTION__, command, bytes_written));
return bytes_written;
@@ -1334,17 +1329,13 @@
}
if ((priv_cmd.total_len > PRIVATE_COMMAND_MAX_LEN) || (priv_cmd.total_len < 0)) {
- DHD_ERROR(("%s: buf length invalid:%d \n", __FUNCTION__, priv_cmd.total_len));
+ DHD_ERROR(("%s: buf length invalid:%d\n", __FUNCTION__,
+ priv_cmd.total_len));
ret = -EINVAL;
goto exit;
}
- if (priv_cmd.total_len < PRIVATE_COMMAND_DEF_LEN) {
- buf_size = PRIVATE_COMMAND_DEF_LEN;
- } else {
- buf_size = priv_cmd.total_len;
- }
-
+ buf_size = max(priv_cmd.total_len, PRIVATE_COMMAND_DEF_LEN);
command = kmalloc((buf_size + 1), GFP_KERNEL);
if (!command)
@@ -1361,22 +1352,20 @@
DHD_INFO(("%s: Android private cmd \"%s\" on %s\n", __FUNCTION__, command, ifr->ifr_name));
- bytes_written = wl_handle_private_cmd(net, command, buf_size);
+ bytes_written = wl_handle_private_cmd(net, command, priv_cmd.total_len);
if (bytes_written >= 0) {
- if ((bytes_written == 0) && (priv_cmd.total_len > 0)) {
+ if ((bytes_written == 0) && (priv_cmd.total_len > 0))
command[0] = '\0';
- }
if (bytes_written >= priv_cmd.total_len) {
- DHD_ERROR(("%s: not enough for output. bytes_written:%d >= total_len:%d \n",
- __FUNCTION__, bytes_written, priv_cmd.total_len));
+ DHD_ERROR(("%s: err. b_w:%d >= tot:%d\n", __FUNCTION__,
+ bytes_written, priv_cmd.total_len));
ret = BCME_BUFTOOSHORT;
goto exit;
- } else {
- bytes_written++;
}
+ bytes_written++;
priv_cmd.used_len = bytes_written;
if (copy_to_user(priv_cmd.buf, command, bytes_written)) {
- DHD_ERROR(("%s: failed to copy data to user buffer\n", __FUNCTION__));
+ DHD_ERROR(("%s: failed copy to user\n", __FUNCTION__));
ret = -EFAULT;
}
} else {
@@ -1385,17 +1374,13 @@
exit:
net_os_wake_unlock(net);
- if (command) {
- kfree(command);
- }
-
+ kfree(command);
return ret;
}
int
wl_handle_private_cmd(struct net_device *net, char *command, u32 buf_size)
{
-
int bytes_written = 0;
android_wifi_priv_cmd priv_cmd;
@@ -1412,7 +1397,7 @@
if (!g_wifi_on) {
DHD_ERROR(("%s: Ignore private cmd \"%s\" - iface is down\n",
- __FUNCTION__, command));
+ __FUNCTION__, command));
return 0;
}
@@ -1570,8 +1555,7 @@
}
else {
DHD_ERROR(("Unknown PRIVATE command %s - ignored\n", command));
- snprintf(command, 5, "FAIL");
- bytes_written = strlen("FAIL");
+ bytes_written = scnprintf(command, sizeof("FAIL"), "FAIL");
}
return bytes_written;
diff --git a/drivers/net/wireless/bcmdhd/wl_cfgvendor.c b/drivers/net/wireless/bcmdhd/wl_cfgvendor.c
index 140a20d..ece2b39 100644
--- a/drivers/net/wireless/bcmdhd/wl_cfgvendor.c
+++ b/drivers/net/wireless/bcmdhd/wl_cfgvendor.c
@@ -840,11 +840,12 @@
const struct nlattr *outer, *inner, *iter;
uint32 cnt_ssid = 0;
wl_pfn_ssid_params_t params;
- bool flush = FALSE;
+ bool flush = FALSE, found = false;
memset(¶ms, 0, sizeof(wl_pfn_ssid_params_t));
nla_for_each_attr(iter, data, len, tmp2) {
type = nla_type(iter);
+ found = true;
switch (type) {
case GSCAN_ATTRIBUTE_EPNO_SSID_LIST:
nla_for_each_nested(outer, iter, tmp) {
@@ -942,6 +943,11 @@
}
}
+
+ if (!found) {
+ return -EINVAL;
+ }
+
if (cnt_ssid != num) {
WL_ERR(("%s: num_ssid %d does not match ssids sent %d\n", __FUNCTION__,
num, cnt_ssid));
@@ -2456,8 +2462,8 @@
dhd_pub_t *dhd_pub = cfg->pub;
struct sk_buff *skb = NULL;
const struct nlattr *iter;
- void __user *user_buf;
- uint16 req_count, resp_count;
+ void __user *user_buf = NULL;
+ uint16 req_count = 0, resp_count;
int ret, tmp, type, mem_needed;
nla_for_each_attr(iter, data, len, tmp) {
@@ -2827,13 +2833,15 @@
const struct nlattr *iter;
int ret = BCME_OK, rem, type;
u8 enable;
+ bool found = false;
nla_for_each_attr(iter, data, len, rem) {
type = nla_type(iter);
switch (type) {
case ANDR_WIFI_ATTRIBUTE_ND_OFFLOAD_VALUE:
enable = nla_get_u8(iter);
- break;
+ found = true;
+ break;
default:
WL_ERR(("Unknown type: %d\n", type));
ret = BCME_BADARG;
@@ -2841,6 +2849,12 @@
}
}
+ if (!found) {
+ WL_ERR(("nla doesn't fit into remaining bytes\n"));
+ ret = BCME_BADARG;
+ goto exit;
+ }
+
ret = dhd_dev_ndo_cfg(bcmcfg_to_prmry_ndev(cfg), enable);
if (ret < 0) {
WL_ERR(("dhd_dev_ndo_cfg() failed: %d\n", ret));
@@ -3252,9 +3266,7 @@
{ OUI_GOOGLE, GOOGLE_SCAN_EPNO_EVENT },
{ OUI_GOOGLE, GOOGLE_DEBUG_RING_EVENT },
{ OUI_GOOGLE, GOOGLE_FW_DUMP_EVENT },
-#ifdef DHD_ANQPO_SUPPORT
{ OUI_GOOGLE, GOOGLE_PNO_HOTSPOT_FOUND_EVENT },
-#endif /* DHD_ANQPO_SUPPORT */
{ OUI_GOOGLE, GOOGLE_RSSI_MONITOR_EVENT },
{ OUI_GOOGLE, GOOGLE_MKEEP_ALIVE_EVENT }
};
diff --git a/drivers/platform/msm/ipa/ipa.c b/drivers/platform/msm/ipa/ipa.c
index 601081d7..d2fbf62 100644
--- a/drivers/platform/msm/ipa/ipa.c
+++ b/drivers/platform/msm/ipa/ipa.c
@@ -1431,7 +1431,7 @@
u32 *entry;
mem.base = dma_alloc_coherent(ipa_ctx->pdev, 4, &mem.phys_base,
- GFP_KERNEL);
+ GFP_ATOMIC);
if (!mem.base) {
IPAERR("failed to alloc DMA buff of size %d\n", mem.size);
return -ENOMEM;
diff --git a/drivers/power/qcom/debug_core.c b/drivers/power/qcom/debug_core.c
index 70574ff..60cc819 100644
--- a/drivers/power/qcom/debug_core.c
+++ b/drivers/power/qcom/debug_core.c
@@ -290,7 +290,7 @@
msm_core_data = get_cpu_pwr_stats();
if (!msm_core_data)
- goto fail;
+ return PTR_ERR(NULL);
dir = debugfs_create_dir("msm_core", NULL);
if (IS_ERR_OR_NULL(dir))
diff --git a/drivers/power/qcom/msm-core.c b/drivers/power/qcom/msm-core.c
index 5e23c19..93248b9 100644
--- a/drivers/power/qcom/msm-core.c
+++ b/drivers/power/qcom/msm-core.c
@@ -347,11 +347,11 @@
{
int i;
int ret;
- int cpu;
struct cpu_activity_info *node;
struct cpu_static_info *sp, *clear_sp;
int mpidr = (argp->cluster << 8);
int cpumask = argp->cpumask;
+ int cpu = num_possible_cpus();
pr_debug("cpumask %d, cluster: %d\n", argp->cpumask, argp->cluster);
for (i = 0; i < MAX_CORES_PER_CLUSTER; i++, cpumask >>= 1) {
diff --git a/drivers/power/qpnp-fg.c b/drivers/power/qpnp-fg.c
index 3e52a5e..4918628 100644
--- a/drivers/power/qpnp-fg.c
+++ b/drivers/power/qpnp-fg.c
@@ -1983,9 +1983,7 @@
}
battery_soc = get_battery_soc_raw(chip) * 100 / FULL_PERCENT_3B;
- if (rc) {
- goto error_done;
- } else if (battery_soc < 25 || battery_soc > 75) {
+ if (battery_soc < 25 || battery_soc > 75) {
if (fg_debug_mask & FG_AGING)
pr_info("Battery SoC (%d) out of range, aborting\n",
(int)battery_soc);
@@ -3523,6 +3521,7 @@
const char *data, *batt_type_str, *old_batt_type;
bool tried_again = false, vbat_in_range, profiles_same;
u8 reg = 0;
+ old_batt_type = default_batt_type;
wait:
fg_stay_awake(&chip->profile_wakeup_source);
@@ -3550,7 +3549,6 @@
fg_batt_type);
if (!profile_node) {
pr_err("couldn't find profile handle\n");
- old_batt_type = default_batt_type;
rc = -ENODATA;
goto fail;
}
@@ -4964,7 +4962,6 @@
static int fg_setup_memif_offset(struct fg_chip *chip)
{
int rc;
- u8 dig_major;
rc = fg_read(chip, chip->revision, chip->mem_base + DIG_MINOR, 4);
if (rc) {
@@ -4978,7 +4975,7 @@
chip->offset = offset[0].address;
break;
default:
- pr_err("Digital Major rev=%d not supported\n", dig_major);
+ pr_err("Digital Major rev=%d not supported\n", chip->revision[DIG_MAJOR]);
return -EINVAL;
}
diff --git a/drivers/power/qpnp-smbcharger.c b/drivers/power/qpnp-smbcharger.c
index 95c388b..e000c7c 100755
--- a/drivers/power/qpnp-smbcharger.c
+++ b/drivers/power/qpnp-smbcharger.c
@@ -6538,6 +6538,8 @@
rc);
return rc;
}
+ } else {
+ vadc_dev = NULL;
}
chip = devm_kzalloc(&spmi->dev, sizeof(*chip), GFP_KERNEL);
diff --git a/drivers/power/smb1351-charger.c b/drivers/power/smb1351-charger.c
index 2f54175..be6e387 100755
--- a/drivers/power/smb1351-charger.c
+++ b/drivers/power/smb1351-charger.c
@@ -1611,7 +1611,7 @@
static void smb1351_chg_adc_notification(enum qpnp_tm_state state, void *ctx)
{
struct smb1351_charger *chip = ctx;
- struct battery_status *cur;
+ struct battery_status *cur = NULL;
int temp;
if (state >= ADC_TM_STATE_NUM) {
@@ -1711,6 +1711,11 @@
}
}
+ if (!cur) {
+ pr_err("invalid transaction: state %d, temp %d\n", state, temp);
+ return;
+ }
+
if (cur->batt_present)
chip->battery_missing = false;
else
diff --git a/drivers/soc/qcom/npa-dump.c b/drivers/soc/qcom/npa-dump.c
index 2b8cb3c..ef343f8 100644
--- a/drivers/soc/qcom/npa-dump.c
+++ b/drivers/soc/qcom/npa-dump.c
@@ -155,7 +155,7 @@
struct resource *res;
void __iomem *npa_base, *rpm_base;
struct dentry *dent;
- int ret;
+ int ret = 0;
/* Get the location of the NPA log's start address offset */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig
index 62a6df9..e191f32 100644
--- a/drivers/staging/android/Kconfig
+++ b/drivers/staging/android/Kconfig
@@ -26,6 +26,18 @@
Enable to support an old 32-bit Android user-space. Breaks the new
Android user-space.
+config ANDROID_BINDER_DEVICES
+ string "Android Binder devices"
+ depends on ANDROID_BINDER_IPC
+ default "binder"
+ ---help---
+ Default value for the binder.devices parameter.
+
+ The binder.devices parameter is a comma-separated list of strings
+ that specifies the names of the binder device nodes that will be
+ created. Each binder device has its own context manager, and is
+ therefore logically separated from the other devices.
+
config ASHMEM
bool "Enable the Anonymous Shared Memory Subsystem"
default n
diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c
index 7340ef7..0c757b7 100644
--- a/drivers/staging/android/binder.c
+++ b/drivers/staging/android/binder.c
@@ -46,14 +46,13 @@
static DEFINE_MUTEX(binder_deferred_lock);
static DEFINE_MUTEX(binder_mmap_lock);
+static HLIST_HEAD(binder_devices);
static HLIST_HEAD(binder_procs);
static HLIST_HEAD(binder_deferred_list);
static HLIST_HEAD(binder_dead_nodes);
static struct dentry *binder_debugfs_dir_entry_root;
static struct dentry *binder_debugfs_dir_entry_proc;
-static struct binder_node *binder_context_mgr_node;
-static kuid_t binder_context_mgr_uid = INVALID_UID;
static int binder_last_id;
static struct workqueue_struct *binder_deferred_workqueue;
@@ -112,6 +111,9 @@
static bool binder_debug_no_lock;
module_param_named(proc_no_lock, binder_debug_no_lock, bool, S_IWUSR | S_IRUGO);
+static char *binder_devices_param = CONFIG_ANDROID_BINDER_DEVICES;
+module_param_named(devices, binder_devices_param, charp, S_IRUGO);
+
static DECLARE_WAIT_QUEUE_HEAD(binder_user_error_wait);
static int binder_stop_on_user_error;
@@ -141,6 +143,17 @@
binder_stop_on_user_error = 2; \
} while (0)
+#define to_flat_binder_object(hdr) \
+ container_of(hdr, struct flat_binder_object, hdr)
+
+#define to_binder_fd_object(hdr) container_of(hdr, struct binder_fd_object, hdr)
+
+#define to_binder_buffer_object(hdr) \
+ container_of(hdr, struct binder_buffer_object, hdr)
+
+#define to_binder_fd_array_object(hdr) \
+ container_of(hdr, struct binder_fd_array_object, hdr)
+
enum binder_stat_types {
BINDER_STAT_PROC,
BINDER_STAT_THREAD,
@@ -154,7 +167,7 @@
struct binder_stats {
int br[_IOC_NR(BR_FAILED_REPLY) + 1];
- int bc[_IOC_NR(BC_DEAD_BINDER_DONE) + 1];
+ int bc[_IOC_NR(BC_REPLY_SG) + 1];
int obj_created[BINDER_STAT_COUNT];
int obj_deleted[BINDER_STAT_COUNT];
};
@@ -182,6 +195,7 @@
int to_node;
int data_size;
int offsets_size;
+ const char *context_name;
};
struct binder_transaction_log {
int next;
@@ -205,6 +219,18 @@
return e;
}
+struct binder_context {
+ struct binder_node *binder_context_mgr_node;
+ kuid_t binder_context_mgr_uid;
+ const char *name;
+};
+
+struct binder_device {
+ struct hlist_node hlist;
+ struct miscdevice miscdev;
+ struct binder_context context;
+};
+
struct binder_work {
struct list_head entry;
enum {
@@ -277,6 +303,7 @@
struct binder_node *target_node;
size_t data_size;
size_t offsets_size;
+ size_t extra_buffers_size;
uint8_t data[0];
};
@@ -320,6 +347,7 @@
int ready_threads;
long default_priority;
struct dentry *debugfs_entry;
+ struct binder_context *context;
};
enum {
@@ -715,7 +743,9 @@
static struct binder_buffer *binder_alloc_buf(struct binder_proc *proc,
size_t data_size,
- size_t offsets_size, int is_async)
+ size_t offsets_size,
+ size_t extra_buffers_size,
+ int is_async)
{
struct rb_node *n = proc->free_buffers.rb_node;
struct binder_buffer *buffer;
@@ -723,7 +753,7 @@
struct rb_node *best_fit = NULL;
void *has_page_addr;
void *end_page_addr;
- size_t size;
+ size_t size, data_offsets_size;
if (proc->vma == NULL) {
pr_err("%d: binder_alloc_buf, no vma\n",
@@ -731,15 +761,20 @@
return NULL;
}
- size = ALIGN(data_size, sizeof(void *)) +
+ data_offsets_size = ALIGN(data_size, sizeof(void *)) +
ALIGN(offsets_size, sizeof(void *));
- if (size < data_size || size < offsets_size) {
+ if (data_offsets_size < data_size || data_offsets_size < offsets_size) {
binder_user_error("%d: got transaction with invalid size %zd-%zd\n",
proc->pid, data_size, offsets_size);
return NULL;
}
-
+ size = data_offsets_size + ALIGN(extra_buffers_size, sizeof(void *));
+ if (size < data_offsets_size || size < extra_buffers_size) {
+ binder_user_error("%d: got transaction with invalid extra_buffers_size %zd\n",
+ proc->pid, extra_buffers_size);
+ return NULL;
+ }
if (is_async &&
proc->free_async_space < size + sizeof(struct binder_buffer)) {
binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
@@ -807,6 +842,7 @@
proc->pid, size, buffer);
buffer->data_size = data_size;
buffer->offsets_size = offsets_size;
+ buffer->extra_buffers_size = extra_buffers_size;
buffer->async_transaction = is_async;
if (is_async) {
proc->free_async_space -= size + sizeof(struct binder_buffer);
@@ -881,7 +917,8 @@
buffer_size = binder_buffer_size(proc, buffer);
size = ALIGN(buffer->data_size, sizeof(void *)) +
- ALIGN(buffer->offsets_size, sizeof(void *));
+ ALIGN(buffer->offsets_size, sizeof(void *)) +
+ ALIGN(buffer->extra_buffers_size, sizeof(void *));
binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
"%d: binder_free_buf %pK size %zd buffer_size %zd\n",
@@ -993,8 +1030,10 @@
if (internal) {
if (target_list == NULL &&
node->internal_strong_refs == 0 &&
- !(node == binder_context_mgr_node &&
- node->has_strong_ref)) {
+ !(node->proc &&
+ node == node->proc->context->
+ binder_context_mgr_node &&
+ node->has_strong_ref)) {
pr_err("invalid inc strong node for %d\n",
node->debug_id);
return -EINVAL;
@@ -1095,6 +1134,7 @@
struct rb_node **p = &proc->refs_by_node.rb_node;
struct rb_node *parent = NULL;
struct binder_ref *ref, *new_ref;
+ struct binder_context *context = proc->context;
while (*p) {
parent = *p;
@@ -1117,7 +1157,7 @@
rb_link_node(&new_ref->rb_node_node, parent, p);
rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node);
- new_ref->desc = (node == binder_context_mgr_node) ? 0 : 1;
+ new_ref->desc = (node == context->binder_context_mgr_node) ? 0 : 1;
for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) {
ref = rb_entry(n, struct binder_ref, rb_node_desc);
if (ref->desc > new_ref->desc)
@@ -1303,11 +1343,158 @@
}
}
+/**
+ * binder_validate_object() - checks for a valid metadata object in a buffer.
+ * @buffer: binder_buffer that we're parsing.
+ * @offset: offset in the buffer at which to validate an object.
+ *
+ * Return: If there's a valid metadata object at @offset in @buffer, the
+ * size of that object. Otherwise, it returns zero.
+ */
+static size_t binder_validate_object(struct binder_buffer *buffer, u64 offset)
+{
+ /* Check if we can read a header first */
+ struct binder_object_header *hdr;
+ size_t object_size = 0;
+
+ if (offset > buffer->data_size - sizeof(*hdr) ||
+ buffer->data_size < sizeof(*hdr) ||
+ !IS_ALIGNED(offset, sizeof(u32)))
+ return 0;
+
+ /* Ok, now see if we can read a complete object. */
+ hdr = (struct binder_object_header *)(buffer->data + offset);
+ switch (hdr->type) {
+ case BINDER_TYPE_BINDER:
+ case BINDER_TYPE_WEAK_BINDER:
+ case BINDER_TYPE_HANDLE:
+ case BINDER_TYPE_WEAK_HANDLE:
+ object_size = sizeof(struct flat_binder_object);
+ break;
+ case BINDER_TYPE_FD:
+ object_size = sizeof(struct binder_fd_object);
+ break;
+ case BINDER_TYPE_PTR:
+ object_size = sizeof(struct binder_buffer_object);
+ break;
+ case BINDER_TYPE_FDA:
+ object_size = sizeof(struct binder_fd_array_object);
+ break;
+ default:
+ return 0;
+ }
+ if (offset <= buffer->data_size - object_size &&
+ buffer->data_size >= object_size)
+ return object_size;
+ else
+ return 0;
+}
+
+/**
+ * binder_validate_ptr() - validates binder_buffer_object in a binder_buffer.
+ * @b: binder_buffer containing the object
+ * @index: index in offset array at which the binder_buffer_object is
+ * located
+ * @start: points to the start of the offset array
+ * @num_valid: the number of valid offsets in the offset array
+ *
+ * Return: If @index is within the valid range of the offset array
+ * described by @start and @num_valid, and if there's a valid
+ * binder_buffer_object at the offset found in index @index
+ * of the offset array, that object is returned. Otherwise,
+ * %NULL is returned.
+ * Note that the offset found in index @index itself is not
+ * verified; this function assumes that @num_valid elements
+ * from @start were previously verified to have valid offsets.
+ */
+static struct binder_buffer_object *binder_validate_ptr(struct binder_buffer *b,
+ binder_size_t index,
+ binder_size_t *start,
+ binder_size_t num_valid)
+{
+ struct binder_buffer_object *buffer_obj;
+ binder_size_t *offp;
+
+ if (index >= num_valid)
+ return NULL;
+
+ offp = start + index;
+ buffer_obj = (struct binder_buffer_object *)(b->data + *offp);
+ if (buffer_obj->hdr.type != BINDER_TYPE_PTR)
+ return NULL;
+
+ return buffer_obj;
+}
+
+/**
+ * binder_validate_fixup() - validates pointer/fd fixups happen in order.
+ * @b: transaction buffer
+ * @objects_start start of objects buffer
+ * @buffer: binder_buffer_object in which to fix up
+ * @offset: start offset in @buffer to fix up
+ * @last_obj: last binder_buffer_object that we fixed up in
+ * @last_min_offset: minimum fixup offset in @last_obj
+ *
+ * Return: %true if a fixup in buffer @buffer at offset @offset is
+ * allowed.
+ *
+ * For safety reasons, we only allow fixups inside a buffer to happen
+ * at increasing offsets; additionally, we only allow fixup on the last
+ * buffer object that was verified, or one of its parents.
+ *
+ * Example of what is allowed:
+ *
+ * A
+ * B (parent = A, offset = 0)
+ * C (parent = A, offset = 16)
+ * D (parent = C, offset = 0)
+ * E (parent = A, offset = 32) // min_offset is 16 (C.parent_offset)
+ *
+ * Examples of what is not allowed:
+ *
+ * Decreasing offsets within the same parent:
+ * A
+ * C (parent = A, offset = 16)
+ * B (parent = A, offset = 0) // decreasing offset within A
+ *
+ * Referring to a parent that wasn't the last object or any of its parents:
+ * A
+ * B (parent = A, offset = 0)
+ * C (parent = A, offset = 0)
+ * C (parent = A, offset = 16)
+ * D (parent = B, offset = 0) // B is not A or any of A's parents
+ */
+static bool binder_validate_fixup(struct binder_buffer *b,
+ binder_size_t *objects_start,
+ struct binder_buffer_object *buffer,
+ binder_size_t fixup_offset,
+ struct binder_buffer_object *last_obj,
+ binder_size_t last_min_offset)
+{
+ if (!last_obj) {
+ /* Nothing to fix up in */
+ return false;
+ }
+
+ while (last_obj != buffer) {
+ /*
+ * Safe to retrieve the parent of last_obj, since it
+ * was already previously verified by the driver.
+ */
+ if ((last_obj->flags & BINDER_BUFFER_FLAG_HAS_PARENT) == 0)
+ return false;
+ last_min_offset = last_obj->parent_offset + sizeof(uintptr_t);
+ last_obj = (struct binder_buffer_object *)
+ (b->data + *(objects_start + last_obj->parent));
+ }
+ return (fixup_offset >= last_min_offset);
+}
+
static void binder_transaction_buffer_release(struct binder_proc *proc,
struct binder_buffer *buffer,
binder_size_t *failed_at)
{
- binder_size_t *offp, *off_end;
+ binder_size_t *offp, *off_start, *off_end;
int debug_id = buffer->debug_id;
binder_debug(BINDER_DEBUG_TRANSACTION,
@@ -1318,26 +1505,30 @@
if (buffer->target_node)
binder_dec_node(buffer->target_node, 1, 0);
- offp = (binder_size_t *)(buffer->data +
- ALIGN(buffer->data_size, sizeof(void *)));
+ off_start = (binder_size_t *)(buffer->data +
+ ALIGN(buffer->data_size, sizeof(void *)));
if (failed_at)
off_end = failed_at;
else
- off_end = (void *)offp + buffer->offsets_size;
- for (; offp < off_end; offp++) {
- struct flat_binder_object *fp;
- if (*offp > buffer->data_size - sizeof(*fp) ||
- buffer->data_size < sizeof(*fp) ||
- !IS_ALIGNED(*offp, sizeof(u32))) {
- pr_err("transaction release %d bad offset %lld, size %zd\n",
+ off_end = (void *)off_start + buffer->offsets_size;
+ for (offp = off_start; offp < off_end; offp++) {
+ struct binder_object_header *hdr;
+ size_t object_size = binder_validate_object(buffer, *offp);
+
+ if (object_size == 0) {
+ pr_err("transaction release %d bad object at offset %lld, size %zd\n",
debug_id, (u64)*offp, buffer->data_size);
continue;
}
- fp = (struct flat_binder_object *)(buffer->data + *offp);
- switch (fp->type) {
+ hdr = (struct binder_object_header *)(buffer->data + *offp);
+ switch (hdr->type) {
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
- struct binder_node *node = binder_get_node(proc, fp->binder);
+ struct flat_binder_object *fp;
+ struct binder_node *node;
+
+ fp = to_flat_binder_object(hdr);
+ node = binder_get_node(proc, fp->binder);
if (node == NULL) {
pr_err("transaction release %d bad node %016llx\n",
debug_id, (u64)fp->binder);
@@ -1346,12 +1537,17 @@
binder_debug(BINDER_DEBUG_TRANSACTION,
" node %d u%016llx\n",
node->debug_id, (u64)node->ptr);
- binder_dec_node(node, fp->type == BINDER_TYPE_BINDER, 0);
+ binder_dec_node(node, hdr->type == BINDER_TYPE_BINDER,
+ 0);
} break;
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: {
- struct binder_ref *ref = binder_get_ref(proc, fp->handle,
- fp->type == BINDER_TYPE_HANDLE);
+ struct flat_binder_object *fp;
+ struct binder_ref *ref;
+
+ fp = to_flat_binder_object(hdr);
+ ref = binder_get_ref(proc, fp->handle,
+ hdr->type == BINDER_TYPE_HANDLE);
if (ref == NULL) {
pr_err("transaction release %d bad handle %d\n",
debug_id, fp->handle);
@@ -1360,32 +1556,348 @@
binder_debug(BINDER_DEBUG_TRANSACTION,
" ref %d desc %d (node %d)\n",
ref->debug_id, ref->desc, ref->node->debug_id);
- binder_dec_ref(&ref, fp->type == BINDER_TYPE_HANDLE);
+ binder_dec_ref(&ref, hdr->type == BINDER_TYPE_HANDLE);
} break;
- case BINDER_TYPE_FD:
- binder_debug(BINDER_DEBUG_TRANSACTION,
- " fd %d\n", fp->handle);
- if (failed_at)
- task_close_fd(proc, fp->handle);
- break;
+ case BINDER_TYPE_FD: {
+ struct binder_fd_object *fp = to_binder_fd_object(hdr);
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+ " fd %d\n", fp->fd);
+ if (failed_at)
+ task_close_fd(proc, fp->fd);
+ } break;
+ case BINDER_TYPE_PTR:
+ /*
+ * Nothing to do here, this will get cleaned up when the
+ * transaction buffer gets freed
+ */
+ break;
+ case BINDER_TYPE_FDA: {
+ struct binder_fd_array_object *fda;
+ struct binder_buffer_object *parent;
+ uintptr_t parent_buffer;
+ u32 *fd_array;
+ size_t fd_index;
+ binder_size_t fd_buf_size;
+
+ fda = to_binder_fd_array_object(hdr);
+ parent = binder_validate_ptr(buffer, fda->parent,
+ off_start,
+ offp - off_start);
+ if (!parent) {
+ pr_err("transaction release %d bad parent offset",
+ debug_id);
+ continue;
+ }
+ /*
+ * Since the parent was already fixed up, convert it
+ * back to kernel address space to access it
+ */
+ parent_buffer = parent->buffer -
+ proc->user_buffer_offset;
+
+ fd_buf_size = sizeof(u32) * fda->num_fds;
+ if (fda->num_fds >= SIZE_MAX / sizeof(u32)) {
+ pr_err("transaction release %d invalid number of fds (%lld)\n",
+ debug_id, (u64)fda->num_fds);
+ continue;
+ }
+ if (fd_buf_size > parent->length ||
+ fda->parent_offset > parent->length - fd_buf_size) {
+ /* No space for all file descriptors here. */
+ pr_err("transaction release %d not enough space for %lld fds in buffer\n",
+ debug_id, (u64)fda->num_fds);
+ continue;
+ }
+ fd_array = (u32 *)(parent_buffer + fda->parent_offset);
+ for (fd_index = 0; fd_index < fda->num_fds; fd_index++)
+ task_close_fd(proc, fd_array[fd_index]);
+ } break;
default:
pr_err("transaction release %d bad object type %x\n",
- debug_id, fp->type);
+ debug_id, hdr->type);
break;
}
}
}
+static int binder_translate_binder(struct flat_binder_object *fp,
+ struct binder_transaction *t,
+ struct binder_thread *thread)
+{
+ struct binder_node *node;
+ struct binder_ref *ref;
+ struct binder_proc *proc = thread->proc;
+ struct binder_proc *target_proc = t->to_proc;
+
+ node = binder_get_node(proc, fp->binder);
+ if (!node) {
+ node = binder_new_node(proc, fp->binder, fp->cookie);
+ if (!node)
+ return -ENOMEM;
+
+ node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
+ node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
+ }
+ if (fp->cookie != node->cookie) {
+ binder_user_error("%d:%d sending u%016llx node %d, cookie mismatch %016llx != %016llx\n",
+ proc->pid, thread->pid, (u64)fp->binder,
+ node->debug_id, (u64)fp->cookie,
+ (u64)node->cookie);
+ return -EINVAL;
+ }
+ if (security_binder_transfer_binder(proc->tsk, target_proc->tsk))
+ return -EPERM;
+
+ ref = binder_get_ref_for_node(target_proc, node);
+ if (!ref)
+ return -EINVAL;
+
+ if (fp->hdr.type == BINDER_TYPE_BINDER)
+ fp->hdr.type = BINDER_TYPE_HANDLE;
+ else
+ fp->hdr.type = BINDER_TYPE_WEAK_HANDLE;
+ fp->binder = 0;
+ fp->handle = ref->desc;
+ fp->cookie = 0;
+ binder_inc_ref(ref, fp->hdr.type == BINDER_TYPE_HANDLE, &thread->todo);
+
+ trace_binder_transaction_node_to_ref(t, node, ref);
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+ " node %d u%016llx -> ref %d desc %d\n",
+ node->debug_id, (u64)node->ptr,
+ ref->debug_id, ref->desc);
+
+ return 0;
+}
+
+static int binder_translate_handle(struct flat_binder_object *fp,
+ struct binder_transaction *t,
+ struct binder_thread *thread)
+{
+ struct binder_ref *ref;
+ struct binder_proc *proc = thread->proc;
+ struct binder_proc *target_proc = t->to_proc;
+
+ ref = binder_get_ref(proc, fp->handle,
+ fp->hdr.type == BINDER_TYPE_HANDLE);
+ if (!ref) {
+ binder_user_error("%d:%d got transaction with invalid handle, %d\n",
+ proc->pid, thread->pid, fp->handle);
+ return -EINVAL;
+ }
+ if (security_binder_transfer_binder(proc->tsk, target_proc->tsk))
+ return -EPERM;
+
+ if (ref->node->proc == target_proc) {
+ if (fp->hdr.type == BINDER_TYPE_HANDLE)
+ fp->hdr.type = BINDER_TYPE_BINDER;
+ else
+ fp->hdr.type = BINDER_TYPE_WEAK_BINDER;
+ fp->binder = ref->node->ptr;
+ fp->cookie = ref->node->cookie;
+ binder_inc_node(ref->node, fp->hdr.type == BINDER_TYPE_BINDER,
+ 0, NULL);
+ trace_binder_transaction_ref_to_node(t, ref);
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+ " ref %d desc %d -> node %d u%016llx\n",
+ ref->debug_id, ref->desc, ref->node->debug_id,
+ (u64)ref->node->ptr);
+ } else {
+ struct binder_ref *new_ref;
+
+ new_ref = binder_get_ref_for_node(target_proc, ref->node);
+ if (!new_ref)
+ return -EINVAL;
+
+ fp->binder = 0;
+ fp->handle = new_ref->desc;
+ fp->cookie = 0;
+ binder_inc_ref(new_ref, fp->hdr.type == BINDER_TYPE_HANDLE,
+ NULL);
+ trace_binder_transaction_ref_to_ref(t, ref, new_ref);
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+ " ref %d desc %d -> ref %d desc %d (node %d)\n",
+ ref->debug_id, ref->desc, new_ref->debug_id,
+ new_ref->desc, ref->node->debug_id);
+ }
+ return 0;
+}
+
+static int binder_translate_fd(int fd,
+ struct binder_transaction *t,
+ struct binder_thread *thread,
+ struct binder_transaction *in_reply_to)
+{
+ struct binder_proc *proc = thread->proc;
+ struct binder_proc *target_proc = t->to_proc;
+ int target_fd;
+ struct file *file;
+ int ret;
+ bool target_allows_fd;
+
+ if (in_reply_to)
+ target_allows_fd = !!(in_reply_to->flags & TF_ACCEPT_FDS);
+ else
+ target_allows_fd = t->buffer->target_node->accept_fds;
+ if (!target_allows_fd) {
+ binder_user_error("%d:%d got %s with fd, %d, but target does not allow fds\n",
+ proc->pid, thread->pid,
+ in_reply_to ? "reply" : "transaction",
+ fd);
+ ret = -EPERM;
+ goto err_fd_not_accepted;
+ }
+
+ file = fget(fd);
+ if (!file) {
+ binder_user_error("%d:%d got transaction with invalid fd, %d\n",
+ proc->pid, thread->pid, fd);
+ ret = -EBADF;
+ goto err_fget;
+ }
+ ret = security_binder_transfer_file(proc->tsk, target_proc->tsk, file);
+ if (ret < 0) {
+ ret = -EPERM;
+ goto err_security;
+ }
+
+ target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC);
+ if (target_fd < 0) {
+ ret = -ENOMEM;
+ goto err_get_unused_fd;
+ }
+ task_fd_install(target_proc, target_fd, file);
+ trace_binder_transaction_fd(t, fd, target_fd);
+ binder_debug(BINDER_DEBUG_TRANSACTION, " fd %d -> %d\n",
+ fd, target_fd);
+
+ return target_fd;
+
+err_get_unused_fd:
+err_security:
+ fput(file);
+err_fget:
+err_fd_not_accepted:
+ return ret;
+}
+
+static int binder_translate_fd_array(struct binder_fd_array_object *fda,
+ struct binder_buffer_object *parent,
+ struct binder_transaction *t,
+ struct binder_thread *thread,
+ struct binder_transaction *in_reply_to)
+{
+ binder_size_t fdi, fd_buf_size, num_installed_fds;
+ int target_fd;
+ uintptr_t parent_buffer;
+ u32 *fd_array;
+ struct binder_proc *proc = thread->proc;
+ struct binder_proc *target_proc = t->to_proc;
+
+ fd_buf_size = sizeof(u32) * fda->num_fds;
+ if (fda->num_fds >= SIZE_MAX / sizeof(u32)) {
+ binder_user_error("%d:%d got transaction with invalid number of fds (%lld)\n",
+ proc->pid, thread->pid, (u64)fda->num_fds);
+ return -EINVAL;
+ }
+ if (fd_buf_size > parent->length ||
+ fda->parent_offset > parent->length - fd_buf_size) {
+ /* No space for all file descriptors here. */
+ binder_user_error("%d:%d not enough space to store %lld fds in buffer\n",
+ proc->pid, thread->pid, (u64)fda->num_fds);
+ return -EINVAL;
+ }
+ /*
+ * Since the parent was already fixed up, convert it
+ * back to the kernel address space to access it
+ */
+ parent_buffer = parent->buffer - target_proc->user_buffer_offset;
+ fd_array = (u32 *)(parent_buffer + fda->parent_offset);
+ if (!IS_ALIGNED((unsigned long)fd_array, sizeof(u32))) {
+ binder_user_error("%d:%d parent offset not aligned correctly.\n",
+ proc->pid, thread->pid);
+ return -EINVAL;
+ }
+ for (fdi = 0; fdi < fda->num_fds; fdi++) {
+ target_fd = binder_translate_fd(fd_array[fdi], t, thread,
+ in_reply_to);
+ if (target_fd < 0)
+ goto err_translate_fd_failed;
+ fd_array[fdi] = target_fd;
+ }
+ return 0;
+
+err_translate_fd_failed:
+ /*
+ * Failed to allocate fd or security error, free fds
+ * installed so far.
+ */
+ num_installed_fds = fdi;
+ for (fdi = 0; fdi < num_installed_fds; fdi++)
+ task_close_fd(target_proc, fd_array[fdi]);
+ return target_fd;
+}
+
+static int binder_fixup_parent(struct binder_transaction *t,
+ struct binder_thread *thread,
+ struct binder_buffer_object *bp,
+ binder_size_t *off_start,
+ binder_size_t num_valid,
+ struct binder_buffer_object *last_fixup_obj,
+ binder_size_t last_fixup_min_off)
+{
+ struct binder_buffer_object *parent;
+ u8 *parent_buffer;
+ struct binder_buffer *b = t->buffer;
+ struct binder_proc *proc = thread->proc;
+ struct binder_proc *target_proc = t->to_proc;
+
+ if (!(bp->flags & BINDER_BUFFER_FLAG_HAS_PARENT))
+ return 0;
+
+ parent = binder_validate_ptr(b, bp->parent, off_start, num_valid);
+ if (!parent) {
+ binder_user_error("%d:%d got transaction with invalid parent offset or type\n",
+ proc->pid, thread->pid);
+ return -EINVAL;
+ }
+
+ if (!binder_validate_fixup(b, off_start,
+ parent, bp->parent_offset,
+ last_fixup_obj,
+ last_fixup_min_off)) {
+ binder_user_error("%d:%d got transaction with out-of-order buffer fixup\n",
+ proc->pid, thread->pid);
+ return -EINVAL;
+ }
+
+ if (parent->length < sizeof(binder_uintptr_t) ||
+ bp->parent_offset > parent->length - sizeof(binder_uintptr_t)) {
+ /* No space for a pointer here! */
+ binder_user_error("%d:%d got transaction with invalid parent offset\n",
+ proc->pid, thread->pid);
+ return -EINVAL;
+ }
+ parent_buffer = (u8 *)(parent->buffer -
+ target_proc->user_buffer_offset);
+ *(binder_uintptr_t *)(parent_buffer + bp->parent_offset) = bp->buffer;
+
+ return 0;
+}
+
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
- struct binder_transaction_data *tr, int reply)
+ struct binder_transaction_data *tr, int reply,
+ binder_size_t extra_buffers_size)
{
+ int ret;
struct binder_transaction *t;
struct binder_work *tcomplete;
- binder_size_t *offp, *off_end;
+ binder_size_t *offp, *off_end, *off_start;
binder_size_t off_min;
+ u8 *sg_bufp, *sg_buf_end;
struct binder_proc *target_proc;
struct binder_thread *target_thread = NULL;
struct binder_node *target_node = NULL;
@@ -1394,6 +1906,9 @@
struct binder_transaction *in_reply_to = NULL;
struct binder_transaction_log_entry *e;
uint32_t return_error;
+ struct binder_buffer_object *last_fixup_obj = NULL;
+ binder_size_t last_fixup_min_off = 0;
+ struct binder_context *context = proc->context;
e = binder_transaction_log_add(&binder_transaction_log);
e->call_type = reply ? 2 : !!(tr->flags & TF_ONE_WAY);
@@ -1402,6 +1917,7 @@
e->target_handle = tr->target.handle;
e->data_size = tr->data_size;
e->offsets_size = tr->offsets_size;
+ e->context_name = proc->context->name;
if (reply) {
in_reply_to = thread->transaction_stack;
@@ -1453,7 +1969,7 @@
}
target_node = ref->node;
} else {
- target_node = binder_context_mgr_node;
+ target_node = context->binder_context_mgr_node;
if (target_node == NULL) {
return_error = BR_DEAD_REPLY;
goto err_no_context_mgr_node;
@@ -1518,20 +2034,22 @@
if (reply)
binder_debug(BINDER_DEBUG_TRANSACTION,
- "%d:%d BC_REPLY %d -> %d:%d, data %016llx-%016llx size %lld-%lld\n",
+ "%d:%d BC_REPLY %d -> %d:%d, data %016llx-%016llx size %lld-%lld-%lld\n",
proc->pid, thread->pid, t->debug_id,
target_proc->pid, target_thread->pid,
(u64)tr->data.ptr.buffer,
(u64)tr->data.ptr.offsets,
- (u64)tr->data_size, (u64)tr->offsets_size);
+ (u64)tr->data_size, (u64)tr->offsets_size,
+ (u64)extra_buffers_size);
else
binder_debug(BINDER_DEBUG_TRANSACTION,
- "%d:%d BC_TRANSACTION %d -> %d - node %d, data %016llx-%016llx size %lld-%lld\n",
+ "%d:%d BC_TRANSACTION %d -> %d - node %d, data %016llx-%016llx size %lld-%lld-%lld\n",
proc->pid, thread->pid, t->debug_id,
target_proc->pid, target_node->debug_id,
(u64)tr->data.ptr.buffer,
(u64)tr->data.ptr.offsets,
- (u64)tr->data_size, (u64)tr->offsets_size);
+ (u64)tr->data_size, (u64)tr->offsets_size,
+ (u64)extra_buffers_size);
if (!reply && !(tr->flags & TF_ONE_WAY))
t->from = thread;
@@ -1547,7 +2065,8 @@
trace_binder_transaction(reply, t, target_node);
t->buffer = binder_alloc_buf(target_proc, tr->data_size,
- tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
+ tr->offsets_size, extra_buffers_size,
+ !reply && (t->flags & TF_ONE_WAY));
if (t->buffer == NULL) {
return_error = BR_FAILED_REPLY;
goto err_binder_alloc_buf_failed;
@@ -1560,8 +2079,9 @@
if (target_node)
binder_inc_node(target_node, 1, 0, NULL);
- offp = (binder_size_t *)(t->buffer->data +
- ALIGN(tr->data_size, sizeof(void *)));
+ off_start = (binder_size_t *)(t->buffer->data +
+ ALIGN(tr->data_size, sizeof(void *)));
+ offp = off_start;
if (copy_from_user_preempt_disabled(t->buffer->data, (const void __user *)(uintptr_t)
tr->data.ptr.buffer, tr->data_size)) {
@@ -1583,166 +2103,139 @@
return_error = BR_FAILED_REPLY;
goto err_bad_offset;
}
- off_end = (void *)offp + tr->offsets_size;
+ if (!IS_ALIGNED(extra_buffers_size, sizeof(u64))) {
+ binder_user_error("%d:%d got transaction with unaligned buffers size, %lld\n",
+ proc->pid, thread->pid,
+ extra_buffers_size);
+ return_error = BR_FAILED_REPLY;
+ goto err_bad_offset;
+ }
+ off_end = (void *)off_start + tr->offsets_size;
+ sg_bufp = (u8 *)(PTR_ALIGN(off_end, sizeof(void *)));
+ sg_buf_end = sg_bufp + extra_buffers_size;
off_min = 0;
for (; offp < off_end; offp++) {
- struct flat_binder_object *fp;
- if (*offp > t->buffer->data_size - sizeof(*fp) ||
- *offp < off_min ||
- t->buffer->data_size < sizeof(*fp) ||
- !IS_ALIGNED(*offp, sizeof(u32))) {
- binder_user_error("%d:%d got transaction with invalid offset, %lld (min %lld, max %lld)\n",
+ struct binder_object_header *hdr;
+ size_t object_size = binder_validate_object(t->buffer, *offp);
+
+ if (object_size == 0 || *offp < off_min) {
+ binder_user_error("%d:%d got transaction with invalid offset (%lld, min %lld max %lld) or object.\n",
proc->pid, thread->pid, (u64)*offp,
(u64)off_min,
- (u64)(t->buffer->data_size -
- sizeof(*fp)));
+ (u64)t->buffer->data_size);
return_error = BR_FAILED_REPLY;
goto err_bad_offset;
}
- fp = (struct flat_binder_object *)(t->buffer->data + *offp);
- off_min = *offp + sizeof(struct flat_binder_object);
- switch (fp->type) {
+
+ hdr = (struct binder_object_header *)(t->buffer->data + *offp);
+ off_min = *offp + object_size;
+ switch (hdr->type) {
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
- struct binder_ref *ref;
- struct binder_node *node = binder_get_node(proc, fp->binder);
- if (node == NULL) {
- node = binder_new_node(proc, fp->binder, fp->cookie);
- if (node == NULL) {
- return_error = BR_FAILED_REPLY;
- goto err_binder_new_node_failed;
- }
- node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
- node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
- }
- if (fp->cookie != node->cookie) {
- binder_user_error("%d:%d sending u%016llx node %d, cookie mismatch %016llx != %016llx\n",
- proc->pid, thread->pid,
- (u64)fp->binder, node->debug_id,
- (u64)fp->cookie, (u64)node->cookie);
- goto err_binder_get_ref_for_node_failed;
- }
- if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
- return_error = BR_FAILED_REPLY;
- goto err_binder_get_ref_for_node_failed;
- }
- ref = binder_get_ref_for_node(target_proc, node);
- if (ref == NULL) {
- return_error = BR_FAILED_REPLY;
- goto err_binder_get_ref_for_node_failed;
- }
- if (fp->type == BINDER_TYPE_BINDER)
- fp->type = BINDER_TYPE_HANDLE;
- else
- fp->type = BINDER_TYPE_WEAK_HANDLE;
- fp->binder = 0;
- fp->handle = ref->desc;
- fp->cookie = 0;
- binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,
- &thread->todo);
+ struct flat_binder_object *fp;
- trace_binder_transaction_node_to_ref(t, node, ref);
- binder_debug(BINDER_DEBUG_TRANSACTION,
- " node %d u%016llx -> ref %d desc %d\n",
- node->debug_id, (u64)node->ptr,
- ref->debug_id, ref->desc);
+ fp = to_flat_binder_object(hdr);
+ ret = binder_translate_binder(fp, t, thread);
+ if (ret < 0) {
+ return_error = BR_FAILED_REPLY;
+ goto err_translate_failed;
+ }
} break;
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: {
- struct binder_ref *ref = binder_get_ref(proc, fp->handle,
- fp->type == BINDER_TYPE_HANDLE);
- if (ref == NULL) {
- binder_user_error("%d:%d got transaction with invalid handle, %d\n",
- proc->pid,
- thread->pid, fp->handle);
+ struct flat_binder_object *fp;
+
+ fp = to_flat_binder_object(hdr);
+ ret = binder_translate_handle(fp, t, thread);
+ if (ret < 0) {
return_error = BR_FAILED_REPLY;
- goto err_binder_get_ref_failed;
- }
- if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
- return_error = BR_FAILED_REPLY;
- goto err_binder_get_ref_failed;
- }
- if (ref->node->proc == target_proc) {
- if (fp->type == BINDER_TYPE_HANDLE)
- fp->type = BINDER_TYPE_BINDER;
- else
- fp->type = BINDER_TYPE_WEAK_BINDER;
- fp->binder = ref->node->ptr;
- fp->cookie = ref->node->cookie;
- binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL);
- trace_binder_transaction_ref_to_node(t, ref);
- binder_debug(BINDER_DEBUG_TRANSACTION,
- " ref %d desc %d -> node %d u%016llx\n",
- ref->debug_id, ref->desc, ref->node->debug_id,
- (u64)ref->node->ptr);
- } else {
- struct binder_ref *new_ref;
- new_ref = binder_get_ref_for_node(target_proc, ref->node);
- if (new_ref == NULL) {
- return_error = BR_FAILED_REPLY;
- goto err_binder_get_ref_for_node_failed;
- }
- fp->binder = 0;
- fp->handle = new_ref->desc;
- fp->cookie = 0;
- binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL);
- trace_binder_transaction_ref_to_ref(t, ref,
- new_ref);
- binder_debug(BINDER_DEBUG_TRANSACTION,
- " ref %d desc %d -> ref %d desc %d (node %d)\n",
- ref->debug_id, ref->desc, new_ref->debug_id,
- new_ref->desc, ref->node->debug_id);
+ goto err_translate_failed;
}
} break;
case BINDER_TYPE_FD: {
- int target_fd;
- struct file *file;
+ struct binder_fd_object *fp = to_binder_fd_object(hdr);
+ int target_fd = binder_translate_fd(fp->fd, t, thread,
+ in_reply_to);
- if (reply) {
- if (!(in_reply_to->flags & TF_ACCEPT_FDS)) {
- binder_user_error("%d:%d got reply with fd, %d, but target does not allow fds\n",
- proc->pid, thread->pid, fp->handle);
- return_error = BR_FAILED_REPLY;
- goto err_fd_not_allowed;
- }
- } else if (!target_node->accept_fds) {
- binder_user_error("%d:%d got transaction with fd, %d, but target does not allow fds\n",
- proc->pid, thread->pid, fp->handle);
- return_error = BR_FAILED_REPLY;
- goto err_fd_not_allowed;
- }
-
- file = fget(fp->handle);
- if (file == NULL) {
- binder_user_error("%d:%d got transaction with invalid fd, %d\n",
- proc->pid, thread->pid, fp->handle);
- return_error = BR_FAILED_REPLY;
- goto err_fget_failed;
- }
- if (security_binder_transfer_file(proc->tsk, target_proc->tsk, file) < 0) {
- fput(file);
- return_error = BR_FAILED_REPLY;
- goto err_get_unused_fd_failed;
- }
- target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC);
if (target_fd < 0) {
- fput(file);
return_error = BR_FAILED_REPLY;
- goto err_get_unused_fd_failed;
+ goto err_translate_failed;
}
- task_fd_install(target_proc, target_fd, file);
- trace_binder_transaction_fd(t, fp->handle, target_fd);
- binder_debug(BINDER_DEBUG_TRANSACTION,
- " fd %d -> %d\n", fp->handle, target_fd);
- /* TODO: fput? */
- fp->binder = 0;
- fp->handle = target_fd;
+ fp->pad_binder = 0;
+ fp->fd = target_fd;
} break;
+ case BINDER_TYPE_FDA: {
+ struct binder_fd_array_object *fda =
+ to_binder_fd_array_object(hdr);
+ struct binder_buffer_object *parent =
+ binder_validate_ptr(t->buffer, fda->parent,
+ off_start,
+ offp - off_start);
+ if (!parent) {
+ binder_user_error("%d:%d got transaction with invalid parent offset or type\n",
+ proc->pid, thread->pid);
+ return_error = BR_FAILED_REPLY;
+ goto err_bad_parent;
+ }
+ if (!binder_validate_fixup(t->buffer, off_start,
+ parent, fda->parent_offset,
+ last_fixup_obj,
+ last_fixup_min_off)) {
+ binder_user_error("%d:%d got transaction with out-of-order buffer fixup\n",
+ proc->pid, thread->pid);
+ return_error = BR_FAILED_REPLY;
+ goto err_bad_parent;
+ }
+ ret = binder_translate_fd_array(fda, parent, t, thread,
+ in_reply_to);
+ if (ret < 0) {
+ return_error = BR_FAILED_REPLY;
+ goto err_translate_failed;
+ }
+ last_fixup_obj = parent;
+ last_fixup_min_off =
+ fda->parent_offset + sizeof(u32) * fda->num_fds;
+ } break;
+ case BINDER_TYPE_PTR: {
+ struct binder_buffer_object *bp =
+ to_binder_buffer_object(hdr);
+ size_t buf_left = sg_buf_end - sg_bufp;
+ if (bp->length > buf_left) {
+ binder_user_error("%d:%d got transaction with too large buffer\n",
+ proc->pid, thread->pid);
+ return_error = BR_FAILED_REPLY;
+ goto err_bad_offset;
+ }
+ if (copy_from_user_preempt_disabled(
+ sg_bufp,
+ (const void __user *)(uintptr_t)
+ bp->buffer, bp->length)) {
+ binder_user_error("%d:%d got transaction with invalid offsets ptr\n",
+ proc->pid, thread->pid);
+ return_error = BR_FAILED_REPLY;
+ goto err_copy_data_failed;
+ }
+ /* Fixup buffer pointer to target proc address space */
+ bp->buffer = (uintptr_t)sg_bufp +
+ target_proc->user_buffer_offset;
+ sg_bufp += ALIGN(bp->length, sizeof(u64));
+
+ ret = binder_fixup_parent(t, thread, bp, off_start,
+ offp - off_start,
+ last_fixup_obj,
+ last_fixup_min_off);
+ if (ret < 0) {
+ return_error = BR_FAILED_REPLY;
+ goto err_translate_failed;
+ }
+ last_fixup_obj = bp;
+ last_fixup_min_off = 0;
+ } break;
default:
binder_user_error("%d:%d got transaction with invalid object type, %x\n",
- proc->pid, thread->pid, fp->type);
+ proc->pid, thread->pid, hdr->type);
return_error = BR_FAILED_REPLY;
goto err_bad_object_type;
}
@@ -1778,14 +2271,10 @@
}
return;
-err_get_unused_fd_failed:
-err_fget_failed:
-err_fd_not_allowed:
-err_binder_get_ref_for_node_failed:
-err_binder_get_ref_failed:
-err_binder_new_node_failed:
+err_translate_failed:
err_bad_object_type:
err_bad_offset:
+err_bad_parent:
err_copy_data_failed:
trace_binder_transaction_failed_buffer_release(t->buffer);
binder_transaction_buffer_release(target_proc, t->buffer, offp);
@@ -1828,6 +2317,7 @@
binder_size_t *consumed)
{
uint32_t cmd;
+ struct binder_context *context = proc->context;
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
@@ -1854,10 +2344,10 @@
if (get_user_preempt_disabled(target, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
- if (target == 0 && binder_context_mgr_node &&
+ if (target == 0 && context->binder_context_mgr_node &&
(cmd == BC_INCREFS || cmd == BC_ACQUIRE)) {
ref = binder_get_ref_for_node(proc,
- binder_context_mgr_node);
+ context->binder_context_mgr_node);
if (ref->desc != target) {
binder_user_error("%d:%d tried to acquire reference to desc 0, got %d instead\n",
proc->pid, thread->pid,
@@ -2008,6 +2498,18 @@
break;
}
+ case BC_TRANSACTION_SG:
+ case BC_REPLY_SG: {
+ struct binder_transaction_data_sg tr;
+
+ if (copy_from_user_preempt_disabled(&tr, ptr,
+ sizeof(tr)))
+ return -EFAULT;
+ ptr += sizeof(tr);
+ binder_transaction(proc, thread, &tr.transaction_data,
+ cmd == BC_REPLY_SG, tr.buffers_size);
+ break;
+ }
case BC_TRANSACTION:
case BC_REPLY: {
struct binder_transaction_data tr;
@@ -2015,7 +2517,8 @@
if (copy_from_user_preempt_disabled(&tr, ptr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
- binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
+ binder_transaction(proc, thread, &tr,
+ cmd == BC_REPLY, 0);
break;
}
@@ -2689,6 +3192,7 @@
{
int ret;
struct binder_proc *proc = filp->private_data;
+ struct binder_context *context = proc->context;
struct binder_thread *thread;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
@@ -2764,7 +3268,7 @@
}
break;
case BINDER_SET_CONTEXT_MGR:
- if (binder_context_mgr_node != NULL) {
+ if (context->binder_context_mgr_node) {
pr_err("BINDER_SET_CONTEXT_MGR already set\n");
ret = -EBUSY;
goto err;
@@ -2772,25 +3276,27 @@
ret = security_binder_set_context_mgr(proc->tsk);
if (ret < 0)
goto err;
- if (uid_valid(binder_context_mgr_uid)) {
- if (!uid_eq(binder_context_mgr_uid, current->cred->euid)) {
+ if (uid_valid(context->binder_context_mgr_uid)) {
+ if (!uid_eq(context->binder_context_mgr_uid,
+ current->cred->euid)) {
pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n",
from_kuid(&init_user_ns, current->cred->euid),
- from_kuid(&init_user_ns, binder_context_mgr_uid));
+ from_kuid(&init_user_ns,
+ context->binder_context_mgr_uid));
ret = -EPERM;
goto err;
}
} else
- binder_context_mgr_uid = current->cred->euid;
- binder_context_mgr_node = binder_new_node(proc, 0, 0);
- if (binder_context_mgr_node == NULL) {
+ context->binder_context_mgr_uid = current->cred->euid;
+ context->binder_context_mgr_node = binder_new_node(proc, 0, 0);
+ if (!context->binder_context_mgr_node) {
ret = -ENOMEM;
goto err;
}
- binder_context_mgr_node->local_weak_refs++;
- binder_context_mgr_node->local_strong_refs++;
- binder_context_mgr_node->has_strong_ref = 1;
- binder_context_mgr_node->has_weak_ref = 1;
+ context->binder_context_mgr_node->local_weak_refs++;
+ context->binder_context_mgr_node->local_strong_refs++;
+ context->binder_context_mgr_node->has_strong_ref = 1;
+ context->binder_context_mgr_node->has_weak_ref = 1;
break;
case BINDER_THREAD_EXIT:
binder_debug(BINDER_DEBUG_THREADS, "%d:%d exit\n",
@@ -2868,7 +3374,7 @@
const char *failure_string;
struct binder_buffer *buffer;
- if (proc->tsk != current)
+ if (proc->tsk != current->group_leader)
return -EINVAL;
if ((vma->vm_end - vma->vm_start) > SZ_4M)
@@ -2966,6 +3472,7 @@
static int binder_open(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc;
+ struct binder_device *binder_dev;
binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
current->group_leader->pid, current->pid);
@@ -2973,11 +3480,14 @@
proc = kzalloc(sizeof(*proc), GFP_KERNEL);
if (proc == NULL)
return -ENOMEM;
- get_task_struct(current);
- proc->tsk = current;
+ get_task_struct(current->group_leader);
+ proc->tsk = current->group_leader;
INIT_LIST_HEAD(&proc->todo);
init_waitqueue_head(&proc->wait);
proc->default_priority = task_nice(current);
+ binder_dev = container_of(filp->private_data, struct binder_device,
+ miscdev);
+ proc->context = &binder_dev->context;
binder_lock(__func__);
@@ -2992,8 +3502,17 @@
if (binder_debugfs_dir_entry_proc) {
char strbuf[11];
snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
+ /*
+ * proc debug entries are shared between contexts, so
+ * this will fail if the process tries to open the driver
+ * again with a different context. The priting code will
+ * anyway print all contexts that a given PID has, so this
+ * is not a problem.
+ */
proc->debugfs_entry = debugfs_create_file(strbuf, S_IRUGO,
- binder_debugfs_dir_entry_proc, proc, &binder_proc_fops);
+ binder_debugfs_dir_entry_proc,
+ (void *)(unsigned long)proc->pid,
+ &binder_proc_fops);
}
return 0;
@@ -3083,6 +3602,7 @@
static void binder_deferred_release(struct binder_proc *proc)
{
struct binder_transaction *t;
+ struct binder_context *context = proc->context;
struct rb_node *n;
int threads, nodes, incoming_refs, outgoing_refs, buffers,
active_transactions, page_count;
@@ -3092,11 +3612,12 @@
hlist_del(&proc->proc_node);
- if (binder_context_mgr_node && binder_context_mgr_node->proc == proc) {
+ if (context->binder_context_mgr_node &&
+ context->binder_context_mgr_node->proc == proc) {
binder_debug(BINDER_DEBUG_DEAD_BINDER,
"%s: %d context_mgr_node gone\n",
__func__, proc->pid);
- binder_context_mgr_node = NULL;
+ context->binder_context_mgr_node = NULL;
}
threads = 0;
@@ -3388,6 +3909,7 @@
size_t header_pos;
seq_printf(m, "proc %d\n", proc->pid);
+ seq_printf(m, "context %s\n", proc->context->name);
header_pos = m->count;
for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n))
@@ -3457,7 +3979,9 @@
"BC_EXIT_LOOPER",
"BC_REQUEST_DEATH_NOTIFICATION",
"BC_CLEAR_DEATH_NOTIFICATION",
- "BC_DEAD_BINDER_DONE"
+ "BC_DEAD_BINDER_DONE",
+ "BC_TRANSACTION_SG",
+ "BC_REPLY_SG",
};
static const char * const binder_objstat_strings[] = {
@@ -3512,6 +4036,7 @@
int count, strong, weak;
seq_printf(m, "proc %d\n", proc->pid);
+ seq_printf(m, "context %s\n", proc->context->name);
count = 0;
for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n))
count++;
@@ -3619,23 +4144,18 @@
static int binder_proc_show(struct seq_file *m, void *unused)
{
struct binder_proc *itr;
- struct binder_proc *proc = m->private;
+ int pid = (unsigned long)m->private;
int do_lock = !binder_debug_no_lock;
- bool valid_proc = false;
if (do_lock)
binder_lock(__func__);
hlist_for_each_entry(itr, &binder_procs, proc_node) {
- if (itr == proc) {
- valid_proc = true;
- break;
+ if (itr->pid == pid) {
+ seq_puts(m, "binder proc state:\n");
+ print_binder_proc(m, itr, 1);
}
}
- if (valid_proc) {
- seq_puts(m, "binder proc state:\n");
- print_binder_proc(m, proc, 1);
- }
if (do_lock)
binder_unlock(__func__);
return 0;
@@ -3645,11 +4165,11 @@
struct binder_transaction_log_entry *e)
{
seq_printf(m,
- "%d: %s from %d:%d to %d:%d node %d handle %d size %d:%d\n",
+ "%d: %s from %d:%d to %d:%d context %s node %d handle %d size %d:%d\n",
e->debug_id, (e->call_type == 2) ? "reply" :
((e->call_type == 1) ? "async" : "call "), e->from_proc,
- e->from_thread, e->to_proc, e->to_thread, e->to_node,
- e->target_handle, e->data_size, e->offsets_size);
+ e->from_thread, e->to_proc, e->to_thread, e->context_name,
+ e->to_node, e->target_handle, e->data_size, e->offsets_size);
}
static int binder_transaction_log_show(struct seq_file *m, void *unused)
@@ -3677,20 +4197,44 @@
.release = binder_release,
};
-static struct miscdevice binder_miscdev = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "binder",
- .fops = &binder_fops
-};
-
BINDER_DEBUG_ENTRY(state);
BINDER_DEBUG_ENTRY(stats);
BINDER_DEBUG_ENTRY(transactions);
BINDER_DEBUG_ENTRY(transaction_log);
+static int __init init_binder_device(const char *name)
+{
+ int ret;
+ struct binder_device *binder_device;
+
+ binder_device = kzalloc(sizeof(*binder_device), GFP_KERNEL);
+ if (!binder_device)
+ return -ENOMEM;
+
+ binder_device->miscdev.fops = &binder_fops;
+ binder_device->miscdev.minor = MISC_DYNAMIC_MINOR;
+ binder_device->miscdev.name = name;
+
+ binder_device->context.binder_context_mgr_uid = INVALID_UID;
+ binder_device->context.name = name;
+
+ ret = misc_register(&binder_device->miscdev);
+ if (ret < 0) {
+ kfree(binder_device);
+ return ret;
+ }
+
+ hlist_add_head(&binder_device->hlist, &binder_devices);
+
+ return ret;
+}
+
static int __init binder_init(void)
{
int ret;
+ char *device_name, *device_names;
+ struct binder_device *device;
+ struct hlist_node *tmp;
binder_deferred_workqueue = create_singlethread_workqueue("binder");
if (!binder_deferred_workqueue)
@@ -3700,7 +4244,7 @@
if (binder_debugfs_dir_entry_root)
binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
binder_debugfs_dir_entry_root);
- ret = misc_register(&binder_miscdev);
+
if (binder_debugfs_dir_entry_root) {
debugfs_create_file("state",
S_IRUGO,
@@ -3728,6 +4272,37 @@
&binder_transaction_log_failed,
&binder_transaction_log_fops);
}
+
+ /*
+ * Copy the module_parameter string, because we don't want to
+ * tokenize it in-place.
+ */
+ device_names = kzalloc(strlen(binder_devices_param) + 1, GFP_KERNEL);
+ if (!device_names) {
+ ret = -ENOMEM;
+ goto err_alloc_device_names_failed;
+ }
+ strcpy(device_names, binder_devices_param);
+
+ while ((device_name = strsep(&device_names, ","))) {
+ ret = init_binder_device(device_name);
+ if (ret)
+ goto err_init_binder_device_failed;
+ }
+
+ return ret;
+
+err_init_binder_device_failed:
+ hlist_for_each_entry_safe(device, tmp, &binder_devices, hlist) {
+ misc_deregister(&device->miscdev);
+ hlist_del(&device->hlist);
+ kfree(device);
+ }
+err_alloc_device_names_failed:
+ debugfs_remove_recursive(binder_debugfs_dir_entry_root);
+
+ destroy_workqueue(binder_deferred_workqueue);
+
return ret;
}
diff --git a/drivers/staging/android/uapi/binder.h b/drivers/staging/android/uapi/binder.h
index 04c735d..52cd3f4 100644
--- a/drivers/staging/android/uapi/binder.h
+++ b/drivers/staging/android/uapi/binder.h
@@ -32,6 +32,8 @@
BINDER_TYPE_HANDLE = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE),
BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),
BINDER_TYPE_FD = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE),
+ BINDER_TYPE_FDA = B_PACK_CHARS('f', 'd', 'a', B_TYPE_LARGE),
+ BINDER_TYPE_PTR = B_PACK_CHARS('p', 't', '*', B_TYPE_LARGE),
};
enum {
@@ -47,6 +49,14 @@
typedef __u64 binder_uintptr_t;
#endif
+/**
+ * struct binder_object_header - header shared by all binder metadata objects.
+ * @type: type of the object
+ */
+struct binder_object_header {
+ __u32 type;
+};
+
/*
* This is the flattened representation of a Binder object for transfer
* between processes. The 'offsets' supplied as part of a binder transaction
@@ -55,9 +65,8 @@
* between processes.
*/
struct flat_binder_object {
- /* 8 bytes for large_flat_header. */
- __u32 type;
- __u32 flags;
+ struct binder_object_header hdr;
+ __u32 flags;
/* 8 bytes of data. */
union {
@@ -69,6 +78,86 @@
binder_uintptr_t cookie;
};
+/**
+ * struct binder_fd_object - describes a filedescriptor to be fixed up.
+ * @hdr: common header structure
+ * @pad_flags: padding to remain compatible with old userspace code
+ * @pad_binder: padding to remain compatible with old userspace code
+ * @fd: file descriptor
+ * @cookie: opaque data, used by user-space
+ */
+struct binder_fd_object {
+ struct binder_object_header hdr;
+ __u32 pad_flags;
+ union {
+ binder_uintptr_t pad_binder;
+ __u32 fd;
+ };
+
+ binder_uintptr_t cookie;
+};
+
+/* struct binder_buffer_object - object describing a userspace buffer
+ * @hdr: common header structure
+ * @flags: one or more BINDER_BUFFER_* flags
+ * @buffer: address of the buffer
+ * @length: length of the buffer
+ * @parent: index in offset array pointing to parent buffer
+ * @parent_offset: offset in @parent pointing to this buffer
+ *
+ * A binder_buffer object represents an object that the
+ * binder kernel driver can copy verbatim to the target
+ * address space. A buffer itself may be pointed to from
+ * within another buffer, meaning that the pointer inside
+ * that other buffer needs to be fixed up as well. This
+ * can be done by setting the BINDER_BUFFER_FLAG_HAS_PARENT
+ * flag in @flags, by setting @parent buffer to the index
+ * in the offset array pointing to the parent binder_buffer_object,
+ * and by setting @parent_offset to the offset in the parent buffer
+ * at which the pointer to this buffer is located.
+ */
+struct binder_buffer_object {
+ struct binder_object_header hdr;
+ __u32 flags;
+ binder_uintptr_t buffer;
+ binder_size_t length;
+ binder_size_t parent;
+ binder_size_t parent_offset;
+};
+
+enum {
+ BINDER_BUFFER_FLAG_HAS_PARENT = 0x01,
+};
+
+/* struct binder_fd_array_object - object describing an array of fds in a buffer
+ * @hdr: common header structure
+ * @pad: padding to ensure correct alignment
+ * @num_fds: number of file descriptors in the buffer
+ * @parent: index in offset array to buffer holding the fd array
+ * @parent_offset: start offset of fd array in the buffer
+ *
+ * A binder_fd_array object represents an array of file
+ * descriptors embedded in a binder_buffer_object. It is
+ * different from a regular binder_buffer_object because it
+ * describes a list of file descriptors to fix up, not an opaque
+ * blob of memory, and hence the kernel needs to treat it differently.
+ *
+ * An example of how this would be used is with Android's
+ * native_handle_t object, which is a struct with a list of integers
+ * and a list of file descriptors. The native_handle_t struct itself
+ * will be represented by a struct binder_buffer_objct, whereas the
+ * embedded list of file descriptors is represented by a
+ * struct binder_fd_array_object with that binder_buffer_object as
+ * a parent.
+ */
+struct binder_fd_array_object {
+ struct binder_object_header hdr;
+ __u32 pad;
+ binder_size_t num_fds;
+ binder_size_t parent;
+ binder_size_t parent_offset;
+};
+
/*
* On 64-bit platforms where user code may run in 32-bits the driver must
* translate the buffer (and local binder) addresses appropriately.
@@ -159,6 +248,11 @@
} data;
};
+struct binder_transaction_data_sg {
+ struct binder_transaction_data transaction_data;
+ binder_size_t buffers_size;
+};
+
struct binder_ptr_cookie {
binder_uintptr_t ptr;
binder_uintptr_t cookie;
@@ -341,6 +435,12 @@
/*
* void *: cookie
*/
+
+ BC_TRANSACTION_SG = _IOW('c', 17, struct binder_transaction_data_sg),
+ BC_REPLY_SG = _IOW('c', 18, struct binder_transaction_data_sg),
+ /*
+ * binder_transaction_data_sg: the sent command.
+ */
};
#endif /* _UAPI_LINUX_BINDER_H */
diff --git a/drivers/staging/nanohub/spi.c b/drivers/staging/nanohub/spi.c
index 30c0bf8..c25e02b 100644
--- a/drivers/staging/nanohub/spi.c
+++ b/drivers/staging/nanohub/spi.c
@@ -321,7 +321,11 @@
if (ret == 0) {
if (offset > 0) {
packet = (struct nanohub_packet *)rx;
- memcpy(&rx[offset], comms->rx_buffer, xfer.len);
+ if (offset + xfer.len > max_length)
+ memcpy(&rx[offset], comms->rx_buffer,
+ max_length - offset);
+ else
+ memcpy(&rx[offset], comms->rx_buffer, xfer.len);
spi_data->rx_offset = spi_data->rx_length = 0;
} else {
for (i = 0; i < xfer.len; i++) {
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 0afbe14..81b55f3 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -528,22 +528,60 @@
bool enabled;
struct ffs_data *data;
struct android_dev *dev;
+ struct android_usb_function *android_func;
+ struct list_head list_item;
};
+#define MAX_FFS_FUNCTIONS 5
+static struct list_head ffs_configs;
+static struct mutex ffs_configs_lock;
+
static int ffs_function_init(struct android_usb_function *f,
struct usb_composite_dev *cdev)
{
- f->config = kzalloc(sizeof(struct functionfs_config), GFP_KERNEL);
- if (!f->config)
- return -ENOMEM;
+ int i;
+ struct functionfs_config *config;
+ struct functionfs_config *next;
+ struct android_usb_function *nf;
+ INIT_LIST_HEAD(&ffs_configs);
+ mutex_init(&ffs_configs_lock);
+
+ for (i = 0; i < MAX_FFS_FUNCTIONS; i++) {
+ nf = kmalloc(sizeof(*nf), GFP_KERNEL);
+ config = kzalloc(sizeof(*config), GFP_KERNEL);
+ if (!nf || !config) {
+ kfree(nf);
+ kfree(config);
+ list_for_each_entry_safe(config, next,
+ &ffs_configs, list_item) {
+ list_del(&config->list_item);
+ kfree(config->android_func);
+ kfree(config);
+ }
+ return -ENOMEM;
+ }
+
+ memcpy(nf, f, sizeof(struct android_usb_function));
+ nf->config = config;
+ config->android_func = nf;
+
+ list_add_tail(&config->list_item, &ffs_configs);
+ }
return functionfs_init();
}
static void ffs_function_cleanup(struct android_usb_function *f)
{
+ struct functionfs_config *config;
+ struct functionfs_config *next;
+ list_for_each_entry_safe(config, next, &ffs_configs, list_item) {
+ list_del(&config->list_item);
+
+ kfree(config->android_func);
+ kfree(config);
+ }
functionfs_cleanup();
- kfree(f->config);
}
static void ffs_function_enable(struct android_usb_function *f)
@@ -636,68 +674,90 @@
static int functionfs_ready_callback(struct ffs_data *ffs)
{
- struct android_dev *dev = ffs_function.android_dev;
- struct functionfs_config *config = ffs_function.config;
+ struct android_dev *dev;
+ struct functionfs_config *config = NULL;
+ struct functionfs_config *cur;
int ret = 0;
- /* dev is null in case ADB is not in the composition */
- if (dev) {
- mutex_lock(&dev->mutex);
- ret = functionfs_bind(ffs, dev->cdev);
- if (ret) {
- mutex_unlock(&dev->mutex);
- return ret;
+ mutex_lock(&ffs_configs_lock);
+ list_for_each_entry(cur, &ffs_configs, list_item) {
+ if (!cur->opened && cur->android_func->android_dev) {
+ config = cur;
+ break;
}
- } else {
- /* android ffs_func requires daemon to start only after enable*/
- pr_debug("start adbd only in ADB composition\n");
+ }
+ if (!config) {
+ pr_err("ffs function %s was not enabled\n",
+ ffs->dev_name);
+ mutex_unlock(&ffs_configs_lock);
return -ENODEV;
}
+ dev = config->android_func->android_dev;
+ mutex_lock(&dev->mutex);
+ ret = functionfs_bind(ffs, dev->cdev);
+ if (ret) {
+ mutex_unlock(&dev->mutex);
+ mutex_unlock(&ffs_configs_lock);
+ return ret;
+ }
config->data = ffs;
config->opened = true;
- /* Save dev in case the adb function will get disabled */
config->dev = dev;
+ mutex_unlock(&ffs_configs_lock);
+
if (config->enabled)
android_enable(dev);
mutex_unlock(&dev->mutex);
- return 0;
+ return ret;
}
static void functionfs_closed_callback(struct ffs_data *ffs)
{
- struct android_dev *dev = ffs_function.android_dev;
- struct functionfs_config *config = ffs_function.config;
+ struct android_dev *dev;
+ struct functionfs_config *config = NULL;
+ struct functionfs_config *cur;
- /*
- * In case new composition is without ADB or ADB got disabled by the
- * time ffs_daemon was stopped then use saved one
- */
+ mutex_lock(&ffs_configs_lock);
+ list_for_each_entry(cur, &ffs_configs, list_item) {
+ if (cur->data == ffs) {
+ config = cur;
+ break;
+ }
+ }
+ if (!config) {
+ pr_err("ffs closed callback failed %s!\n", ffs->dev_name);
+ mutex_unlock(&ffs_configs_lock);
+ return;
+ }
+ dev = config->android_func->android_dev;
if (!dev)
dev = config->dev;
/* fatal-error: It should never happen */
- if (!dev)
- pr_err("adb_closed_callback: config->dev is NULL");
+ if (!dev) {
+ pr_err("functionfs_closed_callback: config->dev is NULL");
+ mutex_unlock(&ffs_configs_lock);
+ return;
+ }
- if (dev)
- mutex_lock(&dev->mutex);
+ mutex_lock(&dev->mutex);
- if (config->enabled && dev)
+ if (config->enabled)
android_disable(dev);
config->dev = NULL;
config->opened = false;
config->data = NULL;
+ mutex_unlock(&ffs_configs_lock);
functionfs_unbind(ffs);
- if (dev)
- mutex_unlock(&dev->mutex);
+ mutex_unlock(&dev->mutex);
}
static void *functionfs_acquire_dev_callback(const char *dev_name)
@@ -3078,6 +3138,49 @@
return -EINVAL;
}
+static int android_enable_ffs_function(struct android_dev *dev,
+ struct android_configuration *conf,
+ char *alias) {
+ struct functionfs_config *config;
+ struct android_usb_function *match = NULL;
+ struct android_usb_function_holder *f_holder;
+ struct android_usb_platform_data *pdata = dev->pdata;
+ struct usb_gadget *gadget = dev->cdev->gadget;
+
+ list_for_each_entry(config, &ffs_configs, list_item) {
+ /* Function already enabled */
+ if (config->android_func->android_dev)
+ continue;
+ if (config->data) {
+ if (!strcmp(alias, config->data->dev_name))
+ match = config->android_func;
+ else
+ continue;
+ }
+
+ /* If no match found, choose first empty function */
+ if (!match)
+ match = config->android_func;
+ }
+ if (!match) {
+ pr_err("too many ffs functions enabled, max is %d\n",
+ MAX_FFS_FUNCTIONS);
+ return -EOVERFLOW;
+ }
+ f_holder = kzalloc(sizeof(*f_holder), GFP_KERNEL);
+ if (unlikely(!f_holder)) {
+ return -ENOMEM;
+ }
+
+ match->android_dev = dev;
+ f_holder->f = match;
+ list_add_tail(&f_holder->enabled_list,
+ &conf->enabled_functions);
+ pr_debug("func:%s is enabled.\n", match->name);
+ check_streaming_func(gadget, pdata, match->name);
+ return 0;
+}
+
/*-------------------------------------------------------------------------*/
/* /sys/class/android_usb/android%d/ interface */
@@ -3167,10 +3270,12 @@
int is_ffs;
int ffs_enabled = 0;
+ mutex_lock(&ffs_configs_lock);
mutex_lock(&dev->mutex);
if (dev->enabled) {
mutex_unlock(&dev->mutex);
+ mutex_unlock(&ffs_configs_lock);
return -EBUSY;
}
@@ -3221,9 +3326,8 @@
}
if (is_ffs) {
- if (ffs_enabled)
- continue;
- err = android_enable_function(dev, conf, "ffs");
+ err = android_enable_ffs_function(dev,
+ conf, name);
if (err)
pr_err("android_usb: Cannot enable ffs (%d)",
err);
@@ -3251,6 +3355,7 @@
}
mutex_unlock(&dev->mutex);
+ mutex_unlock(&ffs_configs_lock);
return size;
}
diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c
index cd3d407..137be79 100644
--- a/drivers/usb/gadget/f_accessory.c
+++ b/drivers/usb/gadget/f_accessory.c
@@ -709,10 +709,17 @@
break;
}
- if (count > BULK_BUFFER_SIZE)
+ if (count > BULK_BUFFER_SIZE) {
xfer = BULK_BUFFER_SIZE;
- else
+ /* ZLP, They will be more TX requests so not yet. */
+ req->zero = 0;
+ } else {
xfer = count;
+ /* If the data length is a multple of the
+ * maxpacket size then send a zero length packet(ZLP).
+ */
+ req->zero = ((xfer % dev->ep_in->maxpacket) == 0);
+ }
if (copy_from_user(req->buf, buf, xfer)) {
r = -EFAULT;
break;
diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c
index f1852ccd..1c979f8 100644
--- a/drivers/usb/gadget/f_fs.c
+++ b/drivers/usb/gadget/f_fs.c
@@ -22,6 +22,7 @@
#include <linux/pagemap.h>
#include <linux/export.h>
#include <linux/hid.h>
+#include <linux/workqueue.h>
#include <asm/unaligned.h>
#include <linux/usb/composite.h>
@@ -30,6 +31,8 @@
#define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */
+#define ENDPOINT_ALLOC_MAX 1 << 25 /* Max endpoint buffer size, 32 MB */
+
/* Debugging ****************************************************************/
@@ -73,6 +76,26 @@
FFS_ACTIVE,
/*
+ * Function is visible to host, but it's not functional. All
+ * setup requests are stalled and transfers on another endpoints
+ * are refused. All epfiles, except ep0, are deleted so there
+ * is no way to perform any operations on them.
+ *
+ * This state is set after closing all functionfs files, when
+ * mount parameter "no_disconnect=1" has been set. Function will
+ * remain in deactivated state until filesystem is umounted or
+ * ep0 is opened again. In the second case functionfs state will
+ * be reset, and it will be ready for descriptors and strings
+ * writing.
+ *
+ * This is useful only when functionfs is composed to gadget
+ * with another function which can perform some critical
+ * operations, and it's strongly desired to have this operations
+ * completed, even after functionfs files closure.
+ */
+ FFS_DEACTIVATED,
+
+ /*
* All endpoints have been closed. This state is also set if
* we encounter an unrecoverable error. The only
* unrecoverable error is situation when after reading strings
@@ -141,8 +164,6 @@
struct usb_request *ep0req; /* P: mutex */
struct completion ep0req_completion; /* P: mutex */
int ep0req_status; /* P: mutex */
- struct completion epin_completion;
- struct completion epout_completion;
/* reference counter */
atomic_t ref;
@@ -237,6 +258,9 @@
kgid_t gid;
} file_perms;
+ bool no_disconnect;
+ struct work_struct reset_work;
+
/*
* The endpoint files, filled by ffs_epfiles_create(),
* destroyed by ffs_epfiles_destroy().
@@ -335,6 +359,9 @@
unsigned char isoc; /* P: ffs->eps_lock */
unsigned char _pad;
+
+ unsigned long buf_len;
+ char *buffer;
};
static int __must_check ffs_epfiles_create(struct ffs_data *ffs);
@@ -772,11 +799,10 @@
{
struct ffs_epfile *epfile = file->private_data;
struct ffs_ep *ep;
- struct ffs_data *ffs = epfile->ffs;
char *data = NULL;
ssize_t ret;
int halt;
- int buffer_len = 0;
+ size_t buffer_len = 0;
pr_debug("%s: len %zu, read %d\n", __func__, len, read);
@@ -837,7 +863,10 @@
/* Allocate & copy */
if (!halt && !data) {
- data = kzalloc(buffer_len, GFP_KERNEL);
+
+ data = buffer_len > epfile->buf_len ?
+ kzalloc(buffer_len, GFP_KERNEL) :
+ epfile->buffer;
if (unlikely(!data))
return -ENOMEM;
@@ -874,27 +903,21 @@
ret = -EBADMSG;
} else {
/* Fire the request */
- struct completion *done;
+ DECLARE_COMPLETION_ONSTACK(done);
struct usb_request *req = ep->req;
req->complete = ffs_epfile_io_complete;
req->buf = data;
req->length = buffer_len;
+ req->context = &done;
- if (read) {
- INIT_COMPLETION(ffs->epout_completion);
- req->context = done = &ffs->epout_completion;
- } else {
- INIT_COMPLETION(ffs->epin_completion);
- req->context = done = &ffs->epin_completion;
- }
ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
spin_unlock_irq(&epfile->ffs->eps_lock);
if (unlikely(ret < 0)) {
ret = -EIO;
- } else if (unlikely(wait_for_completion_interruptible(done))) {
+ } else if (unlikely(wait_for_completion_interruptible(&done))) {
spin_lock_irq(&epfile->ffs->eps_lock);
/*
* While we were acquiring lock endpoint got disabled
@@ -917,14 +940,14 @@
spin_unlock_irq(&epfile->ffs->eps_lock);
if (read && ret > 0) {
if (len != MAX_BUF_LEN && ret < len)
- pr_err("less data(%zd) recieved than intended length(%zu)\n",
+ pr_debug("less data(%zd) received than intended length(%zu)\n",
ret, len);
if (ret > len) {
- ret = -EOVERFLOW;
- pr_err("More data(%zd) recieved than intended length(%zu)\n",
+ pr_err("More data(%zd) received than intended length(%zu)\n",
ret, len);
+ ret = -EOVERFLOW;
} else if (unlikely(copy_to_user(
- buf, data, ret))) {
+ buf, data, ret))) {
pr_err("Fail to copy to user len:%zd\n",
ret);
ret = -EFAULT;
@@ -935,7 +958,8 @@
mutex_unlock(&epfile->mutex);
error:
- kfree(data);
+ if (buffer_len > epfile->buf_len)
+ kfree(data);
if (ret < 0 && ret != -ERESTARTSYS)
pr_err_ratelimited("%s(): Error: returning %zd value\n",
__func__, ret);
@@ -984,6 +1008,9 @@
ENTER();
atomic_set(&epfile->error, 1);
+ epfile->buf_len = 0;
+ kfree(epfile->buffer);
+ epfile->buffer = NULL;
ffs_data_closed(epfile->ffs);
file->private_data = NULL;
@@ -1002,7 +1029,7 @@
return -ENODEV;
spin_lock_irq(&epfile->ffs->eps_lock);
- if (likely(epfile->ep)) {
+ if (epfile->ep) {
switch (code) {
case FUNCTIONFS_FIFO_STATUS:
ret = usb_ep_fifo_status(epfile->ep->ep);
@@ -1017,6 +1044,52 @@
case FUNCTIONFS_ENDPOINT_REVMAP:
ret = epfile->ep->num;
break;
+ case FUNCTIONFS_ENDPOINT_DESC:
+ {
+ int desc_idx;
+ struct usb_endpoint_descriptor *desc;
+
+ switch (epfile->ffs->gadget->speed) {
+ case USB_SPEED_SUPER:
+ desc_idx = 2;
+ break;
+ case USB_SPEED_HIGH:
+ desc_idx = 1;
+ break;
+ default:
+ desc_idx = 0;
+ }
+ desc = epfile->ep->descs[desc_idx];
+
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+ ret = copy_to_user((void *)value, desc, sizeof(*desc));
+ if (ret)
+ ret = -EFAULT;
+ return ret;
+ }
+ case FUNCTIONFS_ENDPOINT_ALLOC:
+ {
+ void *temp = epfile->buffer;
+ epfile->buffer = NULL;
+ epfile->buf_len = 0;
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+
+ kfree(temp);
+ if (!value)
+ return 0;
+ if (value > ENDPOINT_ALLOC_MAX)
+ return -EINVAL;
+
+ temp = kzalloc(value, GFP_KERNEL);
+ if (!temp)
+ return -ENOMEM;
+
+ spin_lock_irq(&epfile->ffs->eps_lock);
+ epfile->buffer = temp;
+ epfile->buf_len = value;
+ ret = 0;
+ break;
+ }
default:
ret = -ENOTTY;
}
@@ -1117,6 +1190,7 @@
struct ffs_file_perms perms;
umode_t root_mode;
const char *dev_name;
+ bool no_disconnect;
struct ffs_data *ffs_data;
};
@@ -1187,6 +1261,12 @@
/* Interpret option */
switch (eq - opts) {
+ case 13:
+ if (!memcmp(opts, "no_disconnect", 13))
+ data->no_disconnect = !!value;
+ else
+ goto invalid;
+ break;
case 5:
if (!memcmp(opts, "rmode", 5))
data->root_mode = (value & 0555) | S_IFDIR;
@@ -1251,6 +1331,7 @@
.gid = GLOBAL_ROOT_GID,
},
.root_mode = S_IFDIR | 0500,
+ .no_disconnect = false,
};
struct dentry *rv;
int ret;
@@ -1267,6 +1348,7 @@
if (unlikely(!ffs))
return ERR_PTR(-ENOMEM);
ffs->file_perms = data.perms;
+ ffs->no_disconnect = data.no_disconnect;
ffs->dev_name = kstrdup(dev_name, GFP_KERNEL);
if (unlikely(!ffs->dev_name)) {
@@ -1298,6 +1380,7 @@
kill_litter_super(sb);
if (sb->s_fs_info) {
functionfs_release_dev_callback(sb->s_fs_info);
+ ffs_data_closed(sb->s_fs_info);
ffs_data_put(sb->s_fs_info);
}
}
@@ -1356,7 +1439,11 @@
smp_mb__before_atomic();
atomic_inc(&ffs->ref);
- atomic_inc(&ffs->opened);
+ if (atomic_add_return(1, &ffs->opened) == 1 &&
+ ffs->state == FFS_DEACTIVATED) {
+ ffs->state = FFS_CLOSING;
+ ffs_data_reset(ffs);
+ }
}
static void ffs_data_put(struct ffs_data *ffs)
@@ -1380,6 +1467,21 @@
smp_mb__before_atomic();
if (atomic_dec_and_test(&ffs->opened)) {
+ if (ffs->no_disconnect) {
+ ffs->state = FFS_DEACTIVATED;
+ if (ffs->epfiles) {
+ ffs_epfiles_destroy(ffs->epfiles,
+ ffs->eps_count);
+ ffs->epfiles = NULL;
+ }
+ if (ffs->setup_state == FFS_SETUP_PENDING)
+ __ffs_ep0_stall(ffs);
+ } else {
+ ffs->state = FFS_CLOSING;
+ ffs_data_reset(ffs);
+ }
+ }
+ if (atomic_read(&ffs->opened) < 0) {
ffs->state = FFS_CLOSING;
ffs_data_reset(ffs);
}
@@ -1402,8 +1504,6 @@
spin_lock_init(&ffs->eps_lock);
init_waitqueue_head(&ffs->ev.waitq);
init_completion(&ffs->ep0req_completion);
- init_completion(&ffs->epout_completion);
- init_completion(&ffs->epin_completion);
/* XXX REVISIT need to update it in some places, or do we? */
ffs->ev.can_stall = 1;
@@ -1642,16 +1742,19 @@
spin_lock_irqsave(&func->ffs->eps_lock, flags);
do {
- atomic_set(&epfile->error, 1);
+ if (epfile)
+ atomic_set(&epfile->error, 1);
/* pending requests get nuked */
if (likely(ep->ep)) {
usb_ep_disable(ep->ep);
ep->ep->driver_data = NULL;
}
- epfile->ep = NULL;
-
++ep;
- ++epfile;
+
+ if (epfile) {
+ epfile->ep = NULL;
+ ++epfile;
+ }
} while (--count);
spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
}
@@ -2479,6 +2582,13 @@
/* Other USB function hooks *************************************************/
+static void ffs_reset_work(struct work_struct *work)
+{
+ struct ffs_data *ffs = container_of(work,
+ struct ffs_data, reset_work);
+ ffs_data_reset(ffs);
+}
+
static void ffs_func_unbind(struct usb_configuration *c,
struct usb_function *f)
{
@@ -2515,6 +2625,13 @@
ffs->func = NULL;
}
+ if (ffs->state == FFS_DEACTIVATED) {
+ ffs->state = FFS_CLOSING;
+ INIT_WORK(&ffs->reset_work, ffs_reset_work);
+ schedule_work(&ffs->reset_work);
+ return -ENODEV;
+ }
+
if (ffs->state != FFS_ACTIVE)
return -ENODEV;
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
index 01bb2dd..78e06b1 100644
--- a/drivers/video/msm/mdss/mdss_fb.c
+++ b/drivers/video/msm/mdss/mdss_fb.c
@@ -2,7 +2,7 @@
* Core MDSS framebuffer driver.
*
* Copyright (C) 2007 Google Incorporated
- * Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2008-2016, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -3499,6 +3499,7 @@
goto buf_sync_err_2;
}
+
ret = copy_to_user(buf_sync->rel_fen_fd, &rel_fen_fd, sizeof(int));
if (ret) {
pr_err("%s: copy_to_user failed\n", sync_pt_data->fence_name);
@@ -3535,6 +3536,7 @@
goto buf_sync_err_3;
}
+
ret = copy_to_user(buf_sync->retire_fen_fd, &retire_fen_fd,
sizeof(int));
if (ret) {
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index 6ba7382..dfe2c83 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -21,6 +21,7 @@
#include <linux/notifier.h>
#include <linux/irqreturn.h>
#include <linux/kref.h>
+#include <linux/kthread.h>
#include "mdss.h"
#include "mdss_mdp_hwio.h"
@@ -582,11 +583,14 @@
struct sw_sync_timeline *vsync_timeline;
struct mdss_mdp_vsync_handler vsync_retire_handler;
- struct work_struct retire_work;
int retire_cnt;
bool kickoff_released;
u32 cursor_ndx[2];
bool dyn_mode_switch; /* Used in prepare, bw calc for new mode */
+
+ struct kthread_worker worker;
+ struct kthread_work vsync_work;
+ struct task_struct *thread;
};
struct mdss_mdp_set_ot_params {
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c b/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
index 4aade22..874b7fc 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_cmd.c
@@ -1213,7 +1213,7 @@
int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state)
{
struct mdss_mdp_cmd_ctx *ctx = ctl->intf_ctx[MASTER_CTX];
- struct mdss_mdp_cmd_ctx *sctx;
+ struct mdss_mdp_cmd_ctx *sctx = NULL;
struct mdss_mdp_ctl *sctl = mdss_mdp_get_split_ctl(ctl);
bool panel_off = false;
bool turn_off_clocks = false;
@@ -1584,4 +1584,3 @@
return 0;
}
-
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index 4ab89d1..41ca5cd 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -4631,12 +4631,12 @@
/*
* the retire work can still schedule after above retire_signal
* api call. Flush workqueue guarantees that current caller
- * context is blocked till retire_work finishes. Any work
+ * context is blocked till vsync_work finishes. Any work
* schedule after flush call should not cause any issue because
* retire_signal api checks for retire_cnt with sync_mutex lock.
*/
- flush_work(&mdp5_data->retire_work);
+ flush_kthread_work(&mdp5_data->vsync_work);
}
ctl_stop:
@@ -4823,13 +4823,13 @@
}
mdp5_data = mfd_to_mdp5_data(mfd);
- schedule_work(&mdp5_data->retire_work);
+ queue_kthread_work(&mdp5_data->worker, &mdp5_data->vsync_work);
}
-static void __vsync_retire_work_handler(struct work_struct *work)
+static void __vsync_retire_work_handler(struct kthread_work *work)
{
struct mdss_overlay_private *mdp5_data =
- container_of(work, typeof(*mdp5_data), retire_work);
+ container_of(work, typeof(*mdp5_data), vsync_work);
if (!mdp5_data->ctl || !mdp5_data->ctl->mfd)
return;
@@ -4917,6 +4917,7 @@
{
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
char name[24];
+ struct sched_param param = { .sched_priority = 5 };
snprintf(name, sizeof(name), "mdss_fb%d_retire", mfd->index);
mdp5_data->vsync_timeline = sw_sync_timeline_create(name);
@@ -4924,12 +4925,25 @@
pr_err("cannot vsync create time line");
return -ENOMEM;
}
+
+ init_kthread_worker(&mdp5_data->worker);
+ init_kthread_work(&mdp5_data->vsync_work, __vsync_retire_work_handler);
+
+ mdp5_data->thread = kthread_run(kthread_worker_fn,
+ &mdp5_data->worker, "vsync_retire_work");
+
+ if (IS_ERR(mdp5_data->thread)) {
+ pr_err("unable to start vsync thread\n");
+ mdp5_data->thread = NULL;
+ return -ENOMEM;
+ }
+
+ sched_setscheduler(mdp5_data->thread, SCHED_FIFO, ¶m);
mfd->mdp_sync_pt_data.get_retire_fence = __vsync_retire_get_fence;
mdp5_data->vsync_retire_handler.vsync_handler =
__vsync_retire_handle_vsync;
mdp5_data->vsync_retire_handler.cmd_post_flush = false;
- INIT_WORK(&mdp5_data->retire_work, __vsync_retire_work_handler);
return 0;
}
diff --git a/fs/attr.c b/fs/attr.c
index 66fa625..64fc598 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -167,7 +167,7 @@
}
EXPORT_SYMBOL(setattr_copy);
-int notify_change(struct dentry * dentry, struct iattr * attr)
+int notify_change2(struct vfsmount *mnt, struct dentry * dentry, struct iattr * attr)
{
struct inode *inode = dentry->d_inode;
umode_t mode = inode->i_mode;
@@ -239,7 +239,9 @@
if (error)
return error;
- if (inode->i_op->setattr)
+ if (mnt && inode->i_op->setattr2)
+ error = inode->i_op->setattr2(mnt, dentry, attr);
+ else if (inode->i_op->setattr)
error = inode->i_op->setattr(dentry, attr);
else
error = simple_setattr(dentry, attr);
@@ -252,4 +254,10 @@
return error;
}
+EXPORT_SYMBOL(notify_change2);
+
+int notify_change(struct dentry * dentry, struct iattr * attr)
+{
+ return notify_change2(NULL, dentry, attr);
+}
EXPORT_SYMBOL(notify_change);
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index ed29cb5..1ae7822 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -567,6 +567,98 @@
#endif
}
+#ifdef CONFIG_KUSER_HELPERS_SELECTIVE_DISABLE
+#define ANDROID_NOTE_OWNER"Android"
+#define ANDROID_KUSER_HELPER_TYPE 0x3L
+#define ANDROID_KUSER_HELPER_ON 0x1L
+
+static int should_call_arch_setup_additional_pages(struct linux_binprm *bprm,
+ struct elfhdr *elf_ex,
+ struct elf_phdr *elf_ppnt)
+{
+ Elf64_Half i;
+
+ /* We want to allow vdso, but not kuser_helpers */
+ if (elf_ex->e_ident[EI_CLASS] == ELFCLASS64)
+ return true;
+
+ for (i = 0; i < elf_ex->e_phnum; i++, elf_ppnt++) {
+ int retval;
+ void *elf_pnotes;
+ struct elf32_note *next_elf_notep;
+ Elf64_Xword left_to_read;
+
+ if (elf_ppnt->p_type != PT_NOTE)
+ continue;
+
+ /*
+ * This code is seeing if we have a special note.
+ * The note tells us that this binary still needs
+ * arch_setup_additional_pages to be called.
+ */
+ if (elf_ppnt->p_filesz < sizeof(struct elf32_note))
+ break;
+
+ elf_pnotes = kmalloc(elf_ppnt->p_filesz, GFP_KERNEL);
+ if (!elf_pnotes)
+ return -ENOMEM;
+
+ retval = kernel_read(bprm->file, elf_ppnt->p_offset,
+ (char *)elf_pnotes, elf_ppnt->p_filesz);
+ if (retval < 0) {
+ kfree(elf_pnotes);
+ return retval;
+ }
+
+ if((Elf64_Xword) retval != elf_ppnt->p_filesz) {
+ kfree(elf_pnotes);
+ return -EIO;
+ }
+
+ /*
+ * Now that we have read in all the notes and find ours
+ */
+ next_elf_notep = (struct elf32_note *)elf_pnotes;
+ left_to_read = elf_ppnt->p_filesz;
+ while (left_to_read >= sizeof(struct elf32_note)) {
+ char *note_namep;
+
+ left_to_read -= sizeof(struct elf32_note);
+
+ /* Sanity check on the name and desc length*/
+ if (((Elf64_Xword) next_elf_notep->n_namesz +
+ (Elf64_Xword) next_elf_notep->n_descsz) >
+ left_to_read)
+ break;
+
+ note_namep = (char *)next_elf_notep +
+ sizeof(struct elf32_note);
+ left_to_read -= next_elf_notep->n_namesz;
+ left_to_read -= next_elf_notep->n_descsz;
+
+ if ((sizeof(ANDROID_NOTE_OWNER) ==
+ next_elf_notep->n_namesz) &&
+ (next_elf_notep->n_type ==
+ ANDROID_KUSER_HELPER_TYPE) &&
+ strncmp(note_namep, ANDROID_NOTE_OWNER,
+ next_elf_notep->n_namesz) == 0) {
+ kfree(elf_pnotes);
+ return true;
+ }
+
+ next_elf_notep = (struct elf32_note *)
+ (note_namep +
+ next_elf_notep->n_namesz +
+ next_elf_notep->n_descsz);
+ }
+
+ kfree(elf_pnotes);
+ }
+
+ return false;
+}
+#endif
+
static int load_elf_binary(struct linux_binprm *bprm)
{
struct file *interpreter = NULL; /* to shut gcc up */
@@ -931,18 +1023,29 @@
}
}
- kfree(elf_phdata);
-
set_binfmt(&elf_format);
#ifdef ARCH_HAS_SETUP_ADDITIONAL_PAGES
- retval = arch_setup_additional_pages(bprm, !!elf_interpreter);
- if (retval < 0) {
- send_sig(SIGKILL, current, 0);
+#ifdef CONFIG_KUSER_HELPERS_SELECTIVE_DISABLE
+ retval = should_call_arch_setup_additional_pages(bprm, &loc->elf_ex,
+ elf_phdata);
+ if (retval < 0)
goto out;
+
+ if (retval) {
+#endif
+ retval = arch_setup_additional_pages(bprm, !!elf_interpreter);
+ if (retval < 0) {
+ send_sig(SIGKILL, current, 0);
+ goto out;
+ }
+#ifdef CONFIG_KUSER_HELPERS_SELECTIVE_DISABLE
}
+#endif
#endif /* ARCH_HAS_SETUP_ADDITIONAL_PAGES */
+ kfree(elf_phdata);
+
install_exec_creds(bprm);
retval = create_elf_tables(bprm, &loc->elf_ex,
load_addr, interp_load_addr);
diff --git a/fs/coredump.c b/fs/coredump.c
index 1d402ce..c0fba3f 100644
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -644,7 +644,7 @@
goto close_fail;
if (!cprm.file->f_op || !cprm.file->f_op->write)
goto close_fail;
- if (do_truncate(cprm.file->f_path.dentry, 0, 0, cprm.file))
+ if (do_truncate2(cprm.file->f_path.mnt, cprm.file->f_path.dentry, 0, 0, cprm.file))
goto close_fail;
}
diff --git a/fs/exec.c b/fs/exec.c
index cb7f31c..03182da 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1100,7 +1100,7 @@
void would_dump(struct linux_binprm *bprm, struct file *file)
{
- if (inode_permission(file_inode(file), MAY_READ) < 0)
+ if (inode_permission2(file->f_path.mnt, file_inode(file), MAY_READ) < 0)
bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;
}
EXPORT_SYMBOL(would_dump);
diff --git a/fs/ext4/crypto.c b/fs/ext4/crypto.c
index 2b49293..a5ba775 100644
--- a/fs/ext4/crypto.c
+++ b/fs/ext4/crypto.c
@@ -34,6 +34,7 @@
#include <linux/random.h>
#include <linux/scatterlist.h>
#include <linux/spinlock_types.h>
+#include <linux/namei.h>
#include "ext4_extents.h"
#include "xattr.h"
@@ -485,6 +486,9 @@
struct ext4_crypt_info *ci;
int dir_has_key, cached_with_key;
+ if (flags & LOOKUP_RCU)
+ return -ECHILD;
+
dir = dget_parent(dentry);
if (!ext4_encrypted_inode(d_inode(dir))) {
dput(dir);
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 28481d3..8f8de66 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3440,6 +3440,11 @@
unsigned blocksize;
struct inode *inode = mapping->host;
+ /* If we are processing an encrypted inode during orphan list
+ * handling */
+ if (ext4_encrypted_inode(inode) && !ext4_has_encryption_key(inode))
+ return 0;
+
blocksize = inode->i_sb->s_blocksize;
length = blocksize - (offset & (blocksize - 1));
diff --git a/fs/fs_struct.c b/fs/fs_struct.c
index d8ac61d..5a1bb82 100644
--- a/fs/fs_struct.c
+++ b/fs/fs_struct.c
@@ -127,6 +127,7 @@
}
return fs;
}
+EXPORT_SYMBOL_GPL(copy_fs_struct);
int unshare_fs_struct(void)
{
diff --git a/fs/inode.c b/fs/inode.c
index 1eab5de..e7a0547 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -1634,12 +1634,12 @@
}
EXPORT_SYMBOL(should_remove_suid);
-static int __remove_suid(struct dentry *dentry, int kill)
+static int __remove_suid(struct vfsmount *mnt, struct dentry *dentry, int kill)
{
struct iattr newattrs;
newattrs.ia_valid = ATTR_FORCE | kill;
- return notify_change(dentry, &newattrs);
+ return notify_change2(mnt, dentry, &newattrs);
}
int file_remove_suid(struct file *file)
@@ -1662,7 +1662,7 @@
if (killpriv)
error = security_inode_killpriv(dentry);
if (!error && killsuid)
- error = __remove_suid(dentry, killsuid);
+ error = __remove_suid(file->f_path.mnt, dentry, killsuid);
if (!error && (inode->i_sb->s_flags & MS_NOSEC))
inode->i_flags |= S_NOSEC;
diff --git a/fs/internal.h b/fs/internal.h
index 6812158..8c89460 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -83,9 +83,11 @@
* super.c
*/
extern int do_remount_sb(struct super_block *, int, void *, int);
+extern int do_remount_sb2(struct vfsmount *, struct super_block *, int,
+ void *, int);
extern bool grab_super_passive(struct super_block *sb);
extern struct dentry *mount_fs(struct file_system_type *,
- int, const char *, void *);
+ int, const char *, struct vfsmount *, void *);
extern struct super_block *user_get_super(dev_t);
/*
diff --git a/fs/namei.c b/fs/namei.c
index 750f569..5778b51 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -356,9 +356,11 @@
* flag in inode->i_opflags, that says "this has not special
* permission function, use the fast case".
*/
-static inline int do_inode_permission(struct inode *inode, int mask)
+static inline int do_inode_permission(struct vfsmount *mnt, struct inode *inode, int mask)
{
if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) {
+ if (likely(mnt && inode->i_op->permission2))
+ return inode->i_op->permission2(mnt, inode, mask);
if (likely(inode->i_op->permission))
return inode->i_op->permission(inode, mask);
@@ -382,7 +384,7 @@
* This does not check for a read-only file system. You probably want
* inode_permission().
*/
-int __inode_permission(struct inode *inode, int mask)
+int __inode_permission2(struct vfsmount *mnt, struct inode *inode, int mask)
{
int retval;
@@ -394,7 +396,7 @@
return -EACCES;
}
- retval = do_inode_permission(inode, mask);
+ retval = do_inode_permission(mnt, inode, mask);
if (retval)
return retval;
@@ -404,6 +406,13 @@
return security_inode_permission(inode, mask);
}
+EXPORT_SYMBOL(__inode_permission2);
+
+int __inode_permission(struct inode *inode, int mask)
+{
+ return __inode_permission2(NULL, inode, mask);
+}
+EXPORT_SYMBOL(__inode_permission);
/**
* sb_permission - Check superblock-level permissions
@@ -437,15 +446,22 @@
*
* When checking for MAY_APPEND, MAY_WRITE must also be set in @mask.
*/
-int inode_permission(struct inode *inode, int mask)
+int inode_permission2(struct vfsmount *mnt, struct inode *inode, int mask)
{
int retval;
retval = sb_permission(inode->i_sb, inode, mask);
if (retval)
return retval;
- return __inode_permission(inode, mask);
+ return __inode_permission2(mnt, inode, mask);
}
+EXPORT_SYMBOL(inode_permission2);
+
+int inode_permission(struct inode *inode, int mask)
+{
+ return inode_permission2(NULL, inode, mask);
+}
+EXPORT_SYMBOL(inode_permission);
/**
* path_get - get a reference to a path
@@ -1460,13 +1476,13 @@
static inline int may_lookup(struct nameidata *nd)
{
if (nd->flags & LOOKUP_RCU) {
- int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
+ int err = inode_permission2(nd->path.mnt, nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
if (err != -ECHILD)
return err;
if (unlazy_walk(nd, NULL))
return -ECHILD;
}
- return inode_permission(nd->inode, MAY_EXEC);
+ return inode_permission2(nd->path.mnt, nd->inode, MAY_EXEC);
}
static inline int handle_dots(struct nameidata *nd, int type)
@@ -1840,10 +1856,11 @@
nd->depth = 0;
if (flags & LOOKUP_ROOT) {
struct inode *inode = nd->root.dentry->d_inode;
+ struct vfsmount *mnt = nd->root.mnt;
if (*name) {
if (!can_lookup(inode))
return -ENOTDIR;
- retval = inode_permission(inode, MAY_EXEC);
+ retval = inode_permission2(mnt, inode, MAY_EXEC);
if (retval)
return retval;
}
@@ -2085,6 +2102,7 @@
/**
* lookup_one_len - filesystem helper to lookup single pathname component
* @name: pathname component to lookup
+ * @mnt: mount we are looking up on
* @base: base directory to lookup from
* @len: maximum length @len should be interpreted to
*
@@ -2093,7 +2111,7 @@
* nameidata argument is passed to the filesystem methods and a filesystem
* using this helper needs to be prepared for that.
*/
-struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
+struct dentry *lookup_one_len2(const char *name, struct vfsmount *mnt, struct dentry *base, int len)
{
struct qstr this;
unsigned int c;
@@ -2127,12 +2145,19 @@
return ERR_PTR(err);
}
- err = inode_permission(base->d_inode, MAY_EXEC);
+ err = inode_permission2(mnt, base->d_inode, MAY_EXEC);
if (err)
return ERR_PTR(err);
return __lookup_hash(&this, base, 0);
}
+EXPORT_SYMBOL(lookup_one_len2);
+
+struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
+{
+ return lookup_one_len2(name, NULL, base, len);
+}
+EXPORT_SYMBOL(lookup_one_len);
int user_path_at_empty(int dfd, const char __user *name, unsigned flags,
struct path *path, int *empty)
@@ -2222,7 +2247,7 @@
* 10. We don't allow removal of NFS sillyrenamed files; it's handled by
* nfs_async_unlink().
*/
-static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
+static int may_delete(struct vfsmount *mnt, struct inode *dir,struct dentry *victim,int isdir)
{
int error;
@@ -2232,7 +2257,7 @@
BUG_ON(victim->d_parent->d_inode != dir);
audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);
- error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
+ error = inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC);
if (error)
return error;
if (IS_APPEND(dir))
@@ -2262,14 +2287,14 @@
* 3. We should have write and exec permissions on dir
* 4. We can't do it if dir is immutable (done in permission())
*/
-static inline int may_create(struct inode *dir, struct dentry *child)
+static inline int may_create(struct vfsmount *mnt, struct inode *dir, struct dentry *child)
{
audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE);
if (child->d_inode)
return -EEXIST;
if (IS_DEADDIR(dir))
return -ENOENT;
- return inode_permission(dir, MAY_WRITE | MAY_EXEC);
+ return inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC);
}
/*
@@ -2314,10 +2339,10 @@
}
}
-int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
- bool want_excl)
+int vfs_create2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry,
+ umode_t mode, bool want_excl)
{
- int error = may_create(dir, dentry);
+ int error = may_create(mnt, dir, dentry);
if (error)
return error;
@@ -2338,10 +2363,19 @@
fsnotify_create(dir, dentry);
return error;
}
+EXPORT_SYMBOL(vfs_create2);
+
+int vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
+ bool want_excl)
+{
+ return vfs_create2(NULL, dir, dentry, mode, want_excl);
+}
+EXPORT_SYMBOL(vfs_create);
static int may_open(struct path *path, int acc_mode, int flag)
{
struct dentry *dentry = path->dentry;
+ struct vfsmount *mnt = path->mnt;
struct inode *inode = dentry->d_inode;
int error;
@@ -2370,7 +2404,7 @@
break;
}
- error = inode_permission(inode, acc_mode);
+ error = inode_permission2(mnt, inode, acc_mode);
if (error)
return error;
@@ -2405,7 +2439,7 @@
if (!error)
error = security_path_truncate(path);
if (!error) {
- error = do_truncate(path->dentry, 0,
+ error = do_truncate2(path->mnt, path->dentry, 0,
ATTR_MTIME|ATTR_CTIME|ATTR_OPEN,
filp);
}
@@ -2426,7 +2460,7 @@
if (error)
return error;
- error = inode_permission(dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);
+ error = inode_permission2(dir->mnt, dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);
if (error)
return error;
@@ -2614,6 +2648,7 @@
bool got_write, int *opened)
{
struct dentry *dir = nd->path.dentry;
+ struct vfsmount *mnt = nd->path.mnt;
struct inode *dir_inode = dir->d_inode;
struct dentry *dentry;
int error;
@@ -2661,7 +2696,7 @@
error = security_path_mknod(&nd->path, dentry, mode, 0);
if (error)
goto out_dput;
- error = vfs_create(dir->d_inode, dentry, mode,
+ error = vfs_create2(mnt, dir->d_inode, dentry, mode,
nd->flags & LOOKUP_EXCL);
if (error)
goto out_dput;
@@ -3122,9 +3157,9 @@
}
EXPORT_SYMBOL(user_path_create);
-int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
+int vfs_mknod2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
{
- int error = may_create(dir, dentry);
+ int error = may_create(mnt, dir, dentry);
if (error)
return error;
@@ -3155,6 +3190,13 @@
return 0;
}
+EXPORT_SYMBOL(vfs_mknod2);
+
+int vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev)
+{
+ return vfs_mknod2(NULL, dir, dentry, mode, dev);
+}
+EXPORT_SYMBOL(vfs_mknod);
static int may_mknod(umode_t mode)
{
@@ -3196,10 +3238,10 @@
goto out;
switch (mode & S_IFMT) {
case 0: case S_IFREG:
- error = vfs_create(path.dentry->d_inode,dentry,mode,true);
+ error = vfs_create2(path.mnt, path.dentry->d_inode,dentry,mode,true);
break;
case S_IFCHR: case S_IFBLK:
- error = vfs_mknod(path.dentry->d_inode,dentry,mode,
+ error = vfs_mknod2(path.mnt, path.dentry->d_inode,dentry,mode,
new_decode_dev(dev));
break;
case S_IFIFO: case S_IFSOCK:
@@ -3220,9 +3262,9 @@
return sys_mknodat(AT_FDCWD, filename, mode, dev);
}
-int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+int vfs_mkdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, umode_t mode)
{
- int error = may_create(dir, dentry);
+ int error = may_create(mnt, dir, dentry);
unsigned max_links = dir->i_sb->s_max_links;
if (error)
@@ -3244,6 +3286,13 @@
fsnotify_mkdir(dir, dentry);
return error;
}
+EXPORT_SYMBOL(vfs_mkdir2);
+
+int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
+{
+ return vfs_mkdir2(NULL, dir, dentry, mode);
+}
+EXPORT_SYMBOL(vfs_mkdir);
SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, umode_t, mode)
{
@@ -3261,7 +3310,7 @@
mode &= ~current_umask();
error = security_path_mkdir(&path, dentry, mode);
if (!error)
- error = vfs_mkdir(path.dentry->d_inode, dentry, mode);
+ error = vfs_mkdir2(path.mnt, path.dentry->d_inode, dentry, mode);
done_path_create(&path, dentry);
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
@@ -3299,9 +3348,9 @@
spin_unlock(&dentry->d_lock);
}
-int vfs_rmdir(struct inode *dir, struct dentry *dentry)
+int vfs_rmdir2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry)
{
- int error = may_delete(dir, dentry, 1);
+ int error = may_delete(mnt, dir, dentry, 1);
if (error)
return error;
@@ -3335,6 +3384,13 @@
d_delete(dentry);
return error;
}
+EXPORT_SYMBOL(vfs_rmdir2);
+
+int vfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ return vfs_rmdir2(NULL, dir, dentry);
+}
+EXPORT_SYMBOL(vfs_rmdir);
static long do_rmdir(int dfd, const char __user *pathname)
{
@@ -3377,7 +3433,7 @@
error = security_path_rmdir(&nd.path, dentry);
if (error)
goto exit3;
- error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
+ error = vfs_rmdir2(nd.path.mnt, nd.path.dentry->d_inode, dentry);
exit3:
dput(dentry);
exit2:
@@ -3398,9 +3454,9 @@
return do_rmdir(AT_FDCWD, pathname);
}
-int vfs_unlink(struct inode *dir, struct dentry *dentry)
+int vfs_unlink2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry)
{
- int error = may_delete(dir, dentry, 0);
+ int error = may_delete(mnt, dir, dentry, 0);
if (error)
return error;
@@ -3429,6 +3485,13 @@
return error;
}
+EXPORT_SYMBOL(vfs_unlink2);
+
+int vfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+ return vfs_unlink2(NULL, dir, dentry);
+}
+EXPORT_SYMBOL(vfs_unlink);
/*
* Make sure that the actual truncation of the file will occur outside its
@@ -3472,7 +3535,7 @@
error = security_path_unlink(&nd.path, dentry);
if (error)
goto exit2;
- error = vfs_unlink(nd.path.dentry->d_inode, dentry);
+ error = vfs_unlink2(nd.path.mnt, nd.path.dentry->d_inode, dentry);
exit2:
dput(dentry);
}
@@ -3512,9 +3575,9 @@
return do_unlinkat(AT_FDCWD, pathname);
}
-int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
+int vfs_symlink2(struct vfsmount *mnt, struct inode *dir, struct dentry *dentry, const char *oldname)
{
- int error = may_create(dir, dentry);
+ int error = may_create(mnt, dir, dentry);
if (error)
return error;
@@ -3531,6 +3594,13 @@
fsnotify_create(dir, dentry);
return error;
}
+EXPORT_SYMBOL(vfs_symlink2);
+
+int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
+{
+ return vfs_symlink2(NULL, dir, dentry, oldname);
+}
+EXPORT_SYMBOL(vfs_symlink);
SYSCALL_DEFINE3(symlinkat, const char __user *, oldname,
int, newdfd, const char __user *, newname)
@@ -3552,7 +3622,7 @@
error = security_path_symlink(&path, dentry, from->name);
if (!error)
- error = vfs_symlink(path.dentry->d_inode, dentry, from->name);
+ error = vfs_symlink2(path.mnt, path.dentry->d_inode, dentry, from->name);
done_path_create(&path, dentry);
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
@@ -3568,7 +3638,7 @@
return sys_symlinkat(oldname, AT_FDCWD, newname);
}
-int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
+int vfs_link2(struct vfsmount *mnt, struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
{
struct inode *inode = old_dentry->d_inode;
unsigned max_links = dir->i_sb->s_max_links;
@@ -3577,7 +3647,7 @@
if (!inode)
return -ENOENT;
- error = may_create(dir, new_dentry);
+ error = may_create(mnt, dir, new_dentry);
if (error)
return error;
@@ -3611,6 +3681,13 @@
fsnotify_link(dir, inode, new_dentry);
return error;
}
+EXPORT_SYMBOL(vfs_link2);
+
+int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
+{
+ return vfs_link2(NULL, old_dentry, dir, new_dentry);
+}
+EXPORT_SYMBOL(vfs_link);
/*
* Hardlinks are often used in delicate situations. We avoid
@@ -3664,7 +3741,7 @@
error = security_path_link(old_path.dentry, &new_path, new_dentry);
if (error)
goto out_dput;
- error = vfs_link(old_path.dentry, new_path.dentry->d_inode, new_dentry);
+ error = vfs_link2(old_path.mnt, old_path.dentry, new_path.dentry->d_inode, new_dentry);
out_dput:
done_path_create(&new_path, new_dentry);
if (retry_estale(error, how)) {
@@ -3710,8 +3787,9 @@
* ->i_mutex on parents, which works but leads to some truly excessive
* locking].
*/
-static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry)
+static int vfs_rename_dir(struct vfsmount *mnt,
+ struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
{
int error = 0;
struct inode *target = new_dentry->d_inode;
@@ -3722,7 +3800,7 @@
* we'll need to flip '..'.
*/
if (new_dir != old_dir) {
- error = inode_permission(old_dentry->d_inode, MAY_WRITE);
+ error = inode_permission2(mnt, old_dentry->d_inode, MAY_WRITE);
if (error)
return error;
}
@@ -3797,7 +3875,8 @@
return error;
}
-int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+int vfs_rename2(struct vfsmount *mnt,
+ struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
int error;
@@ -3807,14 +3886,14 @@
if (old_dentry->d_inode == new_dentry->d_inode)
return 0;
- error = may_delete(old_dir, old_dentry, is_dir);
+ error = may_delete(mnt, old_dir, old_dentry, is_dir);
if (error)
return error;
if (!new_dentry->d_inode)
- error = may_create(new_dir, new_dentry);
+ error = may_create(mnt, new_dir, new_dentry);
else
- error = may_delete(new_dir, new_dentry, is_dir);
+ error = may_delete(mnt, new_dir, new_dentry, is_dir);
if (error)
return error;
@@ -3824,7 +3903,7 @@
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(mnt, old_dir,old_dentry,new_dir,new_dentry);
else
error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry);
if (!error)
@@ -3834,6 +3913,14 @@
return error;
}
+EXPORT_SYMBOL(vfs_rename2);
+
+int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ return vfs_rename2(NULL, old_dir, old_dentry, new_dir, new_dentry);
+}
+EXPORT_SYMBOL(vfs_rename);
SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
int, newdfd, const char __user *, newname)
@@ -3916,7 +4003,7 @@
&newnd.path, new_dentry);
if (error)
goto exit5;
- error = vfs_rename(old_dir->d_inode, old_dentry,
+ error = vfs_rename2(oldnd.path.mnt, old_dir->d_inode, old_dentry,
new_dir->d_inode, new_dentry);
exit5:
dput(new_dentry);
@@ -4090,7 +4177,6 @@
EXPORT_SYMBOL(follow_up);
EXPORT_SYMBOL(get_write_access); /* nfsd */
EXPORT_SYMBOL(lock_rename);
-EXPORT_SYMBOL(lookup_one_len);
EXPORT_SYMBOL(page_follow_link_light);
EXPORT_SYMBOL(page_put_link);
EXPORT_SYMBOL(page_readlink);
@@ -4099,18 +4185,9 @@
EXPORT_SYMBOL(page_symlink_inode_operations);
EXPORT_SYMBOL(kern_path);
EXPORT_SYMBOL(vfs_path_lookup);
-EXPORT_SYMBOL(inode_permission);
EXPORT_SYMBOL(unlock_rename);
-EXPORT_SYMBOL(vfs_create);
EXPORT_SYMBOL(vfs_follow_link);
-EXPORT_SYMBOL(vfs_link);
-EXPORT_SYMBOL(vfs_mkdir);
-EXPORT_SYMBOL(vfs_mknod);
EXPORT_SYMBOL(generic_permission);
EXPORT_SYMBOL(vfs_readlink);
-EXPORT_SYMBOL(vfs_rename);
-EXPORT_SYMBOL(vfs_rmdir);
-EXPORT_SYMBOL(vfs_symlink);
-EXPORT_SYMBOL(vfs_unlink);
EXPORT_SYMBOL(dentry_unhash);
EXPORT_SYMBOL(generic_readlink);
diff --git a/fs/namespace.c b/fs/namespace.c
index d0244c8..b015932 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -541,6 +541,7 @@
static void free_vfsmnt(struct mount *mnt)
{
+ kfree(mnt->mnt.data);
kfree(mnt->mnt_devname);
mnt_free_id(mnt);
#ifdef CONFIG_SMP
@@ -784,11 +785,21 @@
if (!mnt)
return ERR_PTR(-ENOMEM);
+ mnt->mnt.data = NULL;
+ if (type->alloc_mnt_data) {
+ mnt->mnt.data = type->alloc_mnt_data();
+ if (!mnt->mnt.data) {
+ mnt_free_id(mnt);
+ free_vfsmnt(mnt);
+ return ERR_PTR(-ENOMEM);
+ }
+ }
if (flags & MS_KERNMOUNT)
mnt->mnt.mnt_flags = MNT_INTERNAL;
- root = mount_fs(type, flags, name, data);
+ root = mount_fs(type, flags, name, &mnt->mnt, data);
if (IS_ERR(root)) {
+ kfree(mnt->mnt.data);
free_vfsmnt(mnt);
return ERR_CAST(root);
}
@@ -815,6 +826,14 @@
if (!mnt)
return ERR_PTR(-ENOMEM);
+ if (sb->s_op->clone_mnt_data) {
+ mnt->mnt.data = sb->s_op->clone_mnt_data(old->mnt.data);
+ if (!mnt->mnt.data) {
+ err = -ENOMEM;
+ goto out_free;
+ }
+ }
+
if (flag & (CL_SLAVE | CL_PRIVATE | CL_SHARED_TO_SLAVE))
mnt->mnt_group_id = 0; /* not a peer of original */
else
@@ -826,7 +845,7 @@
goto out_free;
}
- mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~MNT_WRITE_HOLD;
+ mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~(MNT_WRITE_HOLD|MNT_MARKED);
/* Don't allow unprivileged users to change mount flags */
if (flag & CL_UNPRIVILEGED) {
mnt->mnt.mnt_flags |= MNT_LOCK_ATIME;
@@ -878,6 +897,7 @@
return mnt;
out_free:
+ kfree(mnt->mnt.data);
free_vfsmnt(mnt);
return ERR_PTR(err);
}
@@ -1578,16 +1598,14 @@
err = invent_group_ids(source_mnt, true);
if (err)
goto out;
- }
- err = propagate_mnt(dest_mnt, dest_mp, source_mnt, &tree_list);
- if (err)
- goto out_cleanup_ids;
-
- br_write_lock(&vfsmount_lock);
-
- if (IS_MNT_SHARED(dest_mnt)) {
+ err = propagate_mnt(dest_mnt, dest_mp, source_mnt, &tree_list);
+ br_write_lock(&vfsmount_lock);
+ if (err)
+ goto out_cleanup_ids;
for (p = source_mnt; p; p = next_mnt(p, source_mnt))
set_mnt_shared(p);
+ } else {
+ br_write_lock(&vfsmount_lock);
}
if (parent_path) {
detach_mnt(source_mnt, parent_path);
@@ -1607,8 +1625,12 @@
return 0;
out_cleanup_ids:
- if (IS_MNT_SHARED(dest_mnt))
- cleanup_group_ids(source_mnt, NULL);
+ while (!list_empty(&tree_list)) {
+ child = list_first_entry(&tree_list, struct mount, mnt_hash);
+ umount_tree(child, 0);
+ }
+ br_write_unlock(&vfsmount_lock);
+ cleanup_group_ids(source_mnt, NULL);
out:
return err;
}
@@ -1849,8 +1871,14 @@
err = change_mount_flags(path->mnt, flags);
else if (!capable(CAP_SYS_ADMIN))
err = -EPERM;
- else
- err = do_remount_sb(sb, flags, data, 0);
+ else {
+ err = do_remount_sb2(path->mnt, sb, flags, data, 0);
+ namespace_lock();
+ br_write_lock(&vfsmount_lock);
+ propagate_remount(mnt);
+ br_write_unlock(&vfsmount_lock);
+ namespace_unlock();
+ }
if (!err) {
br_write_lock(&vfsmount_lock);
mnt_flags |= mnt->mnt.mnt_flags & ~MNT_USER_SETTABLE_MASK;
@@ -1975,7 +2003,7 @@
struct mount *parent;
int err;
- mnt_flags &= ~(MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL);
+ mnt_flags &= ~MNT_INTERNAL_FLAGS;
mp = lock_mount(path);
if (IS_ERR(mp))
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 9be6b41..92d31fa 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -490,7 +490,7 @@
}
/* you can only watch an inode if you have read permissions on it */
- ret = inode_permission(path->dentry->d_inode, MAY_READ);
+ ret = inode_permission2(path->mnt, path->dentry->d_inode, MAY_READ);
if (ret)
path_put(path);
out:
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c
index fce2b85..941e59d 100644
--- a/fs/notify/inotify/inotify_user.c
+++ b/fs/notify/inotify/inotify_user.c
@@ -349,7 +349,7 @@
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 = inode_permission2(path->mnt, path->dentry->d_inode, MAY_READ);
if (error)
path_put(path);
return error;
diff --git a/fs/open.c b/fs/open.c
index b1fd2c1..eec8b7f 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -34,8 +34,8 @@
#include "internal.h"
-int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
- struct file *filp)
+int do_truncate2(struct vfsmount *mnt, struct dentry *dentry, loff_t length,
+ unsigned int time_attrs, struct file *filp)
{
int ret;
struct iattr newattrs;
@@ -57,17 +57,24 @@
newattrs.ia_valid |= ret | ATTR_FORCE;
mutex_lock(&dentry->d_inode->i_mutex);
- ret = notify_change(dentry, &newattrs);
+ ret = notify_change2(mnt, dentry, &newattrs);
mutex_unlock(&dentry->d_inode->i_mutex);
return ret;
}
+int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
+ struct file *filp)
+{
+ return do_truncate2(NULL, dentry, length, time_attrs, filp);
+}
long vfs_truncate(struct path *path, loff_t length)
{
struct inode *inode;
+ struct vfsmount *mnt;
long error;
inode = path->dentry->d_inode;
+ mnt = path->mnt;
/* For directories it's -EISDIR, for other non-regulars - -EINVAL */
if (S_ISDIR(inode->i_mode))
@@ -79,7 +86,7 @@
if (error)
goto out;
- error = inode_permission(inode, MAY_WRITE);
+ error = inode_permission2(mnt, inode, MAY_WRITE);
if (error)
goto mnt_drop_write_and_out;
@@ -103,7 +110,7 @@
if (!error)
error = security_path_truncate(path);
if (!error)
- error = do_truncate(path->dentry, length, 0, NULL);
+ error = do_truncate2(mnt, path->dentry, length, 0, NULL);
put_write_and_out:
put_write_access(inode);
@@ -152,6 +159,7 @@
{
struct inode *inode;
struct dentry *dentry;
+ struct vfsmount *mnt;
struct fd f;
int error;
@@ -168,6 +176,7 @@
small = 0;
dentry = f.file->f_path.dentry;
+ mnt = f.file->f_path.mnt;
inode = dentry->d_inode;
error = -EINVAL;
if (!S_ISREG(inode->i_mode) || !(f.file->f_mode & FMODE_WRITE))
@@ -187,7 +196,7 @@
if (!error)
error = security_path_truncate(&f.file->f_path);
if (!error)
- error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, f.file);
+ error = do_truncate2(mnt, dentry, length, ATTR_MTIME|ATTR_CTIME, f.file);
sb_end_write(inode->i_sb);
out_putf:
fdput(f);
@@ -302,6 +311,7 @@
struct cred *override_cred;
struct path path;
struct inode *inode;
+ struct vfsmount *mnt;
int res;
unsigned int lookup_flags = LOOKUP_FOLLOW;
@@ -332,6 +342,7 @@
goto out;
inode = path.dentry->d_inode;
+ mnt = path.mnt;
if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) {
/*
@@ -343,7 +354,7 @@
goto out_path_release;
}
- res = inode_permission(inode, mode | MAY_ACCESS);
+ res = inode_permission2(mnt, inode, 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;
@@ -387,7 +398,7 @@
if (error)
goto out;
- error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
+ error = inode_permission2(path.mnt, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
if (error)
goto dput_and_out;
@@ -407,6 +418,7 @@
{
struct fd f = fdget_raw(fd);
struct inode *inode;
+ struct vfsmount *mnt;
int error = -EBADF;
error = -EBADF;
@@ -414,12 +426,13 @@
goto out;
inode = file_inode(f.file);
+ mnt = f.file->f_path.mnt;
error = -ENOTDIR;
if (!S_ISDIR(inode->i_mode))
goto out_putf;
- error = inode_permission(inode, MAY_EXEC | MAY_CHDIR);
+ error = inode_permission2(mnt, inode, MAY_EXEC | MAY_CHDIR);
if (!error)
set_fs_pwd(current->fs, &f.file->f_path);
out_putf:
@@ -438,7 +451,7 @@
if (error)
goto out;
- error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
+ error = inode_permission2(path.mnt, path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
if (error)
goto dput_and_out;
@@ -476,7 +489,7 @@
goto out_unlock;
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_change2(path->mnt, path->dentry, &newattrs);
out_unlock:
mutex_unlock(&inode->i_mutex);
mnt_drop_write(path->mnt);
@@ -550,7 +563,7 @@
mutex_lock(&inode->i_mutex);
error = security_path_chown(path, uid, gid);
if (!error)
- error = notify_change(path->dentry, &newattrs);
+ error = notify_change2(path->mnt, path->dentry, &newattrs);
mutex_unlock(&inode->i_mutex);
return error;
diff --git a/fs/pnode.c b/fs/pnode.c
index 9af0df1..e8d7d68 100644
--- a/fs/pnode.c
+++ b/fs/pnode.c
@@ -164,46 +164,94 @@
}
}
-/*
- * return the source mount to be used for cloning
- *
- * @dest the current destination mount
- * @last_dest the last seen destination mount
- * @last_src the last seen source mount
- * @type return CL_SLAVE if the new mount has to be
- * cloned as a slave.
- */
-static struct mount *get_source(struct mount *dest,
- struct mount *last_dest,
- struct mount *last_src,
- int *type)
+static struct mount *next_group(struct mount *m, struct mount *origin)
{
- struct mount *p_last_src = NULL;
- struct mount *p_last_dest = NULL;
-
- while (last_dest != dest->mnt_master) {
- p_last_dest = last_dest;
- p_last_src = last_src;
- last_dest = last_dest->mnt_master;
- last_src = last_src->mnt_master;
- }
-
- if (p_last_dest) {
- do {
- p_last_dest = next_peer(p_last_dest);
- } while (IS_MNT_NEW(p_last_dest));
- /* is that a peer of the earlier? */
- if (dest == p_last_dest) {
- *type = CL_MAKE_SHARED;
- return p_last_src;
+ while (1) {
+ while (1) {
+ struct mount *next;
+ if (!IS_MNT_NEW(m) && !list_empty(&m->mnt_slave_list))
+ return first_slave(m);
+ next = next_peer(m);
+ if (m->mnt_group_id == origin->mnt_group_id) {
+ if (next == origin)
+ return NULL;
+ } else if (m->mnt_slave.next != &next->mnt_slave)
+ break;
+ m = next;
}
+ /* m is the last peer */
+ while (1) {
+ struct mount *master = m->mnt_master;
+ if (m->mnt_slave.next != &master->mnt_slave_list)
+ return next_slave(m);
+ m = next_peer(master);
+ if (master->mnt_group_id == origin->mnt_group_id)
+ break;
+ if (master->mnt_slave.next == &m->mnt_slave)
+ break;
+ m = master;
+ }
+ if (m == origin)
+ return NULL;
}
- /* slave of the earlier, then */
- *type = CL_SLAVE;
- /* beginning of peer group among the slaves? */
- if (IS_MNT_SHARED(dest))
- *type |= CL_MAKE_SHARED;
- return last_src;
+}
+
+/* all accesses are serialized by namespace_sem */
+static struct user_namespace *user_ns;
+static struct mount *last_dest, *last_source, *dest_master;
+static struct mountpoint *mp;
+static struct list_head *list;
+
+static int propagate_one(struct mount *m)
+{
+ struct mount *child;
+ int type;
+ /* skip ones added by this propagate_mnt() */
+ if (IS_MNT_NEW(m))
+ return 0;
+ /* skip if mountpoint isn't covered by it */
+ if (!is_subdir(mp->m_dentry, m->mnt.mnt_root))
+ return 0;
+ if (m->mnt_group_id == last_dest->mnt_group_id) {
+ type = CL_MAKE_SHARED;
+ } else {
+ struct mount *n, *p;
+ for (n = m; ; n = p) {
+ p = n->mnt_master;
+ if (p == dest_master || IS_MNT_MARKED(p)) {
+ while (last_dest->mnt_master != p) {
+ last_source = last_source->mnt_master;
+ last_dest = last_source->mnt_parent;
+ }
+ if (n->mnt_group_id != last_dest->mnt_group_id) {
+ last_source = last_source->mnt_master;
+ last_dest = last_source->mnt_parent;
+ }
+ break;
+ }
+ }
+ type = CL_SLAVE;
+ /* beginning of peer group among the slaves? */
+ if (IS_MNT_SHARED(m))
+ type |= CL_MAKE_SHARED;
+ }
+
+ /* Notice when we are propagating across user namespaces */
+ if (m->mnt_ns->user_ns != user_ns)
+ type |= CL_UNPRIVILEGED;
+ child = copy_tree(last_source, last_source->mnt.mnt_root, type);
+ if (IS_ERR(child))
+ return PTR_ERR(child);
+ mnt_set_mountpoint(m, mp, child);
+ last_dest = m;
+ last_source = child;
+ if (m->mnt_master != dest_master) {
+ br_write_lock(&vfsmount_lock);
+ SET_MNT_MARK(m->mnt_master);
+ br_write_unlock(&vfsmount_lock);
+ }
+ list_add_tail(&child->mnt_hash, list);
+ return 0;
}
/*
@@ -222,52 +270,46 @@
int propagate_mnt(struct mount *dest_mnt, struct mountpoint *dest_mp,
struct mount *source_mnt, struct list_head *tree_list)
{
- struct user_namespace *user_ns = current->nsproxy->mnt_ns->user_ns;
- struct mount *m, *child;
+ struct mount *m, *n;
int ret = 0;
- struct mount *prev_dest_mnt = dest_mnt;
- struct mount *prev_src_mnt = source_mnt;
- LIST_HEAD(tmp_list);
- for (m = propagation_next(dest_mnt, dest_mnt); m;
- m = propagation_next(m, dest_mnt)) {
- int type;
- struct mount *source;
+ /*
+ * we don't want to bother passing tons of arguments to
+ * propagate_one(); everything is serialized by namespace_sem,
+ * so globals will do just fine.
+ */
+ user_ns = current->nsproxy->mnt_ns->user_ns;
+ last_dest = dest_mnt;
+ last_source = source_mnt;
+ mp = dest_mp;
+ list = tree_list;
+ dest_master = dest_mnt->mnt_master;
- if (IS_MNT_NEW(m))
- continue;
-
- source = get_source(m, prev_dest_mnt, prev_src_mnt, &type);
-
- /* Notice when we are propagating across user namespaces */
- if (m->mnt_ns->user_ns != user_ns)
- type |= CL_UNPRIVILEGED;
-
- child = copy_tree(source, source->mnt.mnt_root, type);
- if (IS_ERR(child)) {
- ret = PTR_ERR(child);
- list_splice(tree_list, tmp_list.prev);
+ /* all peers of dest_mnt, except dest_mnt itself */
+ for (n = next_peer(dest_mnt); n != dest_mnt; n = next_peer(n)) {
+ ret = propagate_one(n);
+ if (ret)
goto out;
- }
+ }
- if (is_subdir(dest_mp->m_dentry, m->mnt.mnt_root)) {
- mnt_set_mountpoint(m, dest_mp, child);
- list_add_tail(&child->mnt_hash, tree_list);
- } else {
- /*
- * This can happen if the parent mount was bind mounted
- * on some subdirectory of a shared/slave mount.
- */
- list_add_tail(&child->mnt_hash, &tmp_list);
- }
- prev_dest_mnt = m;
- prev_src_mnt = child;
+ /* all slave groups */
+ for (m = next_group(dest_mnt, dest_mnt); m;
+ m = next_group(m, dest_mnt)) {
+ /* everything in that slave group */
+ n = m;
+ do {
+ ret = propagate_one(n);
+ if (ret)
+ goto out;
+ n = next_peer(n);
+ } while (n != m);
}
out:
br_write_lock(&vfsmount_lock);
- while (!list_empty(&tmp_list)) {
- child = list_first_entry(&tmp_list, struct mount, mnt_hash);
- umount_tree(child, 0);
+ list_for_each_entry(n, tree_list, mnt_hash) {
+ m = n->mnt_parent;
+ if (m->mnt_master != dest_mnt->mnt_master)
+ CLEAR_MNT_MARK(m->mnt_master);
}
br_write_unlock(&vfsmount_lock);
return ret;
@@ -359,3 +401,37 @@
__propagate_umount(mnt);
return 0;
}
+
+/*
+ * Iterates over all slaves, and slaves of slaves.
+ */
+static struct mount *next_descendent(struct mount *root, struct mount *cur)
+{
+ if (!IS_MNT_NEW(cur) && !list_empty(&cur->mnt_slave_list))
+ return first_slave(cur);
+ do {
+ struct mount *master = cur->mnt_master;
+
+ if (!master || cur->mnt_slave.next != &master->mnt_slave_list) {
+ struct mount *next = next_slave(cur);
+
+ return (next == root) ? NULL : next;
+ }
+ cur = master;
+ } while (cur != root);
+ return NULL;
+}
+
+void propagate_remount(struct mount *mnt)
+{
+ struct mount *m = mnt;
+ struct super_block *sb = mnt->mnt.mnt_sb;
+
+ if (sb->s_op->copy_mnt_data) {
+ m = next_descendent(mnt, m);
+ while (m) {
+ sb->s_op->copy_mnt_data(m->mnt.data, mnt->mnt.data);
+ m = next_descendent(mnt, m);
+ }
+ }
+}
diff --git a/fs/pnode.h b/fs/pnode.h
index b091445..e16e597 100644
--- a/fs/pnode.h
+++ b/fs/pnode.h
@@ -16,6 +16,9 @@
#define IS_MNT_NEW(m) (!(m)->mnt_ns)
#define CLEAR_MNT_SHARED(m) ((m)->mnt.mnt_flags &= ~MNT_SHARED)
#define IS_MNT_UNBINDABLE(m) ((m)->mnt.mnt_flags & MNT_UNBINDABLE)
+#define IS_MNT_MARKED(m) ((m)->mnt.mnt_flags & MNT_MARKED)
+#define SET_MNT_MARK(m) ((m)->mnt.mnt_flags |= MNT_MARKED)
+#define CLEAR_MNT_MARK(m) ((m)->mnt.mnt_flags &= ~MNT_MARKED)
#define CL_EXPIRE 0x01
#define CL_SLAVE 0x02
@@ -36,6 +39,7 @@
struct list_head *);
int propagate_umount(struct list_head *);
int propagate_mount_busy(struct mount *, int);
+void propagate_remount(struct mount *);
void mnt_release_group_id(struct mount *);
int get_dominating_id(struct mount *mnt, const struct path *root);
unsigned int mnt_get_count(struct mount *mnt);
diff --git a/fs/proc/array.c b/fs/proc/array.c
index 6ed9580..c1b1232 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -312,7 +312,8 @@
static inline void task_cap(struct seq_file *m, struct task_struct *p)
{
const struct cred *cred;
- kernel_cap_t cap_inheritable, cap_permitted, cap_effective, cap_bset;
+ kernel_cap_t cap_inheritable, cap_permitted, cap_effective,
+ cap_bset, cap_ambient;
rcu_read_lock();
cred = __task_cred(p);
@@ -320,12 +321,14 @@
cap_permitted = cred->cap_permitted;
cap_effective = cred->cap_effective;
cap_bset = cred->cap_bset;
+ cap_ambient = cred->cap_ambient;
rcu_read_unlock();
render_cap_t(m, "CapInh:\t", &cap_inheritable);
render_cap_t(m, "CapPrm:\t", &cap_permitted);
render_cap_t(m, "CapEff:\t", &cap_effective);
render_cap_t(m, "CapBnd:\t", &cap_bset);
+ render_cap_t(m, "CapAmb:\t", &cap_ambient);
}
static inline void task_seccomp(struct seq_file *m, struct task_struct *p)
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 7ce45b9..8c2514a 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -3245,6 +3245,44 @@
}
/*
+ * proc_tid_comm_permission is a special permission function exclusively
+ * used for the node /proc/<pid>/task/<tid>/comm.
+ * It bypasses generic permission checks in the case where a task of the same
+ * task group attempts to access the node.
+ * The rational behind this is that glibc and bionic access this node for
+ * cross thread naming (pthread_set/getname_np(!self)). However, if
+ * PR_SET_DUMPABLE gets set to 0 this node among others becomes uid=0 gid=0,
+ * which locks out the cross thread naming implementation.
+ * This function makes sure that the node is always accessible for members of
+ * same thread group.
+ */
+static int proc_tid_comm_permission(struct inode *inode, int mask)
+{
+ bool is_same_tgroup;
+ struct task_struct *task;
+
+ task = get_proc_task(inode);
+ if (!task)
+ return -ESRCH;
+ is_same_tgroup = same_thread_group(current, task);
+ put_task_struct(task);
+
+ if (likely(is_same_tgroup && !(mask & MAY_EXEC))) {
+ /* This file (/proc/<pid>/task/<tid>/comm) can always be
+ * read or written by the members of the corresponding
+ * thread group.
+ */
+ return 0;
+ }
+
+ return generic_permission(inode, mask);
+}
+
+static const struct inode_operations proc_tid_comm_inode_operations = {
+ .permission = proc_tid_comm_permission,
+};
+
+/*
* Tasks
*/
static const struct pid_entry tid_base_stuff[] = {
@@ -3265,7 +3303,9 @@
#ifdef CONFIG_TASK_CPUFREQ_STATS
REG("cpufreq_stats", S_IRUGO|S_IWUSR, proc_pid_cpufreq_stats_operations),
#endif
- REG("comm", S_IRUGO|S_IWUSR, proc_pid_set_comm_operations),
+ NOD("comm", S_IFREG|S_IRUGO|S_IWUSR,
+ &proc_tid_comm_inode_operations,
+ &proc_pid_set_comm_operations, {}),
#ifdef CONFIG_HAVE_ARCH_TRACEHOOK
INF("syscall", S_IRUGO, proc_pid_syscall),
#endif
diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c
index 5fe34c3..9243966 100644
--- a/fs/proc_namespace.c
+++ b/fs/proc_namespace.c
@@ -112,7 +112,9 @@
if (err)
goto out;
show_mnt_opts(m, mnt);
- if (sb->s_op->show_options)
+ if (sb->s_op->show_options2)
+ err = sb->s_op->show_options2(mnt, m, mnt_path.dentry);
+ else if (sb->s_op->show_options)
err = sb->s_op->show_options(m, mnt_path.dentry);
seq_puts(m, " 0 0\n");
out:
@@ -173,7 +175,9 @@
err = show_sb_opts(m, sb);
if (err)
goto out;
- if (sb->s_op->show_options)
+ if (sb->s_op->show_options2) {
+ err = sb->s_op->show_options2(mnt, m, mnt->mnt_root);
+ } else if (sb->s_op->show_options)
err = sb->s_op->show_options(m, mnt->mnt_root);
seq_putc(m, '\n');
out:
diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c
index b2fa36f..c8f3bcb 100755
--- a/fs/sdcardfs/dentry.c
+++ b/fs/sdcardfs/dentry.c
@@ -34,6 +34,8 @@
struct dentry *parent_lower_dentry = NULL;
struct dentry *lower_cur_parent_dentry = NULL;
struct dentry *lower_dentry = NULL;
+ struct inode *inode;
+ struct sdcardfs_inode_data *data;
if (flags & LOOKUP_RCU)
return -ECHILD;
@@ -46,7 +48,8 @@
spin_unlock(&dentry->d_lock);
/* check uninitialized obb_dentry and
- * whether the base obbpath has been changed or not */
+ * whether the base obbpath has been changed or not
+ */
if (is_obbpath_invalid(dentry)) {
d_drop(dentry);
return 0;
@@ -59,6 +62,14 @@
lower_dentry = lower_path.dentry;
lower_cur_parent_dentry = dget_parent(lower_dentry);
+ if ((lower_dentry->d_flags & DCACHE_OP_REVALIDATE)) {
+ err = lower_dentry->d_op->d_revalidate(lower_dentry, flags);
+ if (err == 0) {
+ d_drop(dentry);
+ goto out;
+ }
+ }
+
spin_lock(&lower_dentry->d_lock);
if (d_unhashed(lower_dentry)) {
spin_unlock(&lower_dentry->d_lock);
@@ -76,17 +87,13 @@
if (dentry < lower_dentry) {
spin_lock(&dentry->d_lock);
- spin_lock(&lower_dentry->d_lock);
+ spin_lock_nested(&lower_dentry->d_lock, DENTRY_D_LOCK_NESTED);
} else {
spin_lock(&lower_dentry->d_lock);
- spin_lock(&dentry->d_lock);
+ spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
}
- if (dentry->d_name.len != lower_dentry->d_name.len) {
- __d_drop(dentry);
- err = 0;
- } else if (strncasecmp(dentry->d_name.name, lower_dentry->d_name.name,
- dentry->d_name.len) != 0) {
+ if (!qstr_case_eq(&dentry->d_name, &lower_dentry->d_name)) {
__d_drop(dentry);
err = 0;
}
@@ -98,6 +105,21 @@
spin_unlock(&dentry->d_lock);
spin_unlock(&lower_dentry->d_lock);
}
+ if (!err)
+ goto out;
+
+ /* If our top's inode is gone, we may be out of date */
+ inode = igrab(dentry->d_inode);
+ if (inode) {
+ data = top_data_get(SDCARDFS_I(inode));
+ if (!data || data->abandoned) {
+ d_drop(dentry);
+ err = 0;
+ }
+ if (data)
+ data_put(data);
+ iput(inode);
+ }
out:
dput(parent_dentry);
@@ -110,12 +132,10 @@
static void sdcardfs_d_release(struct dentry *dentry)
{
/* release and reset the lower paths */
- if(has_graft_path(dentry)) {
+ if (has_graft_path(dentry))
sdcardfs_put_reset_orig_path(dentry);
- }
sdcardfs_put_reset_lower_path(dentry);
free_dentry_private_data(dentry);
- return;
}
static int sdcardfs_hash_ci(const struct dentry *dentry,
@@ -132,12 +152,10 @@
unsigned long hash;
name = qstr->name;
- //len = vfat_striptail_len(qstr);
len = qstr->len;
hash = init_name_hash();
while (len--)
- //hash = partial_name_hash(nls_tolower(t, *name++), hash);
hash = partial_name_hash(tolower(*name++), hash);
qstr->hash = end_name_hash(hash);
@@ -152,35 +170,25 @@
const struct dentry *dentry, const struct inode *inode,
unsigned int len, const char *str, const struct qstr *name)
{
- /* This function is copy of vfat_cmpi */
- // FIXME Should we support national language?
- //struct nls_table *t = MSDOS_SB(parent->d_sb)->nls_io;
- //unsigned int alen, blen;
+ /* FIXME Should we support national language? */
- /* A filename cannot end in '.' or we treat it like it has none */
- /*
- alen = vfat_striptail_len(name);
- blen = __vfat_striptail_len(len, str);
- if (alen == blen) {
- if (nls_strnicmp(t, name->name, str, alen) == 0)
- return 0;
- }
- */
if (name->len == len) {
- if (strncasecmp(name->name, str, len) == 0)
+ if (str_n_case_eq(name->name, str, len))
return 0;
}
return 1;
}
-static void sdcardfs_canonical_path(const struct path *path, struct path *actual_path) {
+static void sdcardfs_canonical_path(const struct path *path,
+ struct path *actual_path)
+{
sdcardfs_get_real_lower(path->dentry, actual_path);
}
const struct dentry_operations sdcardfs_ci_dops = {
.d_revalidate = sdcardfs_d_revalidate,
.d_release = sdcardfs_d_release,
- .d_hash = sdcardfs_hash_ci,
+ .d_hash = sdcardfs_hash_ci,
.d_compare = sdcardfs_cmp_ci,
.d_canonical_path = sdcardfs_canonical_path,
};
diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c
index 9de45bc..8a42e01 100755
--- a/fs/sdcardfs/derived_perm.c
+++ b/fs/sdcardfs/derived_perm.c
@@ -26,100 +26,295 @@
struct sdcardfs_inode_info *pi = SDCARDFS_I(parent);
struct sdcardfs_inode_info *ci = SDCARDFS_I(child);
- ci->perm = PERM_INHERIT;
- ci->userid = pi->userid;
- ci->d_uid = pi->d_uid;
- ci->under_android = pi->under_android;
+ ci->data->perm = PERM_INHERIT;
+ ci->data->userid = pi->data->userid;
+ ci->data->d_uid = pi->data->d_uid;
+ ci->data->under_android = pi->data->under_android;
+ ci->data->under_cache = pi->data->under_cache;
+ ci->data->under_obb = pi->data->under_obb;
+ set_top(ci, pi->top_data);
}
/* helper function for derived state */
-void setup_derived_state(struct inode *inode, perm_t perm,
- userid_t userid, uid_t uid, bool under_android)
+void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid,
+ uid_t uid, bool under_android,
+ struct sdcardfs_inode_data *top)
{
struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
- info->perm = perm;
- info->userid = userid;
- info->d_uid = uid;
- info->under_android = under_android;
+ info->data->perm = perm;
+ info->data->userid = userid;
+ info->data->d_uid = uid;
+ info->data->under_android = under_android;
+ info->data->under_cache = false;
+ info->data->under_obb = false;
+ set_top(info, top);
}
-/* While renaming, there is a point where we want the path from dentry, but the name from newdentry */
-void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry)
+/* While renaming, there is a point where we want the path from dentry,
+ * but the name from newdentry
+ */
+void get_derived_permission_new(struct dentry *parent, struct dentry *dentry,
+ const struct qstr *name)
{
- struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
struct sdcardfs_inode_info *info = SDCARDFS_I(dentry->d_inode);
- struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode);
+ struct sdcardfs_inode_data *parent_data =
+ SDCARDFS_I(parent->d_inode)->data;
appid_t appid;
+ unsigned long user_num;
+ int err;
+ struct qstr q_Android = QSTR_LITERAL("Android");
+ struct qstr q_data = QSTR_LITERAL("data");
+ struct qstr q_obb = QSTR_LITERAL("obb");
+ struct qstr q_media = QSTR_LITERAL("media");
+ struct qstr q_cache = QSTR_LITERAL("cache");
/* By default, each inode inherits from its parent.
* the properties are maintained on its private fields
* because the inode attributes will be modified with that of
* its lower inode.
- * The derived state will be updated on the last
- * stage of each system call by fix_derived_permission(inode).
+ * These values are used by our custom permission call instead
+ * of using the inode permissions.
*/
inherit_derived_state(parent->d_inode, dentry->d_inode);
+ /* Files don't get special labels */
+ if (!S_ISDIR(dentry->d_inode->i_mode))
+ return;
/* Derive custom permissions based on parent and current node */
- switch (parent_info->perm) {
- case PERM_INHERIT:
- /* Already inherited above */
- break;
- case PERM_PRE_ROOT:
- /* Legacy internal layout places users at top level */
- info->perm = PERM_ROOT;
- info->userid = simple_strtoul(newdentry->d_name.name, NULL, 10);
- break;
- case PERM_ROOT:
- /* Assume masked off by default. */
- if (!strcasecmp(newdentry->d_name.name, "Android")) {
- /* App-specific directories inside; let anyone traverse */
- info->perm = PERM_ANDROID;
- info->under_android = true;
- }
- break;
- case PERM_ANDROID:
- if (!strcasecmp(newdentry->d_name.name, "data")) {
- /* App-specific directories inside; let anyone traverse */
- info->perm = PERM_ANDROID_DATA;
- } else if (!strcasecmp(newdentry->d_name.name, "obb")) {
- /* App-specific directories inside; let anyone traverse */
- info->perm = PERM_ANDROID_OBB;
- /* Single OBB directory is always shared */
- } else if (!strcasecmp(newdentry->d_name.name, "media")) {
- /* App-specific directories inside; let anyone traverse */
- info->perm = PERM_ANDROID_MEDIA;
- }
- break;
- case PERM_ANDROID_DATA:
- case PERM_ANDROID_OBB:
- case PERM_ANDROID_MEDIA:
- appid = get_appid(sbi->pkgl_id, newdentry->d_name.name);
- if (appid != 0) {
- info->d_uid = multiuser_get_uid(parent_info->userid, appid);
- }
- break;
+ switch (parent_data->perm) {
+ case PERM_INHERIT:
+ case PERM_ANDROID_PACKAGE_CACHE:
+ /* Already inherited above */
+ break;
+ case PERM_PRE_ROOT:
+ /* Legacy internal layout places users at top level */
+ info->data->perm = PERM_ROOT;
+ err = kstrtoul(name->name, 10, &user_num);
+ if (err)
+ info->data->userid = 0;
+ else
+ info->data->userid = user_num;
+ set_top(info, info->data);
+ break;
+ case PERM_ROOT:
+ /* Assume masked off by default. */
+ if (qstr_case_eq(name, &q_Android)) {
+ /* App-specific directories inside; let anyone traverse */
+ info->data->perm = PERM_ANDROID;
+ info->data->under_android = true;
+ set_top(info, info->data);
+ }
+ break;
+ case PERM_ANDROID:
+ if (qstr_case_eq(name, &q_data)) {
+ /* App-specific directories inside; let anyone traverse */
+ info->data->perm = PERM_ANDROID_DATA;
+ set_top(info, info->data);
+ } else if (qstr_case_eq(name, &q_obb)) {
+ /* App-specific directories inside; let anyone traverse */
+ info->data->perm = PERM_ANDROID_OBB;
+ info->data->under_obb = true;
+ set_top(info, info->data);
+ /* Single OBB directory is always shared */
+ } else if (qstr_case_eq(name, &q_media)) {
+ /* App-specific directories inside; let anyone traverse */
+ info->data->perm = PERM_ANDROID_MEDIA;
+ set_top(info, info->data);
+ }
+ break;
+ case PERM_ANDROID_OBB:
+ case PERM_ANDROID_DATA:
+ case PERM_ANDROID_MEDIA:
+ info->data->perm = PERM_ANDROID_PACKAGE;
+ appid = get_appid(name->name);
+ if (appid != 0 && !is_excluded(name->name, parent_data->userid))
+ info->data->d_uid =
+ multiuser_get_uid(parent_data->userid, appid);
+ set_top(info, info->data);
+ break;
+ case PERM_ANDROID_PACKAGE:
+ if (qstr_case_eq(name, &q_cache)) {
+ info->data->perm = PERM_ANDROID_PACKAGE_CACHE;
+ info->data->under_cache = true;
+ }
+ break;
}
}
void get_derived_permission(struct dentry *parent, struct dentry *dentry)
{
- get_derived_permission_new(parent, dentry, dentry);
+ get_derived_permission_new(parent, dentry, &dentry->d_name);
}
-void get_derive_permissions_recursive(struct dentry *parent) {
- struct dentry *dentry;
- list_for_each_entry(dentry, &parent->d_subdirs, d_u.d_child) {
- if (dentry && dentry->d_inode) {
- mutex_lock(&dentry->d_inode->i_mutex);
- get_derived_permission(parent, dentry);
- fix_derived_permission(dentry->d_inode);
- get_derive_permissions_recursive(dentry);
- mutex_unlock(&dentry->d_inode->i_mutex);
+static appid_t get_type(const char *name)
+{
+ const char *ext = strrchr(name, '.');
+ appid_t id;
+
+ if (ext && ext[0]) {
+ ext = &ext[1];
+ id = get_ext_gid(ext);
+ return id?:AID_MEDIA_RW;
+ }
+ return AID_MEDIA_RW;
+}
+
+void fixup_lower_ownership(struct dentry *dentry, const char *name)
+{
+ struct path path;
+ struct inode *inode;
+ int error;
+ struct sdcardfs_inode_info *info;
+ struct sdcardfs_inode_data *info_d;
+ struct sdcardfs_inode_data *info_top;
+ perm_t perm;
+ struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
+ uid_t uid = sbi->options.fs_low_uid;
+ gid_t gid = sbi->options.fs_low_gid;
+ struct iattr newattrs;
+
+ info = SDCARDFS_I(dentry->d_inode);
+ info_d = info->data;
+ perm = info_d->perm;
+ if (info_d->under_obb) {
+ perm = PERM_ANDROID_OBB;
+ } else if (info_d->under_cache) {
+ perm = PERM_ANDROID_PACKAGE_CACHE;
+ } else if (perm == PERM_INHERIT) {
+ info_top = top_data_get(info);
+ perm = info_top->perm;
+ data_put(info_top);
+ }
+
+ switch (perm) {
+ case PERM_ROOT:
+ case PERM_ANDROID:
+ case PERM_ANDROID_DATA:
+ case PERM_ANDROID_MEDIA:
+ case PERM_ANDROID_PACKAGE:
+ case PERM_ANDROID_PACKAGE_CACHE:
+ uid = multiuser_get_uid(info_d->userid, uid);
+ break;
+ case PERM_ANDROID_OBB:
+ uid = AID_MEDIA_OBB;
+ break;
+ case PERM_PRE_ROOT:
+ default:
+ break;
+ }
+ switch (perm) {
+ case PERM_ROOT:
+ case PERM_ANDROID:
+ case PERM_ANDROID_DATA:
+ case PERM_ANDROID_MEDIA:
+ if (S_ISDIR(dentry->d_inode->i_mode))
+ gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW);
+ else
+ gid = multiuser_get_uid(info_d->userid, get_type(name));
+ break;
+ case PERM_ANDROID_OBB:
+ gid = AID_MEDIA_OBB;
+ break;
+ case PERM_ANDROID_PACKAGE:
+ if (uid_is_app(info_d->d_uid))
+ gid = multiuser_get_ext_gid(info_d->d_uid);
+ else
+ gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW);
+ break;
+ case PERM_ANDROID_PACKAGE_CACHE:
+ if (uid_is_app(info_d->d_uid))
+ gid = multiuser_get_ext_cache_gid(info_d->d_uid);
+ else
+ gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW);
+ break;
+ case PERM_PRE_ROOT:
+ default:
+ break;
+ }
+
+ sdcardfs_get_lower_path(dentry, &path);
+ inode = path.dentry->d_inode;
+ if (path.dentry->d_inode->i_gid != gid || path.dentry->d_inode->i_uid != uid) {
+ newattrs.ia_valid = ATTR_GID | ATTR_UID | ATTR_FORCE;
+ newattrs.ia_uid = make_kuid(current_user_ns(), uid);
+ newattrs.ia_gid = make_kgid(current_user_ns(), gid);
+ if (!S_ISDIR(inode->i_mode))
+ newattrs.ia_valid |=
+ ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
+ mutex_lock(&inode->i_mutex);
+ error = security_path_chown(&path, newattrs.ia_uid, newattrs.ia_gid);
+ if (!error)
+ error = notify_change2(path.mnt, path.dentry, &newattrs);
+ mutex_unlock(&inode->i_mutex);
+ if (error)
+ pr_debug("sdcardfs: Failed to touch up lower fs gid/uid for %s\n", name);
+ }
+ sdcardfs_put_lower_path(dentry, &path);
+}
+
+static int descendant_may_need_fixup(struct sdcardfs_inode_data *data,
+ struct limit_search *limit)
+{
+ if (data->perm == PERM_ROOT)
+ return (limit->flags & BY_USERID) ?
+ data->userid == limit->userid : 1;
+ if (data->perm == PERM_PRE_ROOT || data->perm == PERM_ANDROID)
+ return 1;
+ return 0;
+}
+
+static int needs_fixup(perm_t perm)
+{
+ if (perm == PERM_ANDROID_DATA || perm == PERM_ANDROID_OBB
+ || perm == PERM_ANDROID_MEDIA)
+ return 1;
+ return 0;
+}
+
+static void __fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit, int depth)
+{
+ struct dentry *child;
+ struct sdcardfs_inode_info *info;
+
+ /*
+ * All paths will terminate their recursion on hitting PERM_ANDROID_OBB,
+ * PERM_ANDROID_MEDIA, or PERM_ANDROID_DATA. This happens at a depth of
+ * at most 3.
+ */
+ WARN(depth > 3, "%s: Max expected depth exceeded!\n", __func__);
+ spin_lock_nested(&dentry->d_lock, depth);
+ if (!dentry->d_inode) {
+ spin_unlock(&dentry->d_lock);
+ return;
+ }
+ info = SDCARDFS_I(dentry->d_inode);
+
+ if (needs_fixup(info->data->perm)) {
+ list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) {
+ spin_lock_nested(&child->d_lock, depth + 1);
+ if (!(limit->flags & BY_NAME) || qstr_case_eq(&child->d_name, &limit->name)) {
+ if (child->d_inode) {
+ get_derived_permission(dentry, child);
+ fixup_tmp_permissions(child->d_inode);
+ spin_unlock(&child->d_lock);
+ break;
+ }
+ }
+ spin_unlock(&child->d_lock);
+ }
+ } else if (descendant_may_need_fixup(info->data, limit)) {
+ list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) {
+ __fixup_perms_recursive(child, limit, depth + 1);
}
}
+ spin_unlock(&dentry->d_lock);
+}
+
+void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit)
+{
+ __fixup_perms_recursive(dentry, limit, 0);
}
/* main function for updating derived permission */
@@ -127,41 +322,38 @@
{
struct dentry *parent;
- if(!dentry || !dentry->d_inode) {
- printk(KERN_ERR "sdcardfs: %s: invalid dentry\n", __func__);
+ if (!dentry || !dentry->d_inode) {
+ pr_err("sdcardfs: %s: invalid dentry\n", __func__);
return;
}
/* FIXME:
* 1. need to check whether the dentry is updated or not
* 2. remove the root dentry update
*/
- mutex_lock(&dentry->d_inode->i_mutex);
- if(IS_ROOT(dentry)) {
- //setup_default_pre_root_state(dentry->d_inode);
- } else {
+ if (!IS_ROOT(dentry)) {
parent = dget_parent(dentry);
- if(parent) {
+ if (parent) {
get_derived_permission(parent, dentry);
dput(parent);
}
}
- fix_derived_permission(dentry->d_inode);
- mutex_unlock(&dentry->d_inode->i_mutex);
+ fixup_tmp_permissions(dentry->d_inode);
}
int need_graft_path(struct dentry *dentry)
{
int ret = 0;
struct dentry *parent = dget_parent(dentry);
- struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode);
+ struct sdcardfs_inode_info *parent_info = SDCARDFS_I(parent->d_inode);
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
+ struct qstr obb = QSTR_LITERAL("obb");
- if(parent_info->perm == PERM_ANDROID &&
- !strcasecmp(dentry->d_name.name, "obb")) {
+ if (parent_info->data->perm == PERM_ANDROID &&
+ qstr_case_eq(&dentry->d_name, &obb)) {
/* /Android/obb is the base obbpath of DERIVED_UNIFIED */
- if(!(sbi->options.multiuser == false
- && parent_info->userid == 0)) {
+ if (!(sbi->options.multiuser == false
+ && parent_info->data->userid == 0)) {
ret = 1;
}
}
@@ -175,36 +367,40 @@
struct sdcardfs_dentry_info *di = SDCARDFS_D(dent);
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dent->d_sb);
char *path_buf, *obbpath_s;
+ int need_put = 0;
+ struct path lower_path;
/* check the base obbpath has been changed.
* this routine can check an uninitialized obb dentry as well.
- * regarding the uninitialized obb, refer to the sdcardfs_mkdir() */
+ * regarding the uninitialized obb, refer to the sdcardfs_mkdir()
+ */
spin_lock(&di->lock);
- if(di->orig_path.dentry) {
- if(!di->lower_path.dentry) {
+ if (di->orig_path.dentry) {
+ if (!di->lower_path.dentry) {
ret = 1;
} else {
path_get(&di->lower_path);
- //lower_parent = lock_parent(lower_path->dentry);
path_buf = kmalloc(PATH_MAX, GFP_ATOMIC);
- if(!path_buf) {
+ if (!path_buf) {
ret = 1;
- printk(KERN_ERR "sdcardfs: fail to allocate path_buf in %s.\n", __func__);
+ pr_err("sdcardfs: fail to allocate path_buf in %s.\n", __func__);
} else {
obbpath_s = d_path(&di->lower_path, path_buf, PATH_MAX);
if (d_unhashed(di->lower_path.dentry) ||
- strcasecmp(sbi->obbpath_s, obbpath_s)) {
+ !str_case_eq(sbi->obbpath_s, obbpath_s)) {
ret = 1;
}
kfree(path_buf);
}
- //unlock_dir(lower_parent);
- path_put(&di->lower_path);
+ pathcpy(&lower_path, &di->lower_path);
+ need_put = 1;
}
}
spin_unlock(&di->lock);
+ if (need_put)
+ path_put(&lower_path);
return ret;
}
@@ -212,17 +408,18 @@
{
int ret = 0;
struct dentry *parent = dget_parent(dentry);
- struct sdcardfs_inode_info *parent_info= SDCARDFS_I(parent->d_inode);
+ struct sdcardfs_inode_info *parent_info = SDCARDFS_I(parent->d_inode);
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
+ struct qstr q_obb = QSTR_LITERAL("obb");
spin_lock(&SDCARDFS_D(dentry)->lock);
if (sbi->options.multiuser) {
- if(parent_info->perm == PERM_PRE_ROOT &&
- !strcasecmp(dentry->d_name.name, "obb")) {
+ if (parent_info->data->perm == PERM_PRE_ROOT &&
+ qstr_case_eq(&dentry->d_name, &q_obb)) {
ret = 1;
}
- } else if (parent_info->perm == PERM_ANDROID &&
- !strcasecmp(dentry->d_name.name, "obb")) {
+ } else if (parent_info->data->perm == PERM_ANDROID &&
+ qstr_case_eq(&dentry->d_name, &q_obb)) {
ret = 1;
}
spin_unlock(&SDCARDFS_D(dentry)->lock);
@@ -232,7 +429,8 @@
/* The lower_path will be stored to the dentry's orig_path
* and the base obbpath will be copyed to the lower_path variable.
* if an error returned, there's no change in the lower_path
- * returns: -ERRNO if error (0: no error) */
+ * returns: -ERRNO if error (0: no error)
+ */
int setup_obb_dentry(struct dentry *dentry, struct path *lower_path)
{
int err = 0;
@@ -241,23 +439,24 @@
/* A local obb dentry must have its own orig_path to support rmdir
* and mkdir of itself. Usually, we expect that the sbi->obbpath
- * is avaiable on this stage. */
+ * is avaiable on this stage.
+ */
sdcardfs_set_orig_path(dentry, lower_path);
err = kern_path(sbi->obbpath_s,
LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &obbpath);
- if(!err) {
+ if (!err) {
/* the obbpath base has been found */
- printk(KERN_INFO "sdcardfs: the sbi->obbpath is found\n");
pathcpy(lower_path, &obbpath);
} else {
/* if the sbi->obbpath is not available, we can optionally
* setup the lower_path with its orig_path.
* but, the current implementation just returns an error
* because the sdcard daemon also regards this case as
- * a lookup fail. */
- printk(KERN_INFO "sdcardfs: the sbi->obbpath is not available\n");
+ * a lookup fail.
+ */
+ pr_info("sdcardfs: the sbi->obbpath is not available\n");
}
return err;
}
diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c
index 369051e..8943bef 100755
--- a/fs/sdcardfs/file.c
+++ b/fs/sdcardfs/file.c
@@ -65,7 +65,7 @@
/* check disk space */
if (!check_min_free_space(dentry, count, 0)) {
- printk(KERN_INFO "No minimum free space.\n");
+ pr_err("No minimum free space.\n");
return -ENOSPC;
}
@@ -113,6 +113,10 @@
if (lower_file->f_op->unlocked_ioctl)
err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg);
+ /* some ioctls can change inode attributes (EXT2_IOC_SETFLAGS) */
+ if (!err)
+ sdcardfs_copy_and_fix_attrs(file_inode(file),
+ file_inode(lower_file));
out:
return err;
}
@@ -160,8 +164,7 @@
lower_file = sdcardfs_lower_file(file);
if (willwrite && !lower_file->f_mapping->a_ops->writepage) {
err = -EINVAL;
- printk(KERN_ERR "sdcardfs: lower file system does not "
- "support writeable mmap\n");
+ pr_err("sdcardfs: lower file system does not support writeable mmap\n");
goto out;
}
@@ -173,16 +176,10 @@
if (!SDCARDFS_F(file)->lower_vm_ops) {
err = lower_file->f_op->mmap(lower_file, vma);
if (err) {
- printk(KERN_ERR "sdcardfs: lower mmap failed %d\n", err);
+ pr_err("sdcardfs: lower mmap failed %d\n", err);
goto out;
}
saved_vm_ops = vma->vm_ops; /* save: came from lower ->mmap */
- err = do_munmap(current->mm, vma->vm_start,
- vma->vm_end - vma->vm_start);
- if (err) {
- printk(KERN_ERR "sdcardfs: do_munmap failed %d\n", err);
- goto out;
- }
}
/*
@@ -195,6 +192,9 @@
file->f_mapping->a_ops = &sdcardfs_aops; /* set our aops */
if (!SDCARDFS_F(file)->lower_vm_ops) /* save for our ->fault */
SDCARDFS_F(file)->lower_vm_ops = saved_vm_ops;
+ vma->vm_private_data = file;
+ get_file(lower_file);
+ vma->vm_file = lower_file;
out:
return err;
@@ -216,16 +216,13 @@
goto out_err;
}
- if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
- printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
- " dentry: %s, task:%s\n",
- __func__, dentry->d_name.name, current->comm);
+ if (!check_caller_access_to_name(parent->d_inode, &dentry->d_name)) {
err = -EACCES;
goto out_err;
}
/* save current_cred and override it */
- OVERRIDE_CRED(sbi, saved_cred);
+ OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(inode));
file->private_data =
kzalloc(sizeof(struct sdcardfs_file_info), GFP_KERNEL);
@@ -251,9 +248,8 @@
if (err)
kfree(SDCARDFS_F(file));
- else {
+ else
sdcardfs_copy_and_fix_attrs(inode, sdcardfs_lower_inode(inode));
- }
out_revert_cred:
REVERT_CRED(saved_cred);
@@ -321,6 +317,29 @@
return err;
}
+/*
+ * Sdcardfs cannot use generic_file_llseek as ->llseek, because it would
+ * only set the offset of the upper file. So we have to implement our
+ * own method to set both the upper and lower file offsets
+ * consistently.
+ */
+static loff_t sdcardfs_file_llseek(struct file *file, loff_t offset, int whence)
+{
+ int err;
+ struct file *lower_file;
+
+ err = generic_file_llseek(file, offset, whence);
+ if (err < 0)
+ goto out;
+
+ lower_file = sdcardfs_lower_file(file);
+ err = generic_file_llseek(lower_file, offset, whence);
+
+out:
+ return err;
+}
+
+
const struct file_operations sdcardfs_main_fops = {
.llseek = generic_file_llseek,
.read = sdcardfs_read,
@@ -339,7 +358,7 @@
/* trimmed directory options */
const struct file_operations sdcardfs_dir_fops = {
- .llseek = generic_file_llseek,
+ .llseek = sdcardfs_file_llseek,
.read = generic_read_dir,
.iterate = sdcardfs_readdir,
.unlocked_ioctl = sdcardfs_unlocked_ioctl,
diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c
index 0f4aa17..f4d6c08 100755
--- a/fs/sdcardfs/inode.c
+++ b/fs/sdcardfs/inode.c
@@ -19,18 +19,26 @@
*/
#include "sdcardfs.h"
+#include <linux/fs_struct.h>
+#include <linux/ratelimit.h>
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
-const struct cred * override_fsids(struct sdcardfs_sb_info* sbi)
+const struct cred *override_fsids(struct sdcardfs_sb_info *sbi,
+ struct sdcardfs_inode_data *data)
{
- struct cred * cred;
- const struct cred * old_cred;
+ struct cred *cred;
+ const struct cred *old_cred;
+ uid_t uid;
cred = prepare_creds();
if (!cred)
return NULL;
- cred->fsuid = sbi->options.fs_low_uid;
+ if (data->under_obb)
+ uid = AID_MEDIA_OBB;
+ else
+ uid = multiuser_get_uid(data->userid, sbi->options.fs_low_uid);
+ cred->fsuid = uid;
cred->fsgid = sbi->options.fs_low_gid;
old_cred = override_creds(cred);
@@ -39,9 +47,9 @@
}
/* Do not directly use this function, use REVERT_CRED() instead. */
-void revert_fsids(const struct cred * old_cred)
+void revert_fsids(const struct cred *old_cred)
{
- const struct cred * cur_cred;
+ const struct cred *cur_cred;
cur_cred = current->cred;
revert_creds(old_cred);
@@ -53,38 +61,54 @@
{
int err = 0;
struct dentry *lower_dentry;
+ struct vfsmount *lower_dentry_mnt;
struct dentry *lower_parent_dentry = NULL;
struct path lower_path;
const struct cred *saved_cred = NULL;
+ struct fs_struct *saved_fs;
+ struct fs_struct *copied_fs;
- if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
- printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
- " dentry: %s, task:%s\n",
- __func__, dentry->d_name.name, current->comm);
+ if (!check_caller_access_to_name(dir, &dentry->d_name)) {
err = -EACCES;
goto out_eacces;
}
/* save current_cred and override it */
- OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
+ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
+ lower_dentry_mnt = lower_path.mnt;
lower_parent_dentry = lock_parent(lower_dentry);
/* set last 16bytes of mode field to 0664 */
mode = (mode & S_IFMT) | 00664;
- err = vfs_create(lower_parent_dentry->d_inode, lower_dentry, mode, want_excl);
+
+ /* temporarily change umask for lower fs write */
+ saved_fs = current->fs;
+ copied_fs = copy_fs_struct(current->fs);
+ if (!copied_fs) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+ current->fs = copied_fs;
+ current->fs->umask = 0;
+ err = vfs_create2(lower_dentry_mnt, lower_parent_dentry->d_inode, lower_dentry, mode, want_excl);
if (err)
goto out;
- err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, SDCARDFS_I(dir)->userid);
+ err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path,
+ SDCARDFS_I(dir)->data->userid);
if (err)
goto out;
fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode);
+ fixup_lower_ownership(dentry, dentry->d_name.name);
out:
+ current->fs = saved_fs;
+ free_fs_struct(copied_fs);
+out_unlock:
unlock_dir(lower_parent_dentry);
sdcardfs_put_lower_path(dentry, &lower_path);
REVERT_CRED(saved_cred);
@@ -138,28 +162,27 @@
{
int err;
struct dentry *lower_dentry;
+ struct vfsmount *lower_mnt;
struct inode *lower_dir_inode = sdcardfs_lower_inode(dir);
struct dentry *lower_dir_dentry;
struct path lower_path;
const struct cred *saved_cred = NULL;
- if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
- printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
- " dentry: %s, task:%s\n",
- __func__, dentry->d_name.name, current->comm);
+ if (!check_caller_access_to_name(dir, &dentry->d_name)) {
err = -EACCES;
goto out_eacces;
}
/* save current_cred and override it */
- OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
+ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
+ lower_mnt = lower_path.mnt;
dget(lower_dentry);
lower_dir_dentry = lock_parent(lower_dentry);
- err = vfs_unlink(lower_dir_inode, lower_dentry);
+ err = vfs_unlink2(lower_mnt, lower_dir_inode, lower_dentry);
/*
* Note: unlinking on top of NFS can cause silly-renamed files.
@@ -219,14 +242,15 @@
}
#endif
-static int touch(char *abs_path, mode_t mode) {
+static int touch(char *abs_path, mode_t mode)
+{
struct file *filp = filp_open(abs_path, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, mode);
+
if (IS_ERR(filp)) {
if (PTR_ERR(filp) == -EEXIST) {
return 0;
- }
- else {
- printk(KERN_ERR "sdcardfs: failed to open(%s): %ld\n",
+ } else {
+ pr_err("sdcardfs: failed to open(%s): %ld\n",
abs_path, PTR_ERR(filp));
return PTR_ERR(filp);
}
@@ -240,31 +264,29 @@
int err = 0;
int make_nomedia_in_obb = 0;
struct dentry *lower_dentry;
+ struct vfsmount *lower_mnt;
struct dentry *lower_parent_dentry = NULL;
struct path lower_path;
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
const struct cred *saved_cred = NULL;
- struct sdcardfs_inode_info *pi = SDCARDFS_I(dir);
- char *page_buf;
- char *nomedia_dir_name;
- char *nomedia_fullpath;
- int fullpath_namelen;
+ struct sdcardfs_inode_data *pd = SDCARDFS_I(dir)->data;
int touch_err = 0;
+ struct fs_struct *saved_fs;
+ struct fs_struct *copied_fs;
+ struct qstr q_obb = QSTR_LITERAL("obb");
+ struct qstr q_data = QSTR_LITERAL("data");
- if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
- printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
- " dentry: %s, task:%s\n",
- __func__, dentry->d_name.name, current->comm);
+ if (!check_caller_access_to_name(dir, &dentry->d_name)) {
err = -EACCES;
goto out_eacces;
}
/* save current_cred and override it */
- OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
+ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
/* check disk space */
if (!check_min_free_space(dentry, 0, 1)) {
- printk(KERN_INFO "sdcardfs: No minimum free space.\n");
+ pr_err("sdcardfs: No minimum free space.\n");
err = -ENOSPC;
goto out_revert;
}
@@ -272,87 +294,84 @@
/* the lower_dentry is negative here */
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
+ lower_mnt = lower_path.mnt;
lower_parent_dentry = lock_parent(lower_dentry);
/* set last 16bytes of mode field to 0775 */
mode = (mode & S_IFMT) | 00775;
- err = vfs_mkdir(lower_parent_dentry->d_inode, lower_dentry, mode);
- if (err)
+ /* temporarily change umask for lower fs write */
+ saved_fs = current->fs;
+ copied_fs = copy_fs_struct(current->fs);
+ if (!copied_fs) {
+ err = -ENOMEM;
+ unlock_dir(lower_parent_dentry);
+ goto out_unlock;
+ }
+ current->fs = copied_fs;
+ current->fs->umask = 0;
+ err = vfs_mkdir2(lower_mnt, lower_parent_dentry->d_inode, lower_dentry, mode);
+
+ if (err) {
+ unlock_dir(lower_parent_dentry);
goto out;
+ }
/* if it is a local obb dentry, setup it with the base obbpath */
- if(need_graft_path(dentry)) {
+ if (need_graft_path(dentry)) {
err = setup_obb_dentry(dentry, &lower_path);
- if(err) {
+ if (err) {
/* if the sbi->obbpath is not available, the lower_path won't be
* changed by setup_obb_dentry() but the lower path is saved to
* its orig_path. this dentry will be revalidated later.
- * but now, the lower_path should be NULL */
+ * but now, the lower_path should be NULL
+ */
sdcardfs_put_reset_lower_path(dentry);
/* the newly created lower path which saved to its orig_path or
* the lower_path is the base obbpath.
- * therefore, an additional path_get is required */
+ * therefore, an additional path_get is required
+ */
path_get(&lower_path);
} else
make_nomedia_in_obb = 1;
}
- err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pi->userid);
- if (err)
+ err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pd->userid);
+ if (err) {
+ unlock_dir(lower_parent_dentry);
goto out;
+ }
fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir));
fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode);
/* update number of links on parent directory */
set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink);
-
- if ((!sbi->options.multiuser) && (!strcasecmp(dentry->d_name.name, "obb"))
- && (pi->perm == PERM_ANDROID) && (pi->userid == 0))
+ fixup_lower_ownership(dentry, dentry->d_name.name);
+ unlock_dir(lower_parent_dentry);
+ if ((!sbi->options.multiuser) && (qstr_case_eq(&dentry->d_name, &q_obb))
+ && (pd->perm == PERM_ANDROID) && (pd->userid == 0))
make_nomedia_in_obb = 1;
/* When creating /Android/data and /Android/obb, mark them as .nomedia */
if (make_nomedia_in_obb ||
- ((pi->perm == PERM_ANDROID) && (!strcasecmp(dentry->d_name.name, "data")))) {
-
- page_buf = (char *)__get_free_page(GFP_KERNEL);
- if (!page_buf) {
- printk(KERN_ERR "sdcardfs: failed to allocate page buf\n");
- goto out;
- }
-
- nomedia_dir_name = d_absolute_path(&lower_path, page_buf, PAGE_SIZE);
- if (IS_ERR(nomedia_dir_name)) {
- free_page((unsigned long)page_buf);
- printk(KERN_ERR "sdcardfs: failed to get .nomedia dir name\n");
- goto out;
- }
-
- fullpath_namelen = page_buf + PAGE_SIZE - nomedia_dir_name - 1;
- fullpath_namelen += strlen("/.nomedia");
- nomedia_fullpath = kzalloc(fullpath_namelen + 1, GFP_KERNEL);
- if (!nomedia_fullpath) {
- free_page((unsigned long)page_buf);
- printk(KERN_ERR "sdcardfs: failed to allocate .nomedia fullpath buf\n");
- goto out;
- }
-
- strcpy(nomedia_fullpath, nomedia_dir_name);
- free_page((unsigned long)page_buf);
- strcat(nomedia_fullpath, "/.nomedia");
- touch_err = touch(nomedia_fullpath, 0664);
+ ((pd->perm == PERM_ANDROID)
+ && (qstr_case_eq(&dentry->d_name, &q_data)))) {
+ REVERT_CRED(saved_cred);
+ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dentry->d_inode));
+ set_fs_pwd(current->fs, &lower_path);
+ touch_err = touch(".nomedia", 0664);
if (touch_err) {
- printk(KERN_ERR "sdcardfs: failed to touch(%s): %d\n",
- nomedia_fullpath, touch_err);
- kfree(nomedia_fullpath);
+ pr_err("sdcardfs: failed to create .nomedia in %s: %d\n",
+ lower_path.dentry->d_name.name, touch_err);
goto out;
}
- kfree(nomedia_fullpath);
}
out:
- unlock_dir(lower_parent_dentry);
+ current->fs = saved_fs;
+ free_fs_struct(copied_fs);
+out_unlock:
sdcardfs_put_lower_path(dentry, &lower_path);
out_revert:
REVERT_CRED(saved_cred);
@@ -364,29 +383,29 @@
{
struct dentry *lower_dentry;
struct dentry *lower_dir_dentry;
+ struct vfsmount *lower_mnt;
int err;
struct path lower_path;
const struct cred *saved_cred = NULL;
- if(!check_caller_access_to_name(dir, dentry->d_name.name)) {
- printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
- " dentry: %s, task:%s\n",
- __func__, dentry->d_name.name, current->comm);
+ if (!check_caller_access_to_name(dir, &dentry->d_name)) {
err = -EACCES;
goto out_eacces;
}
/* save current_cred and override it */
- OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred);
+ OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
/* sdcardfs_get_real_lower(): in case of remove an user's obb dentry
- * the dentry on the original path should be deleted. */
+ * the dentry on the original path should be deleted.
+ */
sdcardfs_get_real_lower(dentry, &lower_path);
lower_dentry = lower_path.dentry;
+ lower_mnt = lower_path.mnt;
lower_dir_dentry = lock_parent(lower_dentry);
- err = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry);
+ err = vfs_rmdir2(lower_mnt, lower_dir_dentry->d_inode, lower_dentry);
if (err)
goto out;
@@ -450,27 +469,25 @@
struct dentry *lower_new_dentry = NULL;
struct dentry *lower_old_dir_dentry = NULL;
struct dentry *lower_new_dir_dentry = NULL;
+ struct vfsmount *lower_mnt = NULL;
struct dentry *trap = NULL;
- struct dentry *new_parent = NULL;
struct path lower_old_path, lower_new_path;
const struct cred *saved_cred = NULL;
- if(!check_caller_access_to_name(old_dir, old_dentry->d_name.name) ||
- !check_caller_access_to_name(new_dir, new_dentry->d_name.name)) {
- printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
- " new_dentry: %s, task:%s\n",
- __func__, new_dentry->d_name.name, current->comm);
+ if (!check_caller_access_to_name(old_dir, &old_dentry->d_name) ||
+ !check_caller_access_to_name(new_dir, &new_dentry->d_name)) {
err = -EACCES;
goto out_eacces;
}
/* save current_cred and override it */
- OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred);
+ OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred, SDCARDFS_I(new_dir));
sdcardfs_get_real_lower(old_dentry, &lower_old_path);
sdcardfs_get_lower_path(new_dentry, &lower_new_path);
lower_old_dentry = lower_old_path.dentry;
lower_new_dentry = lower_new_path.dentry;
+ lower_mnt = lower_old_path.mnt;
lower_old_dir_dentry = dget_parent(lower_old_dentry);
lower_new_dir_dentry = dget_parent(lower_new_dentry);
@@ -486,7 +503,8 @@
goto out;
}
- err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry,
+ err = vfs_rename2(lower_mnt,
+ lower_old_dir_dentry->d_inode, lower_old_dentry,
lower_new_dir_dentry->d_inode, lower_new_dentry);
if (err)
goto out;
@@ -498,25 +516,11 @@
if (new_dir != old_dir) {
sdcardfs_copy_and_fix_attrs(old_dir, lower_old_dir_dentry->d_inode);
fsstack_copy_inode_size(old_dir, lower_old_dir_dentry->d_inode);
-
- /* update the derived permission of the old_dentry
- * with its new parent
- */
- new_parent = dget_parent(new_dentry);
- if(new_parent) {
- if(old_dentry->d_inode) {
- update_derived_permission_lock(old_dentry);
- }
- dput(new_parent);
- }
}
- /* At this point, not all dentry information has been moved, so
- * we pass along new_dentry for the name.*/
- mutex_lock(&old_dentry->d_inode->i_mutex);
- get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry);
- fix_derived_permission(old_dentry->d_inode);
- get_derive_permissions_recursive(old_dentry);
- mutex_unlock(&old_dentry->d_inode->i_mutex);
+ get_derived_permission_new(new_dentry->d_parent, old_dentry, &new_dentry->d_name);
+ fixup_tmp_permissions(old_dentry->d_inode);
+ fixup_lower_ownership(old_dentry, new_dentry->d_name.name);
+ d_invalidate(old_dentry); /* Can't fixup ownership recursively :( */
out:
unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
dput(lower_old_dir_dentry);
@@ -598,16 +602,61 @@
}
#endif
-static int sdcardfs_permission(struct inode *inode, int mask)
+static int sdcardfs_permission_wrn(struct inode *inode, int mask)
+{
+ WARN_RATELIMIT(1, "sdcardfs does not support permission. Use permission2.\n");
+ return -EINVAL;
+}
+
+void copy_attrs(struct inode *dest, const struct inode *src)
+{
+ dest->i_mode = src->i_mode;
+ dest->i_uid = src->i_uid;
+ dest->i_gid = src->i_gid;
+ dest->i_rdev = src->i_rdev;
+ dest->i_atime = src->i_atime;
+ dest->i_mtime = src->i_mtime;
+ dest->i_ctime = src->i_ctime;
+ dest->i_blkbits = src->i_blkbits;
+ dest->i_flags = src->i_flags;
+#ifdef CONFIG_FS_POSIX_ACL
+ dest->i_acl = src->i_acl;
+#endif
+#ifdef CONFIG_SECURITY
+ dest->i_security = src->i_security;
+#endif
+}
+
+static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int mask)
{
int err;
+ struct inode tmp;
+ struct sdcardfs_inode_data *top = top_data_get(SDCARDFS_I(inode));
+
+ if (!top)
+ return -EINVAL;
/*
* Permission check on sdcardfs inode.
* Calling process should have AID_SDCARD_RW permission
+ * Since generic_permission only needs i_mode, i_uid,
+ * i_gid, and i_sb, we can create a fake inode to pass
+ * this information down in.
+ *
+ * The underlying code may attempt to take locks in some
+ * cases for features we're not using, but if that changes,
+ * locks must be dealt with to avoid undefined behavior.
*/
- err = generic_permission(inode, mask);
-
+ copy_attrs(&tmp, inode);
+ tmp.i_uid = make_kuid(&init_user_ns, top->d_uid);
+ tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, top));
+ tmp.i_mode = (inode->i_mode & S_IFMT)
+ | get_mode(mnt, SDCARDFS_I(inode), top);
+ data_put(top);
+ tmp.i_sb = inode->i_sb;
+ if (IS_POSIXACL(inode))
+ pr_warn("%s: This may be undefined behavior...\n", __func__);
+ err = generic_permission(&tmp, mask);
/* XXX
* Original sdcardfs code calls inode_permission(lower_inode,.. )
* for checking inode permission. But doing such things here seems
@@ -624,6 +673,7 @@
* we check it with AID_MEDIA_RW permission
*/
struct inode *lower_inode;
+
OVERRIDE_CRED(SDCARDFS_SB(inode->sb));
lower_inode = sdcardfs_lower_inode(inode);
@@ -636,82 +686,85 @@
}
-static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
- struct kstat *stat)
+static int sdcardfs_setattr_wrn(struct dentry *dentry, struct iattr *ia)
{
- struct dentry *lower_dentry;
- struct inode *inode;
- struct inode *lower_inode;
- struct path lower_path;
- struct dentry *parent;
-
- parent = dget_parent(dentry);
- if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
- printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
- " dentry: %s, task:%s\n",
- __func__, dentry->d_name.name, current->comm);
- dput(parent);
- return -EACCES;
- }
- dput(parent);
-
- inode = dentry->d_inode;
-
- sdcardfs_get_lower_path(dentry, &lower_path);
- lower_dentry = lower_path.dentry;
- lower_inode = sdcardfs_lower_inode(inode);
-
-
- sdcardfs_copy_and_fix_attrs(inode, lower_inode);
- fsstack_copy_inode_size(inode, lower_inode);
-
-
- generic_fillattr(inode, stat);
- sdcardfs_put_lower_path(dentry, &lower_path);
- return 0;
+ WARN_RATELIMIT(1, "sdcardfs does not support setattr. User setattr2.\n");
+ return -EINVAL;
}
-static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia)
+static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct iattr *ia)
{
int err = 0;
struct dentry *lower_dentry;
+ struct vfsmount *lower_mnt;
struct inode *inode;
struct inode *lower_inode;
struct path lower_path;
struct iattr lower_ia;
struct dentry *parent;
+ struct inode tmp;
+ struct sdcardfs_inode_data *top;
+ const struct cred *saved_cred = NULL;
inode = dentry->d_inode;
+ top = top_data_get(SDCARDFS_I(inode));
+
+ if (!top)
+ return -EINVAL;
+
+ /*
+ * Permission check on sdcardfs inode.
+ * Calling process should have AID_SDCARD_RW permission
+ * Since generic_permission only needs i_mode, i_uid,
+ * i_gid, and i_sb, we can create a fake inode to pass
+ * this information down in.
+ *
+ * The underlying code may attempt to take locks in some
+ * cases for features we're not using, but if that changes,
+ * locks must be dealt with to avoid undefined behavior.
+ *
+ */
+ copy_attrs(&tmp, inode);
+ tmp.i_uid = make_kuid(&init_user_ns, top->d_uid);
+ tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, top));
+ tmp.i_mode = (inode->i_mode & S_IFMT)
+ | get_mode(mnt, SDCARDFS_I(inode), top);
+ tmp.i_size = i_size_read(inode);
+ data_put(top);
+ tmp.i_sb = inode->i_sb;
/*
* Check if user has permission to change inode. We don't check if
* this user can change the lower inode: that should happen when
* calling notify_change on the lower inode.
*/
- err = inode_change_ok(inode, ia);
+ /* prepare our own lower struct iattr (with the lower file) */
+ memcpy(&lower_ia, ia, sizeof(lower_ia));
+ /* Allow touch updating timestamps. A previous permission check ensures
+ * we have write access. Changes to mode, owner, and group are ignored
+ */
+ ia->ia_valid |= ATTR_FORCE;
+ err = inode_change_ok(&tmp, ia);
- /* no vfs_XXX operations required, cred overriding will be skipped. wj*/
if (!err) {
/* check the Android group ID */
parent = dget_parent(dentry);
- if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
- printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
- " dentry: %s, task:%s\n",
- __func__, dentry->d_name.name, current->comm);
+ if (!check_caller_access_to_name(parent->d_inode, &dentry->d_name))
err = -EACCES;
- }
dput(parent);
}
if (err)
goto out_err;
+ /* save current_cred and override it */
+ OVERRIDE_CRED(SDCARDFS_SB(dentry->d_sb), saved_cred, SDCARDFS_I(inode));
+
sdcardfs_get_lower_path(dentry, &lower_path);
lower_dentry = lower_path.dentry;
+ lower_mnt = lower_path.mnt;
lower_inode = sdcardfs_lower_inode(inode);
- /* prepare our own lower struct iattr (with the lower file) */
- memcpy(&lower_ia, ia, sizeof(lower_ia));
if (ia->ia_valid & ATTR_FILE)
lower_ia.ia_file = sdcardfs_lower_file(ia->ia_file);
@@ -728,7 +781,7 @@
if (current->mm)
down_write(¤t->mm->mmap_sem);
if (ia->ia_valid & ATTR_SIZE) {
- err = inode_newsize_ok(inode, ia->ia_size);
+ err = inode_newsize_ok(&tmp, ia->ia_size);
if (err) {
if (current->mm)
up_write(¤t->mm->mmap_sem);
@@ -751,7 +804,8 @@
* tries to open(), unlink(), then ftruncate() a file.
*/
mutex_lock(&lower_dentry->d_inode->i_mutex);
- err = notify_change(lower_dentry, &lower_ia); /* note: lower_ia */
+ err = notify_change2(lower_mnt, lower_dentry, &lower_ia); /* note: lower_ia */
+
mutex_unlock(&lower_dentry->d_inode->i_mutex);
if (current->mm)
up_write(¤t->mm->mmap_sem);
@@ -769,13 +823,68 @@
out:
sdcardfs_put_lower_path(dentry, &lower_path);
+ REVERT_CRED(saved_cred);
out_err:
return err;
}
+static int sdcardfs_fillattr(struct vfsmount *mnt,
+ struct inode *inode, struct kstat *stat)
+{
+ struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
+ struct sdcardfs_inode_data *top = top_data_get(info);
+
+ if (!top)
+ return -EINVAL;
+
+ stat->dev = inode->i_sb->s_dev;
+ stat->ino = inode->i_ino;
+ stat->mode = (inode->i_mode & S_IFMT) | get_mode(mnt, info, top);
+ stat->nlink = inode->i_nlink;
+ stat->uid = make_kuid(&init_user_ns, top->d_uid);
+ stat->gid = make_kgid(&init_user_ns, get_gid(mnt, top));
+ stat->rdev = inode->i_rdev;
+ stat->size = i_size_read(inode);
+ stat->atime = inode->i_atime;
+ stat->mtime = inode->i_mtime;
+ stat->ctime = inode->i_ctime;
+ stat->blksize = (1 << inode->i_blkbits);
+ stat->blocks = inode->i_blocks;
+ data_put(top);
+ return 0;
+}
+
+static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
+ struct kstat *stat)
+{
+ struct kstat lower_stat;
+ struct path lower_path;
+ struct dentry *parent;
+ int err;
+
+ parent = dget_parent(dentry);
+ if (!check_caller_access_to_name(parent->d_inode, &dentry->d_name)) {
+ dput(parent);
+ return -EACCES;
+ }
+ dput(parent);
+
+ sdcardfs_get_lower_path(dentry, &lower_path);
+ err = vfs_getattr(&lower_path, &lower_stat);
+ if (err)
+ goto out;
+ sdcardfs_copy_and_fix_attrs(dentry->d_inode,
+ lower_path.dentry->d_inode);
+ err = sdcardfs_fillattr(mnt, dentry->d_inode, stat);
+ stat->blocks = lower_stat.blocks;
+out:
+ sdcardfs_put_lower_path(dentry, &lower_path);
+ return err;
+}
+
const struct inode_operations sdcardfs_symlink_iops = {
- .permission = sdcardfs_permission,
- .setattr = sdcardfs_setattr,
+ .permission2 = sdcardfs_permission,
+ .setattr2 = sdcardfs_setattr,
/* XXX Following operations are implemented,
* but FUSE(sdcard) or FAT does not support them
* These methods are *NOT* perfectly tested.
@@ -788,14 +897,14 @@
const struct inode_operations sdcardfs_dir_iops = {
.create = sdcardfs_create,
.lookup = sdcardfs_lookup,
-#if 0
- .permission = sdcardfs_permission,
-#endif
+ .permission = sdcardfs_permission_wrn,
+ .permission2 = sdcardfs_permission,
.unlink = sdcardfs_unlink,
.mkdir = sdcardfs_mkdir,
.rmdir = sdcardfs_rmdir,
.rename = sdcardfs_rename,
- .setattr = sdcardfs_setattr,
+ .setattr = sdcardfs_setattr_wrn,
+ .setattr2 = sdcardfs_setattr,
.getattr = sdcardfs_getattr,
/* XXX Following operations are implemented,
* but FUSE(sdcard) or FAT does not support them
@@ -807,7 +916,9 @@
};
const struct inode_operations sdcardfs_main_iops = {
- .permission = sdcardfs_permission,
- .setattr = sdcardfs_setattr,
+ .permission = sdcardfs_permission_wrn,
+ .permission2 = sdcardfs_permission,
+ .setattr = sdcardfs_setattr_wrn,
+ .setattr2 = sdcardfs_setattr,
.getattr = sdcardfs_getattr,
};
diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c
index a01b06a..369b94e 100755
--- a/fs/sdcardfs/lookup.c
+++ b/fs/sdcardfs/lookup.c
@@ -36,8 +36,7 @@
void sdcardfs_destroy_dentry_cache(void)
{
- if (sdcardfs_dentry_cachep)
- kmem_cache_destroy(sdcardfs_dentry_cachep);
+ kmem_cache_destroy(sdcardfs_dentry_cachep);
}
void free_dentry_private_data(struct dentry *dentry)
@@ -72,7 +71,8 @@
static int sdcardfs_inode_test(struct inode *inode, void *candidate_data/*void *candidate_lower_inode*/)
{
struct inode *current_lower_inode = sdcardfs_lower_inode(inode);
- userid_t current_userid = SDCARDFS_I(inode)->userid;
+ userid_t current_userid = SDCARDFS_I(inode)->data->userid;
+
if (current_lower_inode == ((struct inode_data *)candidate_data)->lower_inode &&
current_userid == ((struct inode_data *)candidate_data)->id)
return 1; /* found a match */
@@ -91,7 +91,9 @@
struct sdcardfs_inode_info *info;
struct inode_data data;
struct inode *inode; /* the new inode to return */
- int err;
+
+ if (!igrab(lower_inode))
+ return ERR_PTR(-ESTALE);
data.id = id;
data.lower_inode = lower_inode;
@@ -102,26 +104,23 @@
* instead.
*/
lower_inode->i_ino, /* hashval */
- sdcardfs_inode_test, /* inode comparison function */
+ sdcardfs_inode_test, /* inode comparison function */
sdcardfs_inode_set, /* inode init function */
&data); /* data passed to test+set fxns */
if (!inode) {
- err = -EACCES;
iput(lower_inode);
- return ERR_PTR(err);
+ return ERR_PTR(-ENOMEM);
}
- /* if found a cached inode, then just return it */
- if (!(inode->i_state & I_NEW))
+ /* if found a cached inode, then just return it (after iput) */
+ if (!(inode->i_state & I_NEW)) {
+ iput(lower_inode);
return inode;
+ }
/* initialize new inode */
info = SDCARDFS_I(inode);
inode->i_ino = lower_inode->i_ino;
- if (!igrab(lower_inode)) {
- err = -ESTALE;
- return ERR_PTR(err);
- }
sdcardfs_set_lower_inode(inode, lower_inode);
inode->i_version++;
@@ -164,27 +163,25 @@
}
/*
- * Connect a sdcardfs inode dentry/inode with several lower ones. This is
- * the classic stackable file system "vnode interposition" action.
- *
- * @dentry: sdcardfs's dentry which interposes on lower one
- * @sb: sdcardfs's super_block
- * @lower_path: the lower path (caller does path_get/put)
+ * Helper interpose routine, called directly by ->lookup to handle
+ * spliced dentries.
*/
-int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb,
- struct path *lower_path, userid_t id)
+static struct dentry *__sdcardfs_interpose(struct dentry *dentry,
+ struct super_block *sb,
+ struct path *lower_path,
+ userid_t id)
{
- int err = 0;
struct inode *inode;
struct inode *lower_inode;
struct super_block *lower_sb;
+ struct dentry *ret_dentry;
lower_inode = lower_path->dentry->d_inode;
lower_sb = sdcardfs_lower_super(sb);
/* check that the lower file system didn't cross a mount point */
if (lower_inode->i_sb != lower_sb) {
- err = -EXDEV;
+ ret_dentry = ERR_PTR(-EXDEV);
goto out;
}
@@ -196,14 +193,54 @@
/* inherit lower inode number for sdcardfs's inode */
inode = sdcardfs_iget(sb, lower_inode, id);
if (IS_ERR(inode)) {
- err = PTR_ERR(inode);
+ ret_dentry = ERR_CAST(inode);
goto out;
}
- d_add(dentry, inode);
+ ret_dentry = d_splice_alias(inode, dentry);
+ dentry = ret_dentry ?: dentry;
update_derived_permission_lock(dentry);
out:
- return err;
+ return ret_dentry;
+}
+
+/*
+ * Connect an sdcardfs inode dentry/inode with several lower ones. This is
+ * the classic stackable file system "vnode interposition" action.
+ *
+ * @dentry: sdcardfs's dentry which interposes on lower one
+ * @sb: sdcardfs's super_block
+ * @lower_path: the lower path (caller does path_get/put)
+ */
+int sdcardfs_interpose(struct dentry *dentry, struct super_block *sb,
+ struct path *lower_path, userid_t id)
+{
+ struct dentry *ret_dentry;
+
+ ret_dentry = __sdcardfs_interpose(dentry, sb, lower_path, id);
+ return PTR_ERR(ret_dentry);
+}
+
+struct sdcardfs_name_data {
+ struct dir_context ctx;
+ const struct qstr *to_find;
+ char *name;
+ bool found;
+};
+
+static int sdcardfs_name_match(void *__buf, const char *name, int namelen,
+ loff_t offset, u64 ino, unsigned int d_type)
+{
+ struct sdcardfs_name_data *buf = (struct sdcardfs_name_data *) __buf;
+ struct qstr candidate = QSTR_INIT(name, namelen);
+
+ if (qstr_case_eq(buf->to_find, &candidate)) {
+ memcpy(buf->name, name, namelen);
+ buf->name[namelen] = 0;
+ buf->found = true;
+ return 1;
+ }
+ return 0;
}
/*
@@ -219,9 +256,10 @@
struct vfsmount *lower_dir_mnt;
struct dentry *lower_dir_dentry = NULL;
struct dentry *lower_dentry;
- const char *name;
+ const struct qstr *name;
struct path lower_path;
- struct qstr this;
+ struct qstr dname;
+ struct dentry *ret_dentry = NULL;
struct sdcardfs_sb_info *sbi;
sbi = SDCARDFS_SB(dentry->d_sb);
@@ -231,47 +269,90 @@
if (IS_ROOT(dentry))
goto out;
- name = dentry->d_name.name;
+ name = &dentry->d_name;
/* now start the actual lookup procedure */
lower_dir_dentry = lower_parent_path->dentry;
lower_dir_mnt = lower_parent_path->mnt;
/* Use vfs_path_lookup to check if the dentry exists or not */
- err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0,
+ err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name->name, 0,
&lower_path);
+ /* check for other cases */
+ if (err == -ENOENT) {
+ struct file *file;
+ const struct cred *cred = current_cred();
+
+ struct sdcardfs_name_data buffer = {
+ .ctx.actor = sdcardfs_name_match,
+ .to_find = name,
+ .name = __getname(),
+ .found = false,
+ };
+
+ if (!buffer.name) {
+ err = -ENOMEM;
+ goto out;
+ }
+ file = dentry_open(lower_parent_path, O_RDONLY, cred);
+ if (IS_ERR(file)) {
+ err = PTR_ERR(file);
+ goto put_name;
+ }
+ err = iterate_dir(file, &buffer.ctx);
+ fput(file);
+ if (err)
+ goto put_name;
+
+ if (buffer.found)
+ err = vfs_path_lookup(lower_dir_dentry,
+ lower_dir_mnt,
+ buffer.name, 0,
+ &lower_path);
+ else
+ err = -ENOENT;
+put_name:
+ __putname(buffer.name);
+ }
/* no error: handle positive dentries */
if (!err) {
/* check if the dentry is an obb dentry
* if true, the lower_inode must be replaced with
- * the inode of the graft path */
+ * the inode of the graft path
+ */
- if(need_graft_path(dentry)) {
+ if (need_graft_path(dentry)) {
/* setup_obb_dentry()
- * The lower_path will be stored to the dentry's orig_path
+ * The lower_path will be stored to the dentry's orig_path
* and the base obbpath will be copyed to the lower_path variable.
* if an error returned, there's no change in the lower_path
- * returns: -ERRNO if error (0: no error) */
+ * returns: -ERRNO if error (0: no error)
+ */
err = setup_obb_dentry(dentry, &lower_path);
- if(err) {
+ if (err) {
/* if the sbi->obbpath is not available, we can optionally
* setup the lower_path with its orig_path.
* but, the current implementation just returns an error
* because the sdcard daemon also regards this case as
- * a lookup fail. */
- printk(KERN_INFO "sdcardfs: base obbpath is not available\n");
+ * a lookup fail.
+ */
+ pr_info("sdcardfs: base obbpath is not available\n");
sdcardfs_put_reset_orig_path(dentry);
goto out;
}
}
sdcardfs_set_lower_path(dentry, &lower_path);
- err = sdcardfs_interpose(dentry, dentry->d_sb, &lower_path, id);
- if (err) /* path_put underlying path on error */
+ ret_dentry =
+ __sdcardfs_interpose(dentry, dentry->d_sb, &lower_path, id);
+ if (IS_ERR(ret_dentry)) {
+ err = PTR_ERR(ret_dentry);
+ /* path_put underlying path on error */
sdcardfs_put_reset_lower_path(dentry);
+ }
goto out;
}
@@ -283,21 +364,24 @@
goto out;
/* instatiate a new negative dentry */
- this.name = name;
- this.len = strlen(name);
- this.hash = full_name_hash(this.name, this.len);
- lower_dentry = d_lookup(lower_dir_dentry, &this);
- if (lower_dentry)
- goto setup_lower;
+ dname.name = name->name;
+ dname.len = name->len;
- lower_dentry = d_alloc(lower_dir_dentry, &this);
+ /* See if the low-level filesystem might want
+ * to use its own hash
+ */
+ lower_dentry = d_hash_and_lookup(lower_dir_dentry, &dname);
+ if (IS_ERR(lower_dentry))
+ return lower_dentry;
if (!lower_dentry) {
- err = -ENOMEM;
+ /* We called vfs_path_lookup earlier, and did not get a negative
+ * dentry then. Don't confuse the lower filesystem by forcing
+ * one on it now...
+ */
+ err = -ENOENT;
goto out;
}
- d_add(lower_dentry, NULL); /* instantiate and hash */
-setup_lower:
lower_path.dentry = lower_dentry;
lower_path.mnt = mntget(lower_dir_mnt);
sdcardfs_set_lower_path(dentry, &lower_path);
@@ -311,14 +395,16 @@
err = 0;
out:
- return ERR_PTR(err);
+ if (err)
+ return ERR_PTR(err);
+ return ret_dentry;
}
/*
* On success:
- * fills dentry object appropriate values and returns NULL.
+ * fills dentry object appropriate values and returns NULL.
* On fail (== error)
- * returns error ptr
+ * returns error ptr
*
* @dir : Parent inode. It is locked (dir->i_mutex)
* @dentry : Target dentry to lookup. we should set each of fields.
@@ -335,16 +421,13 @@
parent = dget_parent(dentry);
- if(!check_caller_access_to_name(parent->d_inode, dentry->d_name.name)) {
+ if (!check_caller_access_to_name(parent->d_inode, &dentry->d_name)) {
ret = ERR_PTR(-EACCES);
- printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n"
- " dentry: %s, task:%s\n",
- __func__, dentry->d_name.name, current->comm);
goto out_err;
- }
+ }
/* save current_cred and override it */
- OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred);
+ OVERRIDE_CRED_PTR(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir));
sdcardfs_get_lower_path(parent, &lower_parent_path);
@@ -355,21 +438,19 @@
goto out;
}
- ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path, SDCARDFS_I(dir)->userid);
+ ret = __sdcardfs_lookup(dentry, flags, &lower_parent_path,
+ SDCARDFS_I(dir)->data->userid);
if (IS_ERR(ret))
- {
goto out;
- }
if (ret)
dentry = ret;
if (dentry->d_inode) {
fsstack_copy_attr_times(dentry->d_inode,
sdcardfs_lower_inode(dentry->d_inode));
- /* get drived permission */
- mutex_lock(&dentry->d_inode->i_mutex);
+ /* get derived permission */
get_derived_permission(parent, dentry);
- fix_derived_permission(dentry->d_inode);
- mutex_unlock(&dentry->d_inode->i_mutex);
+ fixup_tmp_permissions(dentry->d_inode);
+ fixup_lower_ownership(dentry, dentry->d_name.name);
}
/* update parent directory's atime */
fsstack_copy_attr_atime(parent->d_inode,
diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c
index 8b51a12..129d98e 100755
--- a/fs/sdcardfs/main.c
+++ b/fs/sdcardfs/main.c
@@ -28,9 +28,8 @@
Opt_fsgid,
Opt_gid,
Opt_debug,
- Opt_lower_fs,
Opt_mask,
- Opt_multiuser, // May need?
+ Opt_multiuser,
Opt_userid,
Opt_reserved_mb,
Opt_err,
@@ -49,7 +48,8 @@
};
static int parse_options(struct super_block *sb, char *options, int silent,
- int *debug, struct sdcardfs_mount_options *opts)
+ int *debug, struct sdcardfs_vfsmount_options *vfsopts,
+ struct sdcardfs_mount_options *opts)
{
char *p;
substring_t args[MAX_OPT_ARGS];
@@ -58,10 +58,10 @@
/* by default, we use AID_MEDIA_RW as uid, gid */
opts->fs_low_uid = AID_MEDIA_RW;
opts->fs_low_gid = AID_MEDIA_RW;
- opts->mask = 0;
+ vfsopts->mask = 0;
opts->multiuser = false;
opts->fs_user_id = 0;
- opts->gid = 0;
+ vfsopts->gid = 0;
/* by default, 0MB is reserved */
opts->reserved_mb = 0;
@@ -72,6 +72,7 @@
while ((p = strsep(&options, ",")) != NULL) {
int token;
+
if (!*p)
continue;
@@ -94,7 +95,7 @@
case Opt_gid:
if (match_int(&args[0], &option))
return 0;
- opts->gid = option;
+ vfsopts->gid = option;
break;
case Opt_userid:
if (match_int(&args[0], &option))
@@ -104,7 +105,7 @@
case Opt_mask:
if (match_int(&args[0], &option))
return 0;
- opts->mask = option;
+ vfsopts->mask = option;
break;
case Opt_multiuser:
opts->multiuser = true;
@@ -116,25 +117,81 @@
break;
/* unknown option */
default:
- if (!silent) {
- printk( KERN_ERR "Unrecognized mount option \"%s\" "
- "or missing value", p);
- }
+ if (!silent)
+ pr_err("Unrecognized mount option \"%s\" or missing value", p);
return -EINVAL;
}
}
if (*debug) {
- printk( KERN_INFO "sdcardfs : options - debug:%d\n", *debug);
- printk( KERN_INFO "sdcardfs : options - uid:%d\n",
+ pr_info("sdcardfs : options - debug:%d\n", *debug);
+ pr_info("sdcardfs : options - uid:%d\n",
opts->fs_low_uid);
- printk( KERN_INFO "sdcardfs : options - gid:%d\n",
+ pr_info("sdcardfs : options - gid:%d\n",
opts->fs_low_gid);
}
return 0;
}
+int parse_options_remount(struct super_block *sb, char *options, int silent,
+ struct sdcardfs_vfsmount_options *vfsopts)
+{
+ char *p;
+ substring_t args[MAX_OPT_ARGS];
+ int option;
+ int debug;
+
+ if (!options)
+ return 0;
+
+ while ((p = strsep(&options, ",")) != NULL) {
+ int token;
+
+ if (!*p)
+ continue;
+
+ token = match_token(p, sdcardfs_tokens, args);
+
+ switch (token) {
+ case Opt_debug:
+ debug = 1;
+ break;
+ case Opt_gid:
+ if (match_int(&args[0], &option))
+ return 0;
+ vfsopts->gid = option;
+
+ break;
+ case Opt_mask:
+ if (match_int(&args[0], &option))
+ return 0;
+ vfsopts->mask = option;
+ break;
+ case Opt_multiuser:
+ case Opt_userid:
+ case Opt_fsuid:
+ case Opt_fsgid:
+ case Opt_reserved_mb:
+ pr_warn("Option \"%s\" can't be changed during remount\n", p);
+ break;
+ /* unknown option */
+ default:
+ if (!silent)
+ pr_err("Unrecognized mount option \"%s\" or missing value", p);
+ return -EINVAL;
+ }
+ }
+
+ if (debug) {
+ pr_info("sdcardfs : options - debug:%d\n", debug);
+ pr_info("sdcardfs : options - gid:%d\n", vfsopts->gid);
+ pr_info("sdcardfs : options - mask:%d\n", vfsopts->mask);
+ }
+
+ return 0;
+}
+
#if 0
/*
* our custom d_alloc_root work-alike
@@ -164,57 +221,58 @@
#endif
DEFINE_MUTEX(sdcardfs_super_list_lock);
-LIST_HEAD(sdcardfs_super_list);
EXPORT_SYMBOL_GPL(sdcardfs_super_list_lock);
+LIST_HEAD(sdcardfs_super_list);
EXPORT_SYMBOL_GPL(sdcardfs_super_list);
/*
* There is no need to lock the sdcardfs_super_info's rwsem as there is no
* way anyone can have a reference to the superblock at this point in time.
*/
-static int sdcardfs_read_super(struct super_block *sb, const char *dev_name,
- void *raw_data, int silent)
+static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb,
+ const char *dev_name, void *raw_data, int silent)
{
int err = 0;
int debug;
struct super_block *lower_sb;
struct path lower_path;
struct sdcardfs_sb_info *sb_info;
+ struct sdcardfs_vfsmount_options *mnt_opt = mnt->data;
struct inode *inode;
- printk(KERN_INFO "sdcardfs version 2.0\n");
+ pr_info("sdcardfs version 2.0\n");
if (!dev_name) {
- printk(KERN_ERR
- "sdcardfs: read_super: missing dev_name argument\n");
+ pr_err("sdcardfs: read_super: missing dev_name argument\n");
err = -EINVAL;
goto out;
}
- printk(KERN_INFO "sdcardfs: dev_name -> %s\n", dev_name);
- printk(KERN_INFO "sdcardfs: options -> %s\n", (char *)raw_data);
+ pr_info("sdcardfs: dev_name -> %s\n", dev_name);
+ pr_info("sdcardfs: options -> %s\n", (char *)raw_data);
+ pr_info("sdcardfs: mnt -> %p\n", mnt);
/* parse lower path */
err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY,
&lower_path);
if (err) {
- printk(KERN_ERR "sdcardfs: error accessing lower directory '%s'\n", dev_name);
+ pr_err("sdcardfs: error accessing lower directory '%s'\n", dev_name);
goto out;
}
/* allocate superblock private data */
sb->s_fs_info = kzalloc(sizeof(struct sdcardfs_sb_info), GFP_KERNEL);
if (!SDCARDFS_SB(sb)) {
- printk(KERN_CRIT "sdcardfs: read_super: out of memory\n");
+ pr_crit("sdcardfs: read_super: out of memory\n");
err = -ENOMEM;
goto out_free;
}
sb_info = sb->s_fs_info;
/* parse options */
- err = parse_options(sb, raw_data, silent, &debug, &sb_info->options);
+ err = parse_options(sb, raw_data, silent, &debug, mnt_opt, &sb_info->options);
if (err) {
- printk(KERN_ERR "sdcardfs: invalid options\n");
+ pr_err("sdcardfs: invalid options\n");
goto out_freesbi;
}
@@ -274,23 +332,24 @@
/* setup permission policy */
sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL);
mutex_lock(&sdcardfs_super_list_lock);
- if(sb_info->options.multiuser) {
- setup_derived_state(sb->s_root->d_inode, PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false);
+ if (sb_info->options.multiuser) {
+ setup_derived_state(sb->s_root->d_inode, PERM_PRE_ROOT,
+ sb_info->options.fs_user_id, AID_ROOT,
+ false, SDCARDFS_I(sb->s_root->d_inode)->data);
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name);
- /*err = prepare_dir(sb_info->obbpath_s,
- sb_info->options.fs_low_uid,
- sb_info->options.fs_low_gid, 00755);*/
} else {
- setup_derived_state(sb->s_root->d_inode, PERM_ROOT, sb_info->options.fs_low_uid, AID_ROOT, false);
+ setup_derived_state(sb->s_root->d_inode, PERM_ROOT,
+ sb_info->options.fs_user_id, AID_ROOT,
+ false, SDCARDFS_I(sb->s_root->d_inode)->data);
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name);
}
- fix_derived_permission(sb->s_root->d_inode);
+ fixup_tmp_permissions(sb->s_root->d_inode);
sb_info->sb = sb;
list_add(&sb_info->list, &sdcardfs_super_list);
mutex_unlock(&sdcardfs_super_list_lock);
if (!silent)
- printk(KERN_INFO "sdcardfs: mounted on top of %s type %s\n",
+ pr_info("sdcardfs: mounted on top of %s type %s\n",
dev_name, lower_sb->s_type->name);
goto out; /* all is well */
@@ -313,9 +372,11 @@
}
/* A feature which supports mount_nodev() with options */
-static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type,
- int flags, const char *dev_name, void *data,
- int (*fill_super)(struct super_block *, const char *, void *, int))
+static struct dentry *mount_nodev_with_options(struct vfsmount *mnt,
+ struct file_system_type *fs_type, int flags,
+ const char *dev_name, void *data,
+ int (*fill_super)(struct vfsmount *, struct super_block *,
+ const char *, void *, int))
{
int error;
@@ -326,7 +387,7 @@
s->s_flags = flags;
- error = fill_super(s, dev_name, data, flags & MS_SILENT ? 1 : 0);
+ error = fill_super(mnt, s, dev_name, data, flags & MS_SILENT ? 1 : 0);
if (error) {
deactivate_locked_super(s);
return ERR_PTR(error);
@@ -335,19 +396,34 @@
return dget(s->s_root);
}
-struct dentry *sdcardfs_mount(struct file_system_type *fs_type, int flags,
+static struct dentry *sdcardfs_mount(struct vfsmount *mnt,
+ struct file_system_type *fs_type, int flags,
const char *dev_name, void *raw_data)
{
/*
* dev_name is a lower_path_name,
* raw_data is a option string.
*/
- return mount_nodev_with_options(fs_type, flags, dev_name,
- raw_data, sdcardfs_read_super);
+ return mount_nodev_with_options(mnt, fs_type, flags, dev_name,
+ raw_data, sdcardfs_read_super);
}
-void sdcardfs_kill_sb(struct super_block *sb) {
+static struct dentry *sdcardfs_mount_wrn(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *raw_data)
+{
+ WARN(1, "sdcardfs does not support mount. Use mount2.\n");
+ return ERR_PTR(-EINVAL);
+}
+
+void *sdcardfs_alloc_mnt_data(void)
+{
+ return kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL);
+}
+
+void sdcardfs_kill_sb(struct super_block *sb)
+{
struct sdcardfs_sb_info *sbi;
+
if (sb->s_magic == SDCARDFS_SUPER_MAGIC) {
sbi = SDCARDFS_SB(sb);
mutex_lock(&sdcardfs_super_list_lock);
@@ -360,10 +436,13 @@
static struct file_system_type sdcardfs_fs_type = {
.owner = THIS_MODULE,
.name = SDCARDFS_NAME,
- .mount = sdcardfs_mount,
+ .mount = sdcardfs_mount_wrn,
+ .mount2 = sdcardfs_mount,
+ .alloc_mnt_data = sdcardfs_alloc_mnt_data,
.kill_sb = sdcardfs_kill_sb,
.fs_flags = 0,
};
+MODULE_ALIAS_FS(SDCARDFS_NAME);
static int __init init_sdcardfs_fs(void)
{
@@ -399,10 +478,15 @@
pr_info("Completed sdcardfs module unload\n");
}
-MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University"
- " (http://www.fsl.cs.sunysb.edu/)");
-MODULE_DESCRIPTION("Wrapfs " SDCARDFS_VERSION
- " (http://wrapfs.filesystems.org/)");
+/* Original wrapfs authors */
+MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University (http://www.fsl.cs.sunysb.edu/)");
+
+/* Original sdcardfs authors */
+MODULE_AUTHOR("Woojoong Lee, Daeho Jeong, Kitae Lee, Yeongjin Gil System Memory Lab., Samsung Electronics");
+
+/* Current maintainer */
+MODULE_AUTHOR("Daniel Rosenberg, Google");
+MODULE_DESCRIPTION("Sdcardfs " SDCARDFS_VERSION);
MODULE_LICENSE("GPL");
module_init(init_sdcardfs_fs);
diff --git a/fs/sdcardfs/mmap.c b/fs/sdcardfs/mmap.c
index c807d7f..96759ca 100755
--- a/fs/sdcardfs/mmap.c
+++ b/fs/sdcardfs/mmap.c
@@ -23,28 +23,46 @@
static int sdcardfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
int err;
- struct file *file, *lower_file;
+ struct file *file;
const struct vm_operations_struct *lower_vm_ops;
- struct vm_area_struct lower_vma;
- memcpy(&lower_vma, vma, sizeof(struct vm_area_struct));
- file = lower_vma.vm_file;
+ file = (struct file *)vma->vm_private_data;
lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops;
BUG_ON(!lower_vm_ops);
- lower_file = sdcardfs_lower_file(file);
- /*
- * XXX: vm_ops->fault may be called in parallel. Because we have to
- * resort to temporarily changing the vma->vm_file to point to the
- * lower file, a concurrent invocation of sdcardfs_fault could see a
- * different value. In this workaround, we keep a different copy of
- * the vma structure in our stack, so we never expose a different
- * value of the vma->vm_file called to us, even temporarily. A
- * better fix would be to change the calling semantics of ->fault to
- * take an explicit file pointer.
- */
- lower_vma.vm_file = lower_file;
- err = lower_vm_ops->fault(&lower_vma, vmf);
+ err = lower_vm_ops->fault(vma, vmf);
+ return err;
+}
+
+static void sdcardfs_vm_open(struct vm_area_struct *vma)
+{
+ struct file *file = (struct file *)vma->vm_private_data;
+
+ get_file(file);
+}
+
+static void sdcardfs_vm_close(struct vm_area_struct *vma)
+{
+ struct file *file = (struct file *)vma->vm_private_data;
+
+ fput(file);
+}
+
+static int sdcardfs_page_mkwrite(struct vm_area_struct *vma,
+ struct vm_fault *vmf)
+{
+ int err = 0;
+ struct file *file;
+ const struct vm_operations_struct *lower_vm_ops;
+
+ file = (struct file *)vma->vm_private_data;
+ lower_vm_ops = SDCARDFS_F(file)->lower_vm_ops;
+ BUG_ON(!lower_vm_ops);
+ if (!lower_vm_ops->page_mkwrite)
+ goto out;
+
+ err = lower_vm_ops->page_mkwrite(vma, vmf);
+out:
return err;
}
@@ -53,30 +71,20 @@
unsigned long nr_segs)
{
/*
- * This function returns zero on purpose in order to support direct IO.
- * __dentry_open checks a_ops->direct_IO and returns EINVAL if it is null.
- *
- * However, this function won't be called by certain file operations
- * including generic fs functions. * reads and writes are delivered to
- * the lower file systems and the direct IOs will be handled by them.
- *
- * NOTE: exceptionally, on the recent kernels (since Linux 3.8.x),
- * swap_writepage invokes this function directly.
+ * This function should never be called directly. We need it
+ * to exist, to get past a check in open_check_o_direct(),
+ * which is called from do_last().
*/
- printk(KERN_INFO "%s, operation is not supported\n", __func__);
- return 0;
+ return -EINVAL;
}
-/*
- * XXX: the default address_space_ops for sdcardfs is empty. We cannot set
- * our inode->i_mapping->a_ops to NULL because too many code paths expect
- * the a_ops vector to be non-NULL.
- */
const struct address_space_operations sdcardfs_aops = {
- /* empty on purpose */
.direct_IO = sdcardfs_direct_IO,
};
const struct vm_operations_struct sdcardfs_vm_ops = {
.fault = sdcardfs_fault,
+ .page_mkwrite = sdcardfs_page_mkwrite,
+ .open = sdcardfs_vm_open,
+ .close = sdcardfs_vm_close,
};
diff --git a/fs/sdcardfs/multiuser.h b/fs/sdcardfs/multiuser.h
index 923ba10..85341e7 100755
--- a/fs/sdcardfs/multiuser.h
+++ b/fs/sdcardfs/multiuser.h
@@ -18,20 +18,36 @@
* General Public License.
*/
-#define MULTIUSER_APP_PER_USER_RANGE 100000
+#define AID_USER_OFFSET 100000 /* offset for uid ranges for each user */
+#define AID_APP_START 10000 /* first app user */
+#define AID_APP_END 19999 /* last app user */
+#define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */
+#define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */
+#define AID_EXT_CACHE_GID_START 40000 /* start of gids for apps to mark external cached data */
+#define AID_EXT_CACHE_GID_END 49999 /* end of gids for apps to mark external cached data */
+#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
typedef uid_t userid_t;
typedef uid_t appid_t;
-static inline userid_t multiuser_get_user_id(uid_t uid) {
- return uid / MULTIUSER_APP_PER_USER_RANGE;
+static inline uid_t multiuser_get_uid(userid_t user_id, appid_t app_id)
+{
+ return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET);
}
-static inline appid_t multiuser_get_app_id(uid_t uid) {
- return uid % MULTIUSER_APP_PER_USER_RANGE;
+static inline bool uid_is_app(uid_t uid)
+{
+ appid_t appid = uid % AID_USER_OFFSET;
+
+ return appid >= AID_APP_START && appid <= AID_APP_END;
}
-static inline uid_t multiuser_get_uid(userid_t userId, appid_t appId) {
- return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE);
+static inline gid_t multiuser_get_ext_cache_gid(uid_t uid)
+{
+ return uid - AID_APP_START + AID_EXT_CACHE_GID_START;
}
+static inline gid_t multiuser_get_ext_gid(uid_t uid)
+{
+ return uid - AID_APP_START + AID_EXT_GID_START;
+}
diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c
index 0cf9c34..f120bcc 100755
--- a/fs/sdcardfs/packagelist.c
+++ b/fs/sdcardfs/packagelist.c
@@ -20,8 +20,10 @@
#include "sdcardfs.h"
#include <linux/hashtable.h>
+#include <linux/ctype.h>
#include <linux/delay.h>
-
+#include <linux/radix-tree.h>
+#include <linux/dcache.h>
#include <linux/init.h>
#include <linux/module.h>
@@ -29,389 +31,840 @@
#include <linux/configfs.h>
-#define STRING_BUF_SIZE (512)
-
struct hashtable_entry {
struct hlist_node hlist;
- void *key;
- unsigned int value;
+ struct hlist_node dlist; /* for deletion cleanup */
+ struct qstr key;
+ atomic_t value;
};
-struct sb_list {
- struct super_block *sb;
- struct list_head list;
-};
+static DEFINE_HASHTABLE(package_to_appid, 8);
+static DEFINE_HASHTABLE(package_to_userid, 8);
+static DEFINE_HASHTABLE(ext_to_groupid, 8);
-struct packagelist_data {
- DECLARE_HASHTABLE(package_to_appid,8);
- struct mutex hashtable_lock;
-
-};
-
-static struct packagelist_data *pkgl_data_all;
static struct kmem_cache *hashtable_entry_cachep;
-static unsigned int str_hash(const char *key) {
- int i;
- unsigned int h = strlen(key);
- char *data = (char *)key;
+static unsigned int full_name_case_hash(const unsigned char *name, unsigned int len)
+{
+ unsigned long hash = init_name_hash();
- for (i = 0; i < strlen(key); i++) {
- h = h * 31 + *data;
- data++;
- }
- return h;
+ while (len--)
+ hash = partial_name_hash(tolower(*name++), hash);
+ return end_name_hash(hash);
}
-appid_t get_appid(void *pkgl_id, const char *app_name)
+static inline void qstr_init(struct qstr *q, const char *name)
{
- struct packagelist_data *pkgl_dat = pkgl_data_all;
+ q->name = name;
+ q->len = strlen(q->name);
+ q->hash = full_name_case_hash(q->name, q->len);
+}
+
+static inline int qstr_copy(const struct qstr *src, struct qstr *dest)
+{
+ dest->name = kstrdup(src->name, GFP_KERNEL);
+ dest->hash_len = src->hash_len;
+ return !!dest->name;
+}
+
+
+static appid_t __get_appid(const struct qstr *key)
+{
struct hashtable_entry *hash_cur;
- unsigned int hash = str_hash(app_name);
+ unsigned int hash = key->hash;
appid_t ret_id;
- mutex_lock(&pkgl_dat->hashtable_lock);
- hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) {
- if (!strcasecmp(app_name, hash_cur->key)) {
- ret_id = (appid_t)hash_cur->value;
- mutex_unlock(&pkgl_dat->hashtable_lock);
+ rcu_read_lock();
+ hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key)) {
+ ret_id = atomic_read(&hash_cur->value);
+ rcu_read_unlock();
return ret_id;
}
}
- mutex_unlock(&pkgl_dat->hashtable_lock);
+ rcu_read_unlock();
return 0;
}
+appid_t get_appid(const char *key)
+{
+ struct qstr q;
+
+ qstr_init(&q, key);
+ return __get_appid(&q);
+}
+
+static appid_t __get_ext_gid(const struct qstr *key)
+{
+ struct hashtable_entry *hash_cur;
+ unsigned int hash = key->hash;
+ appid_t ret_id;
+
+ rcu_read_lock();
+ hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key)) {
+ ret_id = atomic_read(&hash_cur->value);
+ rcu_read_unlock();
+ return ret_id;
+ }
+ }
+ rcu_read_unlock();
+ return 0;
+}
+
+appid_t get_ext_gid(const char *key)
+{
+ struct qstr q;
+
+ qstr_init(&q, key);
+ return __get_ext_gid(&q);
+}
+
+static appid_t __is_excluded(const struct qstr *app_name, userid_t user)
+{
+ struct hashtable_entry *hash_cur;
+ unsigned int hash = app_name->hash;
+
+ rcu_read_lock();
+ hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
+ if (atomic_read(&hash_cur->value) == user &&
+ qstr_case_eq(app_name, &hash_cur->key)) {
+ rcu_read_unlock();
+ return 1;
+ }
+ }
+ rcu_read_unlock();
+ return 0;
+}
+
+appid_t is_excluded(const char *key, userid_t user)
+{
+ struct qstr q;
+
+ qstr_init(&q, key);
+ return __is_excluded(&q, user);
+}
+
/* Kernel has already enforced everything we returned through
* derive_permissions_locked(), so this is used to lock down access
- * even further, such as enforcing that apps hold sdcard_rw. */
-int check_caller_access_to_name(struct inode *parent_node, const char* name) {
+ * even further, such as enforcing that apps hold sdcard_rw.
+ */
+int check_caller_access_to_name(struct inode *parent_node, const struct qstr *name)
+{
+ struct qstr q_autorun = QSTR_LITERAL("autorun.inf");
+ struct qstr q__android_secure = QSTR_LITERAL(".android_secure");
+ struct qstr q_android_secure = QSTR_LITERAL("android_secure");
/* Always block security-sensitive files at root */
- if (parent_node && SDCARDFS_I(parent_node)->perm == PERM_ROOT) {
- if (!strcasecmp(name, "autorun.inf")
- || !strcasecmp(name, ".android_secure")
- || !strcasecmp(name, "android_secure")) {
+ if (parent_node && SDCARDFS_I(parent_node)->data->perm == PERM_ROOT) {
+ if (qstr_case_eq(name, &q_autorun)
+ || qstr_case_eq(name, &q__android_secure)
+ || qstr_case_eq(name, &q_android_secure)) {
return 0;
}
}
/* Root always has access; access for any other UIDs should always
- * be controlled through packages.list. */
- if (current_fsuid() == 0) {
+ * be controlled through packages.list.
+ */
+ if (current_fsuid() == 0)
return 1;
- }
/* No extra permissions to enforce */
return 1;
}
/* This function is used when file opening. The open flags must be
- * checked before calling check_caller_access_to_name() */
-int open_flags_to_access_mode(int open_flags) {
- if((open_flags & O_ACCMODE) == O_RDONLY) {
- return 0; /* R_OK */
- } else if ((open_flags & O_ACCMODE) == O_WRONLY) {
- return 1; /* W_OK */
- } else {
- /* Probably O_RDRW, but treat as default to be safe */
- return 1; /* R_OK | W_OK */
- }
-}
-
-static int insert_str_to_int_lock(struct packagelist_data *pkgl_dat, char *key,
- unsigned int value)
+ * checked before calling check_caller_access_to_name()
+ */
+int open_flags_to_access_mode(int open_flags)
{
- struct hashtable_entry *hash_cur;
- struct hashtable_entry *new_entry;
- unsigned int hash = str_hash(key);
-
- hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) {
- if (!strcasecmp(key, hash_cur->key)) {
- hash_cur->value = value;
- return 0;
- }
- }
- new_entry = kmem_cache_alloc(hashtable_entry_cachep, GFP_KERNEL);
- if (!new_entry)
- return -ENOMEM;
- new_entry->key = kstrdup(key, GFP_KERNEL);
- new_entry->value = value;
- hash_add(pkgl_dat->package_to_appid, &new_entry->hlist, hash);
- return 0;
+ if ((open_flags & O_ACCMODE) == O_RDONLY)
+ return 0; /* R_OK */
+ if ((open_flags & O_ACCMODE) == O_WRONLY)
+ return 1; /* W_OK */
+ /* Probably O_RDRW, but treat as default to be safe */
+ return 1; /* R_OK | W_OK */
}
-static void fixup_perms(struct super_block *sb) {
- if (sb && sb->s_magic == SDCARDFS_SUPER_MAGIC) {
- mutex_lock(&sb->s_root->d_inode->i_mutex);
- get_derive_permissions_recursive(sb->s_root);
- mutex_unlock(&sb->s_root->d_inode->i_mutex);
- }
-}
+static struct hashtable_entry *alloc_hashtable_entry(const struct qstr *key,
+ appid_t value)
+{
+ struct hashtable_entry *ret = kmem_cache_alloc(hashtable_entry_cachep,
+ GFP_KERNEL);
+ if (!ret)
+ return NULL;
+ INIT_HLIST_NODE(&ret->dlist);
+ INIT_HLIST_NODE(&ret->hlist);
-static int insert_str_to_int(struct packagelist_data *pkgl_dat, char *key,
- unsigned int value) {
- int ret;
- struct sdcardfs_sb_info *sbinfo;
- mutex_lock(&sdcardfs_super_list_lock);
- mutex_lock(&pkgl_dat->hashtable_lock);
- ret = insert_str_to_int_lock(pkgl_dat, key, value);
- mutex_unlock(&pkgl_dat->hashtable_lock);
-
- list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
- if (sbinfo) {
- fixup_perms(sbinfo->sb);
- }
+ if (!qstr_copy(key, &ret->key)) {
+ kmem_cache_free(hashtable_entry_cachep, ret);
+ return NULL;
}
- mutex_unlock(&sdcardfs_super_list_lock);
+
+ atomic_set(&ret->value, value);
return ret;
}
-static void remove_str_to_int_lock(struct hashtable_entry *h_entry) {
- kfree(h_entry->key);
- hash_del(&h_entry->hlist);
- kmem_cache_free(hashtable_entry_cachep, h_entry);
+static int insert_packagelist_appid_entry_locked(const struct qstr *key, appid_t value)
+{
+ struct hashtable_entry *hash_cur;
+ struct hashtable_entry *new_entry;
+ unsigned int hash = key->hash;
+
+ hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key)) {
+ atomic_set(&hash_cur->value, value);
+ return 0;
+ }
+ }
+ new_entry = alloc_hashtable_entry(key, value);
+ if (!new_entry)
+ return -ENOMEM;
+ hash_add_rcu(package_to_appid, &new_entry->hlist, hash);
+ return 0;
}
-static void remove_str_to_int(struct packagelist_data *pkgl_dat, const char *key)
+static int insert_ext_gid_entry_locked(const struct qstr *key, appid_t value)
+{
+ struct hashtable_entry *hash_cur;
+ struct hashtable_entry *new_entry;
+ unsigned int hash = key->hash;
+
+ /* An extension can only belong to one gid */
+ hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key))
+ return -EINVAL;
+ }
+ new_entry = alloc_hashtable_entry(key, value);
+ if (!new_entry)
+ return -ENOMEM;
+ hash_add_rcu(ext_to_groupid, &new_entry->hlist, hash);
+ return 0;
+}
+
+static int insert_userid_exclude_entry_locked(const struct qstr *key, userid_t value)
+{
+ struct hashtable_entry *hash_cur;
+ struct hashtable_entry *new_entry;
+ unsigned int hash = key->hash;
+
+ /* Only insert if not already present */
+ hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
+ if (atomic_read(&hash_cur->value) == value &&
+ qstr_case_eq(key, &hash_cur->key))
+ return 0;
+ }
+ new_entry = alloc_hashtable_entry(key, value);
+ if (!new_entry)
+ return -ENOMEM;
+ hash_add_rcu(package_to_userid, &new_entry->hlist, hash);
+ return 0;
+}
+
+static void fixup_all_perms_name(const struct qstr *key)
{
struct sdcardfs_sb_info *sbinfo;
- struct hashtable_entry *hash_cur;
- unsigned int hash = str_hash(key);
+ struct limit_search limit = {
+ .flags = BY_NAME,
+ .name = QSTR_INIT(key->name, key->len),
+ };
+ list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
+ if (sbinfo_has_sdcard_magic(sbinfo))
+ fixup_perms_recursive(sbinfo->sb->s_root, &limit);
+ }
+}
+
+static void fixup_all_perms_name_userid(const struct qstr *key, userid_t userid)
+{
+ struct sdcardfs_sb_info *sbinfo;
+ struct limit_search limit = {
+ .flags = BY_NAME | BY_USERID,
+ .name = QSTR_INIT(key->name, key->len),
+ .userid = userid,
+ };
+ list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
+ if (sbinfo_has_sdcard_magic(sbinfo))
+ fixup_perms_recursive(sbinfo->sb->s_root, &limit);
+ }
+}
+
+static void fixup_all_perms_userid(userid_t userid)
+{
+ struct sdcardfs_sb_info *sbinfo;
+ struct limit_search limit = {
+ .flags = BY_USERID,
+ .userid = userid,
+ };
+ list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
+ if (sbinfo_has_sdcard_magic(sbinfo))
+ fixup_perms_recursive(sbinfo->sb->s_root, &limit);
+ }
+}
+
+static int insert_packagelist_entry(const struct qstr *key, appid_t value)
+{
+ int err;
+
mutex_lock(&sdcardfs_super_list_lock);
- mutex_lock(&pkgl_dat->hashtable_lock);
- hash_for_each_possible(pkgl_dat->package_to_appid, hash_cur, hlist, hash) {
- if (!strcasecmp(key, hash_cur->key)) {
- remove_str_to_int_lock(hash_cur);
+ err = insert_packagelist_appid_entry_locked(key, value);
+ if (!err)
+ fixup_all_perms_name(key);
+ mutex_unlock(&sdcardfs_super_list_lock);
+
+ return err;
+}
+
+static int insert_ext_gid_entry(const struct qstr *key, appid_t value)
+{
+ int err;
+
+ mutex_lock(&sdcardfs_super_list_lock);
+ err = insert_ext_gid_entry_locked(key, value);
+ mutex_unlock(&sdcardfs_super_list_lock);
+
+ return err;
+}
+
+static int insert_userid_exclude_entry(const struct qstr *key, userid_t value)
+{
+ int err;
+
+ mutex_lock(&sdcardfs_super_list_lock);
+ err = insert_userid_exclude_entry_locked(key, value);
+ if (!err)
+ fixup_all_perms_name_userid(key, value);
+ mutex_unlock(&sdcardfs_super_list_lock);
+
+ return err;
+}
+
+static void free_hashtable_entry(struct hashtable_entry *entry)
+{
+ kfree(entry->key.name);
+ kmem_cache_free(hashtable_entry_cachep, entry);
+}
+
+static void remove_packagelist_entry_locked(const struct qstr *key)
+{
+ struct hashtable_entry *hash_cur;
+ unsigned int hash = key->hash;
+ struct hlist_node *h_t;
+ HLIST_HEAD(free_list);
+
+ hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key)) {
+ hash_del_rcu(&hash_cur->hlist);
+ hlist_add_head(&hash_cur->dlist, &free_list);
+ }
+ }
+ hash_for_each_possible_rcu(package_to_appid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key)) {
+ hash_del_rcu(&hash_cur->hlist);
+ hlist_add_head(&hash_cur->dlist, &free_list);
break;
}
}
- mutex_unlock(&pkgl_dat->hashtable_lock);
- list_for_each_entry(sbinfo, &sdcardfs_super_list, list) {
- if (sbinfo) {
- fixup_perms(sbinfo->sb);
- }
- }
- mutex_unlock(&sdcardfs_super_list_lock);
- return;
+ synchronize_rcu();
+ hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist)
+ free_hashtable_entry(hash_cur);
}
-static void remove_all_hashentrys(struct packagelist_data *pkgl_dat)
+static void remove_packagelist_entry(const struct qstr *key)
+{
+ mutex_lock(&sdcardfs_super_list_lock);
+ remove_packagelist_entry_locked(key);
+ fixup_all_perms_name(key);
+ mutex_unlock(&sdcardfs_super_list_lock);
+}
+
+static void remove_ext_gid_entry_locked(const struct qstr *key, gid_t group)
+{
+ struct hashtable_entry *hash_cur;
+ unsigned int hash = key->hash;
+
+ hash_for_each_possible_rcu(ext_to_groupid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key) && atomic_read(&hash_cur->value) == group) {
+ hash_del_rcu(&hash_cur->hlist);
+ synchronize_rcu();
+ free_hashtable_entry(hash_cur);
+ break;
+ }
+ }
+}
+
+static void remove_ext_gid_entry(const struct qstr *key, gid_t group)
+{
+ mutex_lock(&sdcardfs_super_list_lock);
+ remove_ext_gid_entry_locked(key, group);
+ mutex_unlock(&sdcardfs_super_list_lock);
+}
+
+static void remove_userid_all_entry_locked(userid_t userid)
{
struct hashtable_entry *hash_cur;
struct hlist_node *h_t;
+ HLIST_HEAD(free_list);
int i;
- mutex_lock(&pkgl_dat->hashtable_lock);
- hash_for_each_safe(pkgl_dat->package_to_appid, i, h_t, hash_cur, hlist)
- remove_str_to_int_lock(hash_cur);
- mutex_unlock(&pkgl_dat->hashtable_lock);
- hash_init(pkgl_dat->package_to_appid);
-}
-static struct packagelist_data * packagelist_create(void)
-{
- struct packagelist_data *pkgl_dat;
-
- pkgl_dat = kmalloc(sizeof(*pkgl_dat), GFP_KERNEL | __GFP_ZERO);
- if (!pkgl_dat) {
- printk(KERN_ERR "sdcardfs: Failed to create hash\n");
- return ERR_PTR(-ENOMEM);
+ hash_for_each_rcu(package_to_userid, i, hash_cur, hlist) {
+ if (atomic_read(&hash_cur->value) == userid) {
+ hash_del_rcu(&hash_cur->hlist);
+ hlist_add_head(&hash_cur->dlist, &free_list);
+ }
}
-
- mutex_init(&pkgl_dat->hashtable_lock);
- hash_init(pkgl_dat->package_to_appid);
-
- return pkgl_dat;
+ synchronize_rcu();
+ hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist) {
+ free_hashtable_entry(hash_cur);
+ }
}
-static void packagelist_destroy(struct packagelist_data *pkgl_dat)
+static void remove_userid_all_entry(userid_t userid)
{
- remove_all_hashentrys(pkgl_dat);
- printk(KERN_INFO "sdcardfs: destroyed packagelist pkgld\n");
- kfree(pkgl_dat);
+ mutex_lock(&sdcardfs_super_list_lock);
+ remove_userid_all_entry_locked(userid);
+ fixup_all_perms_userid(userid);
+ mutex_unlock(&sdcardfs_super_list_lock);
}
-struct package_appid {
+static void remove_userid_exclude_entry_locked(const struct qstr *key, userid_t userid)
+{
+ struct hashtable_entry *hash_cur;
+ unsigned int hash = key->hash;
+
+ hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(key, &hash_cur->key) &&
+ atomic_read(&hash_cur->value) == userid) {
+ hash_del_rcu(&hash_cur->hlist);
+ synchronize_rcu();
+ free_hashtable_entry(hash_cur);
+ break;
+ }
+ }
+}
+
+static void remove_userid_exclude_entry(const struct qstr *key, userid_t userid)
+{
+ mutex_lock(&sdcardfs_super_list_lock);
+ remove_userid_exclude_entry_locked(key, userid);
+ fixup_all_perms_name_userid(key, userid);
+ mutex_unlock(&sdcardfs_super_list_lock);
+}
+
+static void packagelist_destroy(void)
+{
+ struct hashtable_entry *hash_cur;
+ struct hlist_node *h_t;
+ HLIST_HEAD(free_list);
+ int i;
+
+ mutex_lock(&sdcardfs_super_list_lock);
+ hash_for_each_rcu(package_to_appid, i, hash_cur, hlist) {
+ hash_del_rcu(&hash_cur->hlist);
+ hlist_add_head(&hash_cur->dlist, &free_list);
+ }
+ hash_for_each_rcu(package_to_userid, i, hash_cur, hlist) {
+ hash_del_rcu(&hash_cur->hlist);
+ hlist_add_head(&hash_cur->dlist, &free_list);
+ }
+ synchronize_rcu();
+ hlist_for_each_entry_safe(hash_cur, h_t, &free_list, dlist)
+ free_hashtable_entry(hash_cur);
+ mutex_unlock(&sdcardfs_super_list_lock);
+ pr_info("sdcardfs: destroyed packagelist pkgld\n");
+}
+
+struct package_details {
struct config_item item;
- int add_pid;
+ struct qstr name;
};
-static inline struct package_appid *to_package_appid(struct config_item *item)
+static inline struct package_details *to_package_details(struct config_item *item)
{
- return item ? container_of(item, struct package_appid, item) : NULL;
+ return item ? container_of(item, struct package_details, item) : NULL;
}
-static struct configfs_attribute package_appid_attr_add_pid = {
- .ca_owner = THIS_MODULE,
- .ca_name = "appid",
- .ca_mode = S_IRUGO | S_IWUGO,
-};
+CONFIGFS_ATTR_STRUCT(package_details);
+#define PACKAGE_DETAILS_ATTR(_name, _mode, _show, _store) \
+struct package_details_attribute package_details_attr_##_name = __CONFIGFS_ATTR(_name, _mode, _show, _store)
+#define PACKAGE_DETAILS_ATTRIBUTE(name) (&package_details_attr_##name.attr)
-static struct configfs_attribute *package_appid_attrs[] = {
- &package_appid_attr_add_pid,
- NULL,
-};
-
-static ssize_t package_appid_attr_show(struct config_item *item,
- struct configfs_attribute *attr,
+static ssize_t package_details_appid_show(struct package_details *package_details,
char *page)
{
- ssize_t count;
- count = sprintf(page, "%d\n", get_appid(pkgl_data_all, item->ci_name));
- return count;
+ return scnprintf(page, PAGE_SIZE, "%u\n", __get_appid(&package_details->name));
}
-static ssize_t package_appid_attr_store(struct config_item *item,
- struct configfs_attribute *attr,
+static ssize_t package_details_appid_store(struct package_details *package_details,
const char *page, size_t count)
{
- struct package_appid *package_appid = to_package_appid(item);
- unsigned long tmp;
- char *p = (char *) page;
+ unsigned int tmp;
int ret;
- tmp = simple_strtoul(p, &p, 10);
- if (!p || (*p && (*p != '\n')))
- return -EINVAL;
+ ret = kstrtouint(page, 10, &tmp);
+ if (ret)
+ return ret;
- if (tmp > INT_MAX)
- return -ERANGE;
- ret = insert_str_to_int(pkgl_data_all, item->ci_name, (unsigned int)tmp);
- package_appid->add_pid = tmp;
+ ret = insert_packagelist_entry(&package_details->name, tmp);
+
if (ret)
return ret;
return count;
}
-static void package_appid_release(struct config_item *item)
+static ssize_t package_details_excluded_userids_show(struct package_details *package_details,
+ char *page)
{
- printk(KERN_INFO "sdcardfs: removing %s\n", item->ci_dentry->d_name.name);
- /* item->ci_name is freed already, so we rely on the dentry */
- remove_str_to_int(pkgl_data_all, item->ci_dentry->d_name.name);
- kfree(to_package_appid(item));
+ struct hashtable_entry *hash_cur;
+ unsigned int hash = package_details->name.hash;
+ int count = 0;
+
+ rcu_read_lock();
+ hash_for_each_possible_rcu(package_to_userid, hash_cur, hlist, hash) {
+ if (qstr_case_eq(&package_details->name, &hash_cur->key))
+ count += scnprintf(page + count, PAGE_SIZE - count,
+ "%d ", atomic_read(&hash_cur->value));
+ }
+ rcu_read_unlock();
+ if (count)
+ count--;
+ count += scnprintf(page + count, PAGE_SIZE - count, "\n");
+ return count;
}
-static struct configfs_item_operations package_appid_item_ops = {
- .release = package_appid_release,
- .show_attribute = package_appid_attr_show,
- .store_attribute = package_appid_attr_store,
-};
-
-static struct config_item_type package_appid_type = {
- .ct_item_ops = &package_appid_item_ops,
- .ct_attrs = package_appid_attrs,
- .ct_owner = THIS_MODULE,
-};
-
-
-struct sdcardfs_packages {
- struct config_group group;
-};
-
-static inline struct sdcardfs_packages *to_sdcardfs_packages(struct config_item *item)
+static ssize_t package_details_excluded_userids_store(struct package_details *package_details,
+ const char *page, size_t count)
{
- return item ? container_of(to_config_group(item), struct sdcardfs_packages, group) : NULL;
+ unsigned int tmp;
+ int ret;
+
+ ret = kstrtouint(page, 10, &tmp);
+ if (ret)
+ return ret;
+
+ ret = insert_userid_exclude_entry(&package_details->name, tmp);
+
+ if (ret)
+ return ret;
+
+ return count;
}
-static struct config_item *sdcardfs_packages_make_item(struct config_group *group, const char *name)
+static ssize_t package_details_clear_userid_store(struct package_details *package_details,
+ const char *page, size_t count)
{
- struct package_appid *package_appid;
+ unsigned int tmp;
+ int ret;
- package_appid = kzalloc(sizeof(struct package_appid), GFP_KERNEL);
- if (!package_appid)
- return ERR_PTR(-ENOMEM);
-
- config_item_init_type_name(&package_appid->item, name,
- &package_appid_type);
-
- package_appid->add_pid = 0;
-
- return &package_appid->item;
+ ret = kstrtouint(page, 10, &tmp);
+ if (ret)
+ return ret;
+ remove_userid_exclude_entry(&package_details->name, tmp);
+ return count;
}
-static struct configfs_attribute sdcardfs_packages_attr_description = {
- .ca_owner = THIS_MODULE,
- .ca_name = "packages_gid.list",
- .ca_mode = S_IRUGO,
-};
+static void package_details_release(struct config_item *item)
+{
+ struct package_details *package_details = to_package_details(item);
-static struct configfs_attribute *sdcardfs_packages_attrs[] = {
- &sdcardfs_packages_attr_description,
+ pr_info("sdcardfs: removing %s\n", package_details->name.name);
+ remove_packagelist_entry(&package_details->name);
+ kfree(package_details->name.name);
+ kfree(package_details);
+}
+
+PACKAGE_DETAILS_ATTR(appid, S_IRUGO | S_IWUGO, package_details_appid_show, package_details_appid_store);
+PACKAGE_DETAILS_ATTR(excluded_userids, S_IRUGO | S_IWUGO,
+ package_details_excluded_userids_show, package_details_excluded_userids_store);
+PACKAGE_DETAILS_ATTR(clear_userid, S_IWUGO, NULL, package_details_clear_userid_store);
+
+static struct configfs_attribute *package_details_attrs[] = {
+ PACKAGE_DETAILS_ATTRIBUTE(appid),
+ PACKAGE_DETAILS_ATTRIBUTE(excluded_userids),
+ PACKAGE_DETAILS_ATTRIBUTE(clear_userid),
NULL,
};
-static ssize_t packages_attr_show(struct config_item *item,
- struct configfs_attribute *attr,
+CONFIGFS_ATTR_OPS(package_details);
+
+static struct configfs_item_operations package_details_item_ops = {
+ .release = package_details_release,
+ .show_attribute = package_details_attr_show,
+ .store_attribute = package_details_attr_store,
+};
+
+static struct config_item_type package_appid_type = {
+ .ct_item_ops = &package_details_item_ops,
+ .ct_attrs = package_details_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+struct extensions_value {
+ struct config_group group;
+ unsigned int num;
+};
+
+struct extension_details {
+ struct config_item item;
+ struct qstr name;
+ unsigned int num;
+};
+
+static inline struct extensions_value *to_extensions_value(struct config_item *item)
+{
+ return item ? container_of(to_config_group(item), struct extensions_value, group) : NULL;
+}
+
+static inline struct extension_details *to_extension_details(struct config_item *item)
+{
+ return item ? container_of(item, struct extension_details, item) : NULL;
+}
+
+static void extension_details_release(struct config_item *item)
+{
+ struct extension_details *extension_details = to_extension_details(item);
+
+ pr_info("sdcardfs: No longer mapping %s files to gid %d\n",
+ extension_details->name.name, extension_details->num);
+ remove_ext_gid_entry(&extension_details->name, extension_details->num);
+ kfree(extension_details->name.name);
+ kfree(extension_details);
+}
+
+static struct configfs_item_operations extension_details_item_ops = {
+ .release = extension_details_release,
+};
+
+static struct config_item_type extension_details_type = {
+ .ct_item_ops = &extension_details_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_item *extension_details_make_item(struct config_group *group, const char *name)
+{
+ struct extensions_value *extensions_value = to_extensions_value(&group->cg_item);
+ struct extension_details *extension_details = kzalloc(sizeof(struct extension_details), GFP_KERNEL);
+ const char *tmp;
+ int ret;
+
+ if (!extension_details)
+ return ERR_PTR(-ENOMEM);
+
+ tmp = kstrdup(name, GFP_KERNEL);
+ if (!tmp) {
+ kfree(extension_details);
+ return ERR_PTR(-ENOMEM);
+ }
+ qstr_init(&extension_details->name, tmp);
+ ret = insert_ext_gid_entry(&extension_details->name, extensions_value->num);
+
+ if (ret) {
+ kfree(extension_details->name.name);
+ kfree(extension_details);
+ return ERR_PTR(ret);
+ }
+ config_item_init_type_name(&extension_details->item, name, &extension_details_type);
+
+ return &extension_details->item;
+}
+
+static struct configfs_group_operations extensions_value_group_ops = {
+ .make_item = extension_details_make_item,
+};
+
+static struct config_item_type extensions_name_type = {
+ .ct_group_ops = &extensions_value_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *extensions_make_group(struct config_group *group, const char *name)
+{
+ struct extensions_value *extensions_value;
+ unsigned int tmp;
+ int ret;
+
+ extensions_value = kzalloc(sizeof(struct extensions_value), GFP_KERNEL);
+ if (!extensions_value)
+ return ERR_PTR(-ENOMEM);
+ ret = kstrtouint(name, 10, &tmp);
+ if (ret) {
+ kfree(extensions_value);
+ return ERR_PTR(ret);
+ }
+
+ extensions_value->num = tmp;
+ config_group_init_type_name(&extensions_value->group, name,
+ &extensions_name_type);
+ return &extensions_value->group;
+}
+
+static void extensions_drop_group(struct config_group *group, struct config_item *item)
+{
+ struct extensions_value *value = to_extensions_value(item);
+
+ pr_info("sdcardfs: No longer mapping any files to gid %d\n", value->num);
+ kfree(value);
+}
+
+static struct configfs_group_operations extensions_group_ops = {
+ .make_group = extensions_make_group,
+ .drop_item = extensions_drop_group,
+};
+
+static struct config_item_type extensions_type = {
+ .ct_group_ops = &extensions_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+struct config_group extension_group = {
+ .cg_item = {
+ .ci_namebuf = "extensions",
+ .ci_type = &extensions_type,
+ },
+};
+
+struct packages {
+ struct configfs_subsystem subsystem;
+};
+
+static inline struct packages *to_packages(struct config_item *item)
+{
+ return item ? container_of(to_configfs_subsystem(to_config_group(item)), struct packages, subsystem) : NULL;
+}
+
+CONFIGFS_ATTR_STRUCT(packages);
+#define PACKAGES_ATTR(_name, _mode, _show, _store) \
+struct packages_attribute packages_attr_##_name = __CONFIGFS_ATTR(_name, _mode, _show, _store)
+#define PACKAGES_ATTR_RO(_name, _show) \
+struct packages_attribute packages_attr_##_name = __CONFIGFS_ATTR_RO(_name, _show)
+
+static struct config_item *packages_make_item(struct config_group *group, const char *name)
+{
+ struct package_details *package_details;
+ const char *tmp;
+
+ package_details = kzalloc(sizeof(struct package_details), GFP_KERNEL);
+ if (!package_details)
+ return ERR_PTR(-ENOMEM);
+ tmp = kstrdup(name, GFP_KERNEL);
+ if (!tmp) {
+ kfree(package_details);
+ return ERR_PTR(-ENOMEM);
+ }
+ qstr_init(&package_details->name, tmp);
+ config_item_init_type_name(&package_details->item, name,
+ &package_appid_type);
+
+ return &package_details->item;
+}
+
+static ssize_t packages_list_show(struct packages *packages,
char *page)
{
- struct hashtable_entry *hash_cur;
- struct hlist_node *h_t;
+ struct hashtable_entry *hash_cur_app;
+ struct hashtable_entry *hash_cur_user;
int i;
int count = 0, written = 0;
- char errormsg[] = "<truncated>\n";
+ const char errormsg[] = "<truncated>\n";
+ unsigned int hash;
- mutex_lock(&pkgl_data_all->hashtable_lock);
- hash_for_each_safe(pkgl_data_all->package_to_appid, i, h_t, hash_cur, hlist) {
- written = scnprintf(page + count, PAGE_SIZE - sizeof(errormsg) - count, "%s %d\n", (char *)hash_cur->key, hash_cur->value);
- if (count + written == PAGE_SIZE - sizeof(errormsg)) {
+ rcu_read_lock();
+ hash_for_each_rcu(package_to_appid, i, hash_cur_app, hlist) {
+ written = scnprintf(page + count, PAGE_SIZE - sizeof(errormsg) - count, "%s %d\n",
+ hash_cur_app->key.name, atomic_read(&hash_cur_app->value));
+ hash = hash_cur_app->key.hash;
+ hash_for_each_possible_rcu(package_to_userid, hash_cur_user, hlist, hash) {
+ if (qstr_case_eq(&hash_cur_app->key, &hash_cur_user->key)) {
+ written += scnprintf(page + count + written - 1,
+ PAGE_SIZE - sizeof(errormsg) - count - written + 1,
+ " %d\n", atomic_read(&hash_cur_user->value)) - 1;
+ }
+ }
+ if (count + written == PAGE_SIZE - sizeof(errormsg) - 1) {
count += scnprintf(page + count, PAGE_SIZE - count, errormsg);
break;
}
count += written;
}
- mutex_unlock(&pkgl_data_all->hashtable_lock);
+ rcu_read_unlock();
return count;
}
-static void sdcardfs_packages_release(struct config_item *item)
+static ssize_t packages_remove_userid_store(struct packages *packages,
+ const char *page, size_t count)
{
+ unsigned int tmp;
+ int ret;
- printk(KERN_INFO "sdcardfs: destroyed something?\n");
- kfree(to_sdcardfs_packages(item));
+ ret = kstrtouint(page, 10, &tmp);
+ if (ret)
+ return ret;
+ remove_userid_all_entry(tmp);
+ return count;
}
-static struct configfs_item_operations sdcardfs_packages_item_ops = {
- .release = sdcardfs_packages_release,
- .show_attribute = packages_attr_show,
+struct packages_attribute packages_attr_packages_gid_list = __CONFIGFS_ATTR_RO(packages_gid.list, packages_list_show);
+PACKAGES_ATTR(remove_userid, S_IWUGO, NULL, packages_remove_userid_store);
+
+static struct configfs_attribute *packages_attrs[] = {
+ &packages_attr_packages_gid_list.attr,
+ &packages_attr_remove_userid.attr,
+ NULL,
+};
+
+CONFIGFS_ATTR_OPS(packages)
+static struct configfs_item_operations packages_item_ops = {
+ .show_attribute = packages_attr_show,
+ .store_attribute = packages_attr_store,
};
/*
* Note that, since no extra work is required on ->drop_item(),
* no ->drop_item() is provided.
*/
-static struct configfs_group_operations sdcardfs_packages_group_ops = {
- .make_item = sdcardfs_packages_make_item,
+static struct configfs_group_operations packages_group_ops = {
+ .make_item = packages_make_item,
};
-static struct config_item_type sdcardfs_packages_type = {
- .ct_item_ops = &sdcardfs_packages_item_ops,
- .ct_group_ops = &sdcardfs_packages_group_ops,
- .ct_attrs = sdcardfs_packages_attrs,
+static struct config_item_type packages_type = {
+ .ct_item_ops = &packages_item_ops,
+ .ct_group_ops = &packages_group_ops,
+ .ct_attrs = packages_attrs,
.ct_owner = THIS_MODULE,
};
-static struct configfs_subsystem sdcardfs_packages_subsys = {
- .su_group = {
- .cg_item = {
- .ci_namebuf = "sdcardfs",
- .ci_type = &sdcardfs_packages_type,
+struct config_group *sd_default_groups[] = {
+ &extension_group,
+ NULL,
+};
+
+static struct packages sdcardfs_packages = {
+ .subsystem = {
+ .su_group = {
+ .cg_item = {
+ .ci_namebuf = "sdcardfs",
+ .ci_type = &packages_type,
+ },
+ .default_groups = sd_default_groups,
},
},
};
static int configfs_sdcardfs_init(void)
{
- int ret;
- struct configfs_subsystem *subsys = &sdcardfs_packages_subsys;
+ int ret, i;
+ struct configfs_subsystem *subsys = &sdcardfs_packages.subsystem;
+ for (i = 0; sd_default_groups[i]; i++)
+ config_group_init(sd_default_groups[i]);
config_group_init(&subsys->su_group);
mutex_init(&subsys->su_mutex);
ret = configfs_register_subsystem(subsys);
if (ret) {
- printk(KERN_ERR "Error %d while registering subsystem %s\n",
+ pr_err("Error %d while registering subsystem %s\n",
ret,
subsys->su_group.cg_item.ci_namebuf);
}
@@ -420,7 +873,7 @@
static void configfs_sdcardfs_exit(void)
{
- configfs_unregister_subsystem(&sdcardfs_packages_subsys);
+ configfs_unregister_subsystem(&sdcardfs_packages.subsystem);
}
int packagelist_init(void)
@@ -429,19 +882,17 @@
kmem_cache_create("packagelist_hashtable_entry",
sizeof(struct hashtable_entry), 0, 0, NULL);
if (!hashtable_entry_cachep) {
- printk(KERN_ERR "sdcardfs: failed creating pkgl_hashtable entry slab cache\n");
+ pr_err("sdcardfs: failed creating pkgl_hashtable entry slab cache\n");
return -ENOMEM;
}
- pkgl_data_all = packagelist_create();
configfs_sdcardfs_init();
- return 0;
+ return 0;
}
void packagelist_exit(void)
{
configfs_sdcardfs_exit();
- packagelist_destroy(pkgl_data_all);
- if (hashtable_entry_cachep)
- kmem_cache_destroy(hashtable_entry_cachep);
+ packagelist_destroy();
+ kmem_cache_destroy(hashtable_entry_cachep);
}
diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h
index 7f3dee3..3874d40 100755
--- a/fs/sdcardfs/sdcardfs.h
+++ b/fs/sdcardfs/sdcardfs.h
@@ -29,6 +29,7 @@
#include <linux/dcache.h>
#include <linux/file.h>
#include <linux/fs.h>
+#include <linux/aio.h>
#include <linux/mm.h>
#include <linux/mount.h>
#include <linux/namei.h>
@@ -52,7 +53,7 @@
#define SDCARDFS_ROOT_INO 1
/* useful for tracking code reachability */
-#define UDBG printk(KERN_DEFAULT "DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__)
+#define UDBG pr_default("DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__)
#define SDCARDFS_DIRENT_SIZE 256
@@ -65,71 +66,89 @@
#define AID_SDCARD_PICS 1033 /* external storage photos access */
#define AID_SDCARD_AV 1034 /* external storage audio/video access */
#define AID_SDCARD_ALL 1035 /* access all users external storage */
+#define AID_MEDIA_OBB 1059 /* obb files */
+
+#define AID_SDCARD_IMAGE 1057
#define AID_PACKAGE_INFO 1027
-#define fix_derived_permission(x) \
+
+/*
+ * Permissions are handled by our permission function.
+ * We don't want anyone who happens to look at our inode value to prematurely
+ * block access, so store more permissive values. These are probably never
+ * used.
+ */
+#define fixup_tmp_permissions(x) \
do { \
- (x)->i_uid = SDCARDFS_I(x)->d_uid; \
- (x)->i_gid = get_gid(SDCARDFS_I(x)); \
- (x)->i_mode = ((x)->i_mode & S_IFMT) | get_mode(SDCARDFS_I(x));\
+ (x)->i_uid = SDCARDFS_I(x)->data->d_uid; \
+ (x)->i_gid = AID_SDCARD_RW; \
+ (x)->i_mode = ((x)->i_mode & S_IFMT) | 0775;\
} while (0)
-
/* OVERRIDE_CRED() and REVERT_CRED()
- * OVERRID_CRED()
- * backup original task->cred
- * and modifies task->cred->fsuid/fsgid to specified value.
+ * OVERRIDE_CRED()
+ * backup original task->cred
+ * and modifies task->cred->fsuid/fsgid to specified value.
* REVERT_CRED()
- * restore original task->cred->fsuid/fsgid.
+ * restore original task->cred->fsuid/fsgid.
* These two macro should be used in pair, and OVERRIDE_CRED() should be
* placed at the beginning of a function, right after variable declaration.
*/
-#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred) \
- saved_cred = override_fsids(sdcardfs_sbi); \
- if (!saved_cred) { return -ENOMEM; }
+#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred, info) \
+ do { \
+ saved_cred = override_fsids(sdcardfs_sbi, info->data); \
+ if (!saved_cred) \
+ return -ENOMEM; \
+ } while (0)
-#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred) \
- saved_cred = override_fsids(sdcardfs_sbi); \
- if (!saved_cred) { return ERR_PTR(-ENOMEM); }
+#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred, info) \
+ do { \
+ saved_cred = override_fsids(sdcardfs_sbi, info->data); \
+ if (!saved_cred) \
+ return ERR_PTR(-ENOMEM); \
+ } while (0)
#define REVERT_CRED(saved_cred) revert_fsids(saved_cred)
-#define DEBUG_CRED() \
- printk("KAKJAGI: %s:%d fsuid %d fsgid %d\n", \
- __FUNCTION__, __LINE__, \
- (int)current->cred->fsuid, \
- (int)current->cred->fsgid);
-
/* Android 5.0 support */
/* Permission mode for a specific node. Controls how file permissions
- * are derived for children nodes. */
+ * are derived for children nodes.
+ */
typedef enum {
- /* Nothing special; this node should just inherit from its parent. */
- PERM_INHERIT,
- /* This node is one level above a normal root; used for legacy layouts
- * which use the first level to represent user_id. */
- PERM_PRE_ROOT,
- /* This node is "/" */
- PERM_ROOT,
- /* This node is "/Android" */
- PERM_ANDROID,
- /* This node is "/Android/data" */
- PERM_ANDROID_DATA,
- /* This node is "/Android/obb" */
- PERM_ANDROID_OBB,
- /* This node is "/Android/media" */
- PERM_ANDROID_MEDIA,
+ /* Nothing special; this node should just inherit from its parent. */
+ PERM_INHERIT,
+ /* This node is one level above a normal root; used for legacy layouts
+ * which use the first level to represent user_id.
+ */
+ PERM_PRE_ROOT,
+ /* This node is "/" */
+ PERM_ROOT,
+ /* This node is "/Android" */
+ PERM_ANDROID,
+ /* This node is "/Android/data" */
+ PERM_ANDROID_DATA,
+ /* This node is "/Android/obb" */
+ PERM_ANDROID_OBB,
+ /* This node is "/Android/media" */
+ PERM_ANDROID_MEDIA,
+ /* This node is "/Android/[data|media|obb]/[package]" */
+ PERM_ANDROID_PACKAGE,
+ /* This node is "/Android/[data|media|obb]/[package]/cache" */
+ PERM_ANDROID_PACKAGE_CACHE,
} perm_t;
struct sdcardfs_sb_info;
struct sdcardfs_mount_options;
+struct sdcardfs_inode_info;
+struct sdcardfs_inode_data;
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
-const struct cred * override_fsids(struct sdcardfs_sb_info* sbi);
+const struct cred *override_fsids(struct sdcardfs_sb_info *sbi,
+ struct sdcardfs_inode_data *data);
/* Do not directly use this function, use REVERT_CRED() instead. */
-void revert_fsids(const struct cred * old_cred);
+void revert_fsids(const struct cred *old_cred);
/* operations vectors defined in specific files */
extern const struct file_operations sdcardfs_main_fops;
@@ -161,14 +180,26 @@
const struct vm_operations_struct *lower_vm_ops;
};
-/* sdcardfs inode data in memory */
-struct sdcardfs_inode_info {
- struct inode *lower_inode;
- /* state derived based on current position in hierachy */
+struct sdcardfs_inode_data {
+ struct kref refcount;
+ bool abandoned;
+
perm_t perm;
userid_t userid;
uid_t d_uid;
bool under_android;
+ bool under_cache;
+ bool under_obb;
+};
+
+/* sdcardfs inode data in memory */
+struct sdcardfs_inode_info {
+ struct inode *lower_inode;
+ /* state derived based on current position in hierarchy */
+ struct sdcardfs_inode_data *data;
+
+ /* top folder for ownership */
+ struct sdcardfs_inode_data *top_data;
struct inode vfs_inode;
};
@@ -185,18 +216,25 @@
uid_t fs_low_uid;
gid_t fs_low_gid;
userid_t fs_user_id;
- gid_t gid;
- mode_t mask;
bool multiuser;
unsigned int reserved_mb;
};
+struct sdcardfs_vfsmount_options {
+ gid_t gid;
+ mode_t mask;
+};
+
+extern int parse_options_remount(struct super_block *sb, char *options, int silent,
+ struct sdcardfs_vfsmount_options *vfsopts);
+
/* sdcardfs super-block data in memory */
struct sdcardfs_sb_info {
struct super_block *sb;
struct super_block *lower_sb;
/* derived perm policy : some of options have been added
- * to sdcardfs_mount_options (Android 4.4 support) */
+ * to sdcardfs_mount_options (Android 4.4 support)
+ */
struct sdcardfs_mount_options options;
spinlock_t lock; /* protects obbpath */
char *obbpath_s;
@@ -307,7 +345,7 @@
{ \
struct path pname; \
spin_lock(&SDCARDFS_D(dent)->lock); \
- if(SDCARDFS_D(dent)->pname.dentry) { \
+ if (SDCARDFS_D(dent)->pname.dentry) { \
pathcpy(&pname, &SDCARDFS_D(dent)->pname); \
SDCARDFS_D(dent)->pname.dentry = NULL; \
SDCARDFS_D(dent)->pname.mnt = NULL; \
@@ -321,38 +359,97 @@
SDCARDFS_DENT_FUNC(lower_path)
SDCARDFS_DENT_FUNC(orig_path)
-static inline int get_gid(struct sdcardfs_inode_info *info) {
- struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb);
- if (sb_info->options.gid == AID_SDCARD_RW) {
+static inline bool sbinfo_has_sdcard_magic(struct sdcardfs_sb_info *sbinfo)
+{
+ return sbinfo && sbinfo->sb
+ && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC;
+}
+
+static inline struct sdcardfs_inode_data *data_get(
+ struct sdcardfs_inode_data *data)
+{
+ if (data)
+ kref_get(&data->refcount);
+ return data;
+}
+
+static inline struct sdcardfs_inode_data *top_data_get(
+ struct sdcardfs_inode_info *info)
+{
+ return data_get(info->top_data);
+}
+
+extern void data_release(struct kref *ref);
+
+static inline void data_put(struct sdcardfs_inode_data *data)
+{
+ kref_put(&data->refcount, data_release);
+}
+
+static inline void release_own_data(struct sdcardfs_inode_info *info)
+{
+ /*
+ * This happens exactly once per inode. At this point, the inode that
+ * originally held this data is about to be freed, and all references
+ * to it are held as a top value, and will likely be released soon.
+ */
+ info->data->abandoned = true;
+ data_put(info->data);
+}
+
+static inline void set_top(struct sdcardfs_inode_info *info,
+ struct sdcardfs_inode_data *top)
+{
+ struct sdcardfs_inode_data *old_top = info->top_data;
+
+ if (top)
+ data_get(top);
+ info->top_data = top;
+ if (old_top)
+ data_put(old_top);
+}
+
+static inline int get_gid(struct vfsmount *mnt,
+ struct sdcardfs_inode_data *data)
+{
+ struct sdcardfs_vfsmount_options *opts = mnt->data;
+
+ if (opts->gid == AID_SDCARD_RW)
/* As an optimization, certain trusted system components only run
* as owner but operate across all users. Since we're now handing
* out the sdcard_rw GID only to trusted apps, we're okay relaxing
* the user boundary enforcement for the default view. The UIDs
- * assigned to app directories are still multiuser aware. */
+ * assigned to app directories are still multiuser aware.
+ */
return AID_SDCARD_RW;
- } else {
- return multiuser_get_uid(info->userid, sb_info->options.gid);
- }
+ else
+ return multiuser_get_uid(data->userid, opts->gid);
}
-static inline int get_mode(struct sdcardfs_inode_info *info) {
+
+static inline int get_mode(struct vfsmount *mnt,
+ struct sdcardfs_inode_info *info,
+ struct sdcardfs_inode_data *data)
+{
int owner_mode;
int filtered_mode;
- struct sdcardfs_sb_info *sb_info = SDCARDFS_SB(info->vfs_inode.i_sb);
- int visible_mode = 0775 & ~sb_info->options.mask;
+ struct sdcardfs_vfsmount_options *opts = mnt->data;
+ int visible_mode = 0775 & ~opts->mask;
- if (info->perm == PERM_PRE_ROOT) {
+
+ if (data->perm == PERM_PRE_ROOT) {
/* Top of multi-user view should always be visible to ensure
- * secondary users can traverse inside. */
+ * secondary users can traverse inside.
+ */
visible_mode = 0711;
- } else if (info->under_android) {
+ } else if (data->under_android) {
/* Block "other" access to Android directories, since only apps
* belonging to a specific user should be in there; we still
- * leave +x open for the default view. */
- if (sb_info->options.gid == AID_SDCARD_RW) {
+ * leave +x open for the default view.
+ */
+ if (opts->gid == AID_SDCARD_RW)
visible_mode = visible_mode & ~0006;
- } else {
+ else
visible_mode = visible_mode & ~0007;
- }
}
owner_mode = info->lower_inode->i_mode & 0700;
filtered_mode = visible_mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6));
@@ -377,7 +474,7 @@
/* in case of a local obb dentry
* the orig_path should be returned
*/
- if(has_graft_path(dent))
+ if (has_graft_path(dent))
sdcardfs_get_orig_path(dent, real_lower);
else
sdcardfs_get_lower_path(dent, real_lower);
@@ -386,7 +483,7 @@
static inline void sdcardfs_put_real_lower(const struct dentry *dent,
struct path *real_lower)
{
- if(has_graft_path(dent))
+ if (has_graft_path(dent))
sdcardfs_put_orig_path(dent, real_lower);
else
sdcardfs_put_lower_path(dent, real_lower);
@@ -396,20 +493,32 @@
extern struct list_head sdcardfs_super_list;
/* for packagelist.c */
-extern appid_t get_appid(void *pkgl_id, const char *app_name);
-extern int check_caller_access_to_name(struct inode *parent_node, const char* name);
+extern appid_t get_appid(const char *app_name);
+extern appid_t get_ext_gid(const char *app_name);
+extern appid_t is_excluded(const char *app_name, userid_t userid);
+extern int check_caller_access_to_name(struct inode *parent_node, const struct qstr *name);
extern int open_flags_to_access_mode(int open_flags);
extern int packagelist_init(void);
extern void packagelist_exit(void);
/* for derived_perm.c */
+#define BY_NAME (1 << 0)
+#define BY_USERID (1 << 1)
+struct limit_search {
+ unsigned int flags;
+ struct qstr name;
+ userid_t userid;
+};
+
extern void setup_derived_state(struct inode *inode, perm_t perm,
- userid_t userid, uid_t uid, bool under_android);
+ userid_t userid, uid_t uid, bool under_android,
+ struct sdcardfs_inode_data *top);
extern void get_derived_permission(struct dentry *parent, struct dentry *dentry);
-extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, struct dentry *newdentry);
-extern void get_derive_permissions_recursive(struct dentry *parent);
+extern void get_derived_permission_new(struct dentry *parent, struct dentry *dentry, const struct qstr *name);
+extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit);
extern void update_derived_permission_lock(struct dentry *dentry);
+void fixup_lower_ownership(struct dentry *dentry, const char *name);
extern int need_graft_path(struct dentry *dentry);
extern int is_base_obbpath(struct dentry *dentry);
extern int is_obbpath_invalid(struct dentry *dentry);
@@ -419,6 +528,7 @@
static inline struct dentry *lock_parent(struct dentry *dentry)
{
struct dentry *dir = dget_parent(dentry);
+
mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
return dir;
}
@@ -444,7 +554,7 @@
goto out_unlock;
}
- err = vfs_mkdir(parent.dentry->d_inode, dent, mode);
+ err = vfs_mkdir2(parent.mnt, parent.dentry->d_inode, dent, mode);
if (err) {
if (err == -EEXIST)
err = 0;
@@ -455,7 +565,7 @@
attrs.ia_gid = gid;
attrs.ia_valid = ATTR_UID | ATTR_GID;
mutex_lock(&dent->d_inode->i_mutex);
- notify_change(dent, &attrs);
+ notify_change2(parent.mnt, dent, &attrs);
mutex_unlock(&dent->d_inode->i_mutex);
out_dput:
@@ -513,12 +623,17 @@
return 1;
}
-/* Copies attrs and maintains sdcardfs managed attrs */
+/*
+ * Copies attrs and maintains sdcardfs managed attrs
+ * Since our permission check handles all special permissions, set those to be open
+ */
static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct inode *src)
{
- dest->i_mode = (src->i_mode & S_IFMT) | get_mode(SDCARDFS_I(dest));
- dest->i_uid = SDCARDFS_I(dest)->d_uid;
- dest->i_gid = get_gid(SDCARDFS_I(dest));
+
+ dest->i_mode = (src->i_mode & S_IFMT) | S_IRWXU | S_IRWXG |
+ S_IROTH | S_IXOTH; /* 0775 */
+ dest->i_uid = SDCARDFS_I(dest)->data->d_uid;
+ dest->i_gid = AID_SDCARD_RW;
dest->i_rdev = src->i_rdev;
dest->i_atime = src->i_atime;
dest->i_mtime = src->i_mtime;
@@ -527,4 +642,22 @@
dest->i_flags = src->i_flags;
set_nlink(dest, src->i_nlink);
}
+
+static inline bool str_case_eq(const char *s1, const char *s2)
+{
+ return !strcasecmp(s1, s2);
+}
+
+static inline bool str_n_case_eq(const char *s1, const char *s2, size_t len)
+{
+ return !strncasecmp(s1, s2, len);
+}
+
+static inline bool qstr_case_eq(const struct qstr *q1, const struct qstr *q2)
+{
+ return q1->len == q2->len && str_case_eq(q1->name, q2->name);
+}
+
+#define QSTR_LITERAL(string) QSTR_INIT(string, sizeof(string)-1)
+
#endif /* not _SDCARDFS_H_ */
diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c
index 1d64901..7f4539b 100755
--- a/fs/sdcardfs/super.c
+++ b/fs/sdcardfs/super.c
@@ -26,6 +26,23 @@
*/
static struct kmem_cache *sdcardfs_inode_cachep;
+/*
+ * To support the top references, we must track some data separately.
+ * An sdcardfs_inode_info always has a reference to its data, and once set up,
+ * also has a reference to its top. The top may be itself, in which case it
+ * holds two references to its data. When top is changed, it takes a ref to the
+ * new data and then drops the ref to the old data.
+ */
+static struct kmem_cache *sdcardfs_inode_data_cachep;
+
+void data_release(struct kref *ref)
+{
+ struct sdcardfs_inode_data *data =
+ container_of(ref, struct sdcardfs_inode_data, refcount);
+
+ kmem_cache_free(sdcardfs_inode_data_cachep, data);
+}
+
/* final actions when unmounting a file system */
static void sdcardfs_put_super(struct super_block *sb)
{
@@ -36,7 +53,7 @@
if (!spd)
return;
- if(spd->obbpath_s) {
+ if (spd->obbpath_s) {
kfree(spd->obbpath_s);
path_put(&spd->obbpath);
}
@@ -64,7 +81,7 @@
if (sbi->options.reserved_mb) {
/* Invalid statfs informations. */
if (buf->f_bsize == 0) {
- printk(KERN_ERR "Returned block size is zero.\n");
+ pr_err("Returned block size is zero.\n");
return -EINVAL;
}
@@ -100,8 +117,7 @@
* SILENT, but anything else left over is an error.
*/
if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT)) != 0) {
- printk(KERN_ERR
- "sdcardfs: remount flags 0x%x unsupported\n", *flags);
+ pr_err("sdcardfs: remount flags 0x%x unsupported\n", *flags);
err = -EINVAL;
}
@@ -109,6 +125,54 @@
}
/*
+ * @mnt: mount point we are remounting
+ * @sb: superblock we are remounting
+ * @flags: numeric mount options
+ * @options: mount options string
+ */
+static int sdcardfs_remount_fs2(struct vfsmount *mnt, struct super_block *sb,
+ int *flags, char *options)
+{
+ int err = 0;
+
+ /*
+ * The VFS will take care of "ro" and "rw" flags among others. We
+ * can safely accept a few flags (RDONLY, MANDLOCK), and honor
+ * SILENT, but anything else left over is an error.
+ */
+ if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT | MS_REMOUNT)) != 0) {
+ pr_err("sdcardfs: remount flags 0x%x unsupported\n", *flags);
+ err = -EINVAL;
+ }
+ pr_info("Remount options were %s for vfsmnt %p.\n", options, mnt);
+ err = parse_options_remount(sb, options, *flags & ~MS_SILENT, mnt->data);
+
+
+ return err;
+}
+
+static void *sdcardfs_clone_mnt_data(void *data)
+{
+ struct sdcardfs_vfsmount_options *opt = kmalloc(sizeof(struct sdcardfs_vfsmount_options), GFP_KERNEL);
+ struct sdcardfs_vfsmount_options *old = data;
+
+ if (!opt)
+ return NULL;
+ opt->gid = old->gid;
+ opt->mask = old->mask;
+ return opt;
+}
+
+static void sdcardfs_copy_mnt_data(void *data, void *newdata)
+{
+ struct sdcardfs_vfsmount_options *old = data;
+ struct sdcardfs_vfsmount_options *new = newdata;
+
+ old->gid = new->gid;
+ old->mask = new->mask;
+}
+
+/*
* Called by iput() when the inode reference count reached zero
* and the inode is not hashed anywhere. Used to clear anything
* that needs to be, before the inode is completely destroyed and put
@@ -119,6 +183,7 @@
struct inode *lower_inode;
truncate_inode_pages(&inode->i_data, 0);
+ set_top(SDCARDFS_I(inode), NULL);
clear_inode(inode);
/*
* Decrement a reference to a lower_inode, which was incremented
@@ -132,6 +197,7 @@
static struct inode *sdcardfs_alloc_inode(struct super_block *sb)
{
struct sdcardfs_inode_info *i;
+ struct sdcardfs_inode_data *d;
i = kmem_cache_alloc(sdcardfs_inode_cachep, GFP_KERNEL);
if (!i)
@@ -140,13 +206,31 @@
/* memset everything up to the inode to 0 */
memset(i, 0, offsetof(struct sdcardfs_inode_info, vfs_inode));
+ d = kmem_cache_alloc(sdcardfs_inode_data_cachep,
+ GFP_KERNEL | __GFP_ZERO);
+ if (!d) {
+ kmem_cache_free(sdcardfs_inode_cachep, i);
+ return NULL;
+ }
+
+ i->data = d;
+ kref_init(&d->refcount);
+
i->vfs_inode.i_version = 1;
return &i->vfs_inode;
}
+static void i_callback(struct rcu_head *head)
+{
+ struct inode *inode = container_of(head, struct inode, i_rcu);
+
+ release_own_data(SDCARDFS_I(inode));
+ kmem_cache_free(sdcardfs_inode_cachep, SDCARDFS_I(inode));
+}
+
static void sdcardfs_destroy_inode(struct inode *inode)
{
- kmem_cache_free(sdcardfs_inode_cachep, SDCARDFS_I(inode));
+ call_rcu(&inode->i_rcu, i_callback);
}
/* sdcardfs inode cache constructor */
@@ -159,22 +243,31 @@
int sdcardfs_init_inode_cache(void)
{
- int err = 0;
-
sdcardfs_inode_cachep =
kmem_cache_create("sdcardfs_inode_cache",
sizeof(struct sdcardfs_inode_info), 0,
SLAB_RECLAIM_ACCOUNT, init_once);
+
if (!sdcardfs_inode_cachep)
- err = -ENOMEM;
- return err;
+ return -ENOMEM;
+
+ sdcardfs_inode_data_cachep =
+ kmem_cache_create("sdcardfs_inode_data_cache",
+ sizeof(struct sdcardfs_inode_data), 0,
+ SLAB_RECLAIM_ACCOUNT, NULL);
+ if (!sdcardfs_inode_data_cachep) {
+ kmem_cache_destroy(sdcardfs_inode_cachep);
+ return -ENOMEM;
+ }
+
+ return 0;
}
/* sdcardfs inode cache destructor */
void sdcardfs_destroy_inode_cache(void)
{
- if (sdcardfs_inode_cachep)
- kmem_cache_destroy(sdcardfs_inode_cachep);
+ kmem_cache_destroy(sdcardfs_inode_data_cachep);
+ kmem_cache_destroy(sdcardfs_inode_cachep);
}
/*
@@ -190,19 +283,25 @@
lower_sb->s_op->umount_begin(lower_sb);
}
-static int sdcardfs_show_options(struct seq_file *m, struct dentry *root)
+static int sdcardfs_show_options(struct vfsmount *mnt, struct seq_file *m,
+ struct dentry *root)
{
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(root->d_sb);
struct sdcardfs_mount_options *opts = &sbi->options;
+ struct sdcardfs_vfsmount_options *vfsopts = mnt->data;
if (opts->fs_low_uid != 0)
- seq_printf(m, ",uid=%u", opts->fs_low_uid);
+ seq_printf(m, ",fsuid=%u", opts->fs_low_uid);
if (opts->fs_low_gid != 0)
- seq_printf(m, ",gid=%u", opts->fs_low_gid);
-
+ seq_printf(m, ",fsgid=%u", opts->fs_low_gid);
+ if (vfsopts->gid != 0)
+ seq_printf(m, ",gid=%u", vfsopts->gid);
if (opts->multiuser)
- seq_printf(m, ",multiuser");
-
+ seq_puts(m, ",multiuser");
+ if (vfsopts->mask)
+ seq_printf(m, ",mask=%u", vfsopts->mask);
+ if (opts->fs_user_id)
+ seq_printf(m, ",userid=%u", opts->fs_user_id);
if (opts->reserved_mb != 0)
seq_printf(m, ",reserved=%uMB", opts->reserved_mb);
@@ -213,9 +312,12 @@
.put_super = sdcardfs_put_super,
.statfs = sdcardfs_statfs,
.remount_fs = sdcardfs_remount_fs,
+ .remount_fs2 = sdcardfs_remount_fs2,
+ .clone_mnt_data = sdcardfs_clone_mnt_data,
+ .copy_mnt_data = sdcardfs_copy_mnt_data,
.evict_inode = sdcardfs_evict_inode,
.umount_begin = sdcardfs_umount_begin,
- .show_options = sdcardfs_show_options,
+ .show_options2 = sdcardfs_show_options,
.alloc_inode = sdcardfs_alloc_inode,
.destroy_inode = sdcardfs_destroy_inode,
.drop_inode = generic_delete_inode,
diff --git a/fs/super.c b/fs/super.c
index a7708e0..a84e274 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -695,7 +695,8 @@
}
/**
- * do_remount_sb - asks filesystem to change mount options.
+ * do_remount_sb2 - asks filesystem to change mount options.
+ * @mnt: mount we are looking at
* @sb: superblock in question
* @flags: numeric part of options
* @data: the rest of options
@@ -703,7 +704,7 @@
*
* Alters the mount options of a mounted file system.
*/
-int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
+int do_remount_sb2(struct vfsmount *mnt, struct super_block *sb, int flags, void *data, int force)
{
int retval;
int remount_ro;
@@ -736,7 +737,16 @@
sync_filesystem(sb);
- if (sb->s_op->remount_fs) {
+ if (mnt && sb->s_op->remount_fs2) {
+ retval = sb->s_op->remount_fs2(mnt, sb, &flags, data);
+ if (retval) {
+ if (!force)
+ goto cancel_readonly;
+ /* If forced remount, go ahead despite any errors */
+ WARN(1, "forced remount of a %s fs returned %i\n",
+ sb->s_type->name, retval);
+ }
+ } else if (sb->s_op->remount_fs) {
retval = sb->s_op->remount_fs(sb, &flags, data);
if (retval) {
if (!force)
@@ -768,6 +778,11 @@
return retval;
}
+int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
+{
+ return do_remount_sb2(NULL, sb, flags, data, force);
+}
+
static void do_emergency_remount(struct work_struct *work)
{
struct super_block *sb, *p = NULL;
@@ -1087,7 +1102,7 @@
EXPORT_SYMBOL(mount_single);
struct dentry *
-mount_fs(struct file_system_type *type, int flags, const char *name, void *data)
+mount_fs(struct file_system_type *type, int flags, const char *name, struct vfsmount *mnt, void *data)
{
struct dentry *root;
struct super_block *sb;
@@ -1104,7 +1119,10 @@
goto out_free_secdata;
}
- root = type->mount(type, flags, name, data);
+ if (type->mount2)
+ root = type->mount2(mnt, type, flags, name, data);
+ else
+ root = type->mount(type, flags, name, data);
if (IS_ERR(root)) {
error = PTR_ERR(root);
goto out_free_secdata;
diff --git a/fs/utimes.c b/fs/utimes.c
index f4fb7ec..7216a07 100644
--- a/fs/utimes.c
+++ b/fs/utimes.c
@@ -96,13 +96,13 @@
goto mnt_drop_write_and_out;
if (!inode_owner_or_capable(inode)) {
- error = inode_permission(inode, MAY_WRITE);
+ error = inode_permission2(path->mnt, inode, MAY_WRITE);
if (error)
goto mnt_drop_write_and_out;
}
}
mutex_lock(&inode->i_mutex);
- error = notify_change(path->dentry, &newattrs);
+ error = notify_change2(path->mnt, path->dentry, &newattrs);
mutex_unlock(&inode->i_mutex);
mnt_drop_write_and_out:
diff --git a/include/linux/cred.h b/include/linux/cred.h
index 6c58dd7..94da8d2 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -122,6 +122,7 @@
kernel_cap_t cap_permitted; /* caps we're permitted */
kernel_cap_t cap_effective; /* caps we can actually use */
kernel_cap_t cap_bset; /* capability bounding set */
+ kernel_cap_t cap_ambient; /* Ambient capability set */
#ifdef CONFIG_KEYS
unsigned char jit_keyring; /* default keyring to attach requested
* keys to */
@@ -197,6 +198,13 @@
}
#endif
+static inline bool cap_ambient_invariant_ok(const struct cred *cred)
+{
+ return cap_issubset(cred->cap_ambient,
+ cap_intersect(cred->cap_permitted,
+ cred->cap_inheritable));
+}
+
/**
* get_new_cred - Get a reference on a new set of credentials
* @cred: The new credentials to reference
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 0d1e168..63ad13a 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1470,13 +1470,20 @@
* VFS helper functions..
*/
extern int vfs_create(struct inode *, struct dentry *, umode_t, bool);
+extern int vfs_create2(struct vfsmount *, struct inode *, struct dentry *, umode_t, bool);
extern int vfs_mkdir(struct inode *, struct dentry *, umode_t);
+extern int vfs_mkdir2(struct vfsmount *, struct inode *, struct dentry *, umode_t);
extern int vfs_mknod(struct inode *, struct dentry *, umode_t, dev_t);
+extern int vfs_mknod2(struct vfsmount *, struct inode *, struct dentry *, umode_t, 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_link2(struct vfsmount *, struct dentry *, struct inode *, struct dentry *);
extern int vfs_rmdir(struct inode *, struct dentry *);
+extern int vfs_rmdir2(struct vfsmount *, struct inode *, struct dentry *);
extern int vfs_unlink(struct inode *, struct dentry *);
+extern int vfs_unlink2(struct vfsmount *, struct inode *, struct dentry *);
extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
+extern int vfs_rename2(struct vfsmount *, struct inode *, struct dentry *, struct inode *, struct dentry *);
/*
* VFS dentry helper functions.
@@ -1580,6 +1587,7 @@
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
void * (*follow_link) (struct dentry *, struct nameidata *);
int (*permission) (struct inode *, int);
+ int (*permission2) (struct vfsmount *, struct inode *, int);
struct posix_acl * (*get_acl)(struct inode *, int);
int (*readlink) (struct dentry *, char __user *,int);
@@ -1595,6 +1603,7 @@
int (*rename) (struct inode *, struct dentry *,
struct inode *, struct dentry *);
int (*setattr) (struct dentry *, struct iattr *);
+ int (*setattr2) (struct vfsmount *, struct dentry *, struct iattr *);
int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
@@ -1634,9 +1643,13 @@
int (*unfreeze_fs) (struct super_block *);
int (*statfs) (struct dentry *, struct kstatfs *);
int (*remount_fs) (struct super_block *, int *, char *);
+ int (*remount_fs2) (struct vfsmount *, struct super_block *, int *, char *);
+ void *(*clone_mnt_data) (void *);
+ void (*copy_mnt_data) (void *, void *);
void (*umount_begin) (struct super_block *);
int (*show_options)(struct seq_file *, struct dentry *);
+ int (*show_options2)(struct vfsmount *,struct seq_file *, struct dentry *);
int (*show_devname)(struct seq_file *, struct dentry *);
int (*show_path)(struct seq_file *, struct dentry *);
int (*show_stats)(struct seq_file *, struct dentry *);
@@ -1843,6 +1856,9 @@
#define FS_RENAME_DOES_D_MOVE 32768 /* FS will handle d_move() during rename() internally. */
struct dentry *(*mount) (struct file_system_type *, int,
const char *, void *);
+ struct dentry *(*mount2) (struct vfsmount *, struct file_system_type *, int,
+ const char *, void *);
+ void *(*alloc_mnt_data) (void);
void (*kill_sb) (struct super_block *);
struct module *owner;
struct file_system_type * next;
@@ -2031,6 +2047,8 @@
extern long vfs_truncate(struct path *, loff_t);
extern int do_truncate(struct dentry *, loff_t start, unsigned int time_attrs,
struct file *filp);
+extern int do_truncate2(struct vfsmount *, struct dentry *, loff_t start,
+ unsigned int time_attrs, struct file *filp);
extern int do_fallocate(struct file *file, int mode, loff_t offset,
loff_t len);
extern long do_sys_open(int dfd, const char __user *filename, int flags,
@@ -2238,7 +2256,9 @@
extern sector_t bmap(struct inode *, sector_t);
#endif
extern int notify_change(struct dentry *, struct iattr *);
+extern int notify_change2(struct vfsmount *, struct dentry *, struct iattr *);
extern int inode_permission(struct inode *, int);
+extern int inode_permission2(struct vfsmount *, struct inode *, int);
extern int generic_permission(struct inode *, int);
static inline bool execute_ok(struct inode *inode)
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index f6f0c3c..d12af11 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -17,6 +17,7 @@
__s32 dad_transmits;
__s32 rtr_solicits;
__s32 rtr_solicit_interval;
+ __s32 rtr_solicit_max_interval;
__s32 rtr_solicit_delay;
__s32 force_mld_version;
#ifdef CONFIG_IPV6_PRIVACY
@@ -33,6 +34,7 @@
__s32 accept_ra_rtr_pref;
__s32 rtr_probe_interval;
#ifdef CONFIG_IPV6_ROUTE_INFO
+ __s32 accept_ra_rt_info_min_plen;
__s32 accept_ra_rt_info_max_plen;
#endif
#endif
diff --git a/include/linux/kallsyms.h b/include/linux/kallsyms.h
index 6883e19..e4a205d 100644
--- a/include/linux/kallsyms.h
+++ b/include/linux/kallsyms.h
@@ -122,7 +122,7 @@
static inline void print_ip_sym(unsigned long ip)
{
- printk("[<%p>] %pS\n", (void *) ip, (void *) ip);
+ printk("[<%pP>] %pS\n", (void *) ip, (void *) ip);
}
#endif /*_LINUX_KALLSYMS_H*/
diff --git a/include/linux/mount.h b/include/linux/mount.h
index 8eeb8f6..03fe740 100644
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -46,8 +46,12 @@
| MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME \
| MNT_READONLY)
+
#define MNT_ATIME_MASK (MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME )
+#define MNT_INTERNAL_FLAGS (MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL | \
+ MNT_MARKED)
+
#define MNT_INTERNAL 0x4000
#define MNT_LOCK_ATIME 0x040000
@@ -55,11 +59,13 @@
#define MNT_LOCK_NOSUID 0x100000
#define MNT_LOCK_NODEV 0x200000
#define MNT_LOCK_READONLY 0x400000
+#define MNT_MARKED 0x4000000
struct vfsmount {
struct dentry *mnt_root; /* root of the mounted tree */
struct super_block *mnt_sb; /* pointer to superblock */
int mnt_flags;
+ void *data;
};
struct file; /* forward dec */
diff --git a/include/linux/namei.h b/include/linux/namei.h
index 5a5ff57..f10c7f2 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -74,6 +74,7 @@
const char *, unsigned int, struct path *);
extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
+extern struct dentry *lookup_one_len2(const char *, struct vfsmount *mnt, struct dentry *, int);
extern int follow_down_one(struct path *);
extern int follow_down(struct path *);
diff --git a/include/linux/netfilter/xt_qtaguid.h b/include/linux/netfilter/xt_qtaguid.h
index ca60fbd..1c67155 100644
--- a/include/linux/netfilter/xt_qtaguid.h
+++ b/include/linux/netfilter/xt_qtaguid.h
@@ -10,4 +10,5 @@
#define XT_QTAGUID_SOCKET XT_OWNER_SOCKET
#define xt_qtaguid_match_info xt_owner_match_info
+int qtaguid_untag(struct socket *sock, bool kernel);
#endif /* _XT_QTAGUID_MATCH_H */
diff --git a/include/linux/security.h b/include/linux/security.h
index 3fd19934..4b5df69 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -2450,7 +2450,7 @@
unsigned long arg4,
unsigned long arg5)
{
- return cap_task_prctl(option, arg2, arg3, arg3, arg5);
+ return cap_task_prctl(option, arg2, arg3, arg4, arg5);
}
static inline void security_task_to_inode(struct task_struct *p, struct inode *inode)
diff --git a/include/net/addrconf.h b/include/net/addrconf.h
index 2cbf0ba..5de60f8 100644
--- a/include/net/addrconf.h
+++ b/include/net/addrconf.h
@@ -1,8 +1,9 @@
#ifndef _ADDRCONF_H
#define _ADDRCONF_H
-#define MAX_RTR_SOLICITATIONS 3
+#define MAX_RTR_SOLICITATIONS -1 /* unlimited */
#define RTR_SOLICITATION_INTERVAL (4*HZ)
+#define RTR_SOLICITATION_MAX_INTERVAL (3600*HZ) /* 1 hour */
#define MIN_VALID_LIFETIME (2*3600) /* 2 hours */
diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h
index 4ac12e1..21666fb 100644
--- a/include/net/fib_rules.h
+++ b/include/net/fib_rules.h
@@ -8,6 +8,11 @@
#include <net/flow.h>
#include <net/rtnetlink.h>
+struct fib_kuid_range {
+ kuid_t start;
+ kuid_t end;
+};
+
struct fib_rule {
struct list_head list;
atomic_t refcnt;
@@ -23,8 +28,7 @@
struct fib_rule __rcu *ctarget;
char iifname[IFNAMSIZ];
char oifname[IFNAMSIZ];
- kuid_t uid_start;
- kuid_t uid_end;
+ struct fib_kuid_range uid_range;
struct rcu_head rcu;
struct net * fr_net;
};
@@ -83,8 +87,7 @@
[FRA_FWMASK] = { .type = NLA_U32 }, \
[FRA_TABLE] = { .type = NLA_U32 }, \
[FRA_GOTO] = { .type = NLA_U32 }, \
- [FRA_UID_START] = { .type = NLA_U32 }, \
- [FRA_UID_END] = { .type = NLA_U32 }
+ [FRA_UID_RANGE] = { .len = sizeof(struct fib_rule_uid_range) }
static inline void fib_rule_get(struct fib_rule *rule)
{
diff --git a/include/net/flow.h b/include/net/flow.h
index 1426681f..15ecdd0 100644
--- a/include/net/flow.h
+++ b/include/net/flow.h
@@ -32,7 +32,7 @@
#define FLOWI_FLAG_CAN_SLEEP 0x02
#define FLOWI_FLAG_KNOWN_NH 0x04
__u32 flowic_secid;
- kuid_t flowic_uid;
+ kuid_t flowic_uid;
};
union flowi_uli {
diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h
index 100fb8c..cc4a879 100644
--- a/include/net/if_inet6.h
+++ b/include/net/if_inet6.h
@@ -195,6 +195,9 @@
struct inet6_dev *next;
struct ipv6_devconf cnf;
struct ipv6_devstat stats;
+
+ __s32 rs_interval; /* in jiffies */
+
unsigned long tstamp; /* ipv6InterfaceTable update timestamp */
struct rcu_head rcu;
};
diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
index 6be6deb..30f068f 100644
--- a/include/net/ip6_route.h
+++ b/include/net/ip6_route.h
@@ -139,7 +139,8 @@
int oif, u32 mark, kuid_t uid);
extern void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk,
__be32 mtu);
-extern void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark);
+extern void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark,
+ kuid_t uid);
extern void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk);
struct netlink_callback;
diff --git a/include/net/route.h b/include/net/route.h
index 647bb2a..4fe6762 100644
--- a/include/net/route.h
+++ b/include/net/route.h
@@ -142,7 +142,7 @@
flowi4_init_output(fl4, oif, sk ? sk->sk_mark : 0, tos,
RT_SCOPE_UNIVERSE, proto,
sk ? inet_sk_flowi_flags(sk) : 0,
- daddr, saddr, dport, sport, sk ? sock_i_uid(sk) : 0);
+ daddr, saddr, dport, sport, sock_net_uid(net, sk));
if (sk)
security_sk_classify_flow(sk, flowi4_to_flowi(fl4));
return ip_route_output_flow(net, fl4, sk);
@@ -254,7 +254,7 @@
flowi4_init_output(fl4, oif, sk->sk_mark, tos, RT_SCOPE_UNIVERSE,
protocol, flow_flags, dst, src, dport, sport,
- sock_i_uid(sk));
+ sk->sk_uid);
}
static inline struct rtable *ip_route_connect(struct flowi4 *fl4,
diff --git a/include/net/sock.h b/include/net/sock.h
index 2947dbf..cadadda 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -395,6 +395,7 @@
void *sk_security;
#endif
__u32 sk_mark;
+ kuid_t sk_uid;
u32 sk_classid;
struct cg_proto *sk_cgrp;
void (*sk_state_change)(struct sock *sk);
@@ -1709,6 +1710,7 @@
sk->sk_wq = parent->wq;
parent->sk = sk;
sk_set_socket(sk, parent);
+ sk->sk_uid = SOCK_INODE(parent)->i_uid;
security_sock_graft(sk, parent);
write_unlock_bh(&sk->sk_callback_lock);
}
@@ -1716,6 +1718,11 @@
extern kuid_t sock_i_uid(struct sock *sk);
extern unsigned long sock_i_ino(struct sock *sk);
+static inline kuid_t sock_net_uid(const struct net *net, const struct sock *sk)
+{
+ return sk ? sk->sk_uid : make_kuid(net->user_ns, 0);
+}
+
static inline struct dst_entry *
__sk_dst_get(struct sock *sk)
{
diff --git a/include/uapi/linux/fib_rules.h b/include/uapi/linux/fib_rules.h
index 9dcdb62..209abc4 100644
--- a/include/uapi/linux/fib_rules.h
+++ b/include/uapi/linux/fib_rules.h
@@ -29,6 +29,11 @@
__u32 flags;
};
+struct fib_rule_uid_range {
+ __u32 start;
+ __u32 end;
+};
+
enum {
FRA_UNSPEC,
FRA_DST, /* destination address */
@@ -49,8 +54,9 @@
FRA_TABLE, /* Extended table id */
FRA_FWMASK, /* mask for netfilter mark */
FRA_OIFNAME,
- FRA_UID_START, /* UID range */
- FRA_UID_END,
+ FRA_PAD,
+ FRA_L3MDEV, /* iif or oif is l3mdev goto its table */
+ FRA_UID_RANGE, /* UID range */
__FRA_MAX
};
diff --git a/include/uapi/linux/hw_breakpoint.h b/include/uapi/linux/hw_breakpoint.h
index b04000a..2b65efd 100644
--- a/include/uapi/linux/hw_breakpoint.h
+++ b/include/uapi/linux/hw_breakpoint.h
@@ -4,7 +4,11 @@
enum {
HW_BREAKPOINT_LEN_1 = 1,
HW_BREAKPOINT_LEN_2 = 2,
+ HW_BREAKPOINT_LEN_3 = 3,
HW_BREAKPOINT_LEN_4 = 4,
+ HW_BREAKPOINT_LEN_5 = 5,
+ HW_BREAKPOINT_LEN_6 = 6,
+ HW_BREAKPOINT_LEN_7 = 7,
HW_BREAKPOINT_LEN_8 = 8,
};
diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h
index a3fd4d1..6c7fbd5 100644
--- a/include/uapi/linux/ipv6.h
+++ b/include/uapi/linux/ipv6.h
@@ -162,9 +162,26 @@
DEVCONF_NDISC_NOTIFY,
DEVCONF_ACCEPT_RA_PREFIX_ROUTE,
DEVCONF_ACCEPT_RA_RT_TABLE,
+ DEVCONF_MLDV1_UNSOLICITED_REPORT_INTERVAL,
+ DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL,
+ DEVCONF_SUPPRESS_FRAG_NDISC,
+ DEVCONF_ACCEPT_RA_FROM_LOCAL,
DEVCONF_USE_OPTIMISTIC,
DEVCONF_ACCEPT_RA_MTU,
+ DEVCONF_STABLE_SECRET,
DEVCONF_USE_OIF_ADDRS_ONLY,
+ DEVCONF_ACCEPT_RA_MIN_HOP_LIMIT,
+ DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN,
+ DEVCONF_DROP_UNICAST_IN_L2_MULTICAST,
+ DEVCONF_DROP_UNSOLICITED_NA,
+ DEVCONF_KEEP_ADDR_ON_DOWN,
+ DEVCONF_RTR_SOLICIT_MAX_INTERVAL,
+ DEVCONF_SEG6_ENABLED,
+ DEVCONF_SEG6_REQUIRE_HMAC,
+ DEVCONF_ENHANCED_DAD,
+ DEVCONF_ADDR_GEN_MODE,
+ DEVCONF_DISABLE_POLICY,
+ DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN,
DEVCONF_MAX
};
diff --git a/include/uapi/linux/magic.h b/include/uapi/linux/magic.h
index 04c5ff2..fc9697a 100644
--- a/include/uapi/linux/magic.h
+++ b/include/uapi/linux/magic.h
@@ -52,7 +52,7 @@
#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs"
#define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs"
-#define SDCARDFS_SUPER_MAGIC 0xb550ca10
+#define SDCARDFS_SUPER_MAGIC 0x5dca2df5
#define SMB_SUPER_MAGIC 0x517B
#define CGROUP_SUPER_MAGIC 0x27e0eb
diff --git a/include/uapi/linux/msm_kgsl.h b/include/uapi/linux/msm_kgsl.h
index 9bcdaf7..412b173 100644
--- a/include/uapi/linux/msm_kgsl.h
+++ b/include/uapi/linux/msm_kgsl.h
@@ -57,6 +57,8 @@
#define KGSL_CONTEXT_TYPE_RS 4
#define KGSL_CONTEXT_TYPE_UNKNOWN 0x1E
+#define KGSL_CONTEXT_INVALIDATE_ON_FAULT 0x10000000
+
#define KGSL_CONTEXT_INVALID 0xffffffff
/*
diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h
index 28bb0b3..5210e82 100644
--- a/include/uapi/linux/prctl.h
+++ b/include/uapi/linux/prctl.h
@@ -158,4 +158,11 @@
#define PR_SET_VMA 0x53564d41
# define PR_SET_VMA_ANON_NAME 0
+/* Control the ambient capability set */
+#define PR_CAP_AMBIENT 47
+# define PR_CAP_AMBIENT_IS_SET 1
+# define PR_CAP_AMBIENT_RAISE 2
+# define PR_CAP_AMBIENT_LOWER 3
+# define PR_CAP_AMBIENT_CLEAR_ALL 4
+
#endif /* _LINUX_PRCTL_H */
diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h
index 07c1146..99c4df8 100644
--- a/include/uapi/linux/rtnetlink.h
+++ b/include/uapi/linux/rtnetlink.h
@@ -297,6 +297,13 @@
RTA_TABLE,
RTA_MARK,
RTA_MFC_STATS,
+ RTA_VIA,
+ RTA_NEWDST,
+ RTA_PREF,
+ RTA_ENCAP_TYPE,
+ RTA_ENCAP,
+ RTA_EXPIRES,
+ RTA_PAD,
RTA_UID,
__RTA_MAX
};
diff --git a/include/uapi/linux/sysctl.h b/include/uapi/linux/sysctl.h
index 0b3eead..9780e8a 100644
--- a/include/uapi/linux/sysctl.h
+++ b/include/uapi/linux/sysctl.h
@@ -569,6 +569,8 @@
NET_IPV6_ACCEPT_RA_RT_INFO_MAX_PLEN=22,
NET_IPV6_PROXY_NDP=23,
NET_IPV6_ACCEPT_SOURCE_ROUTE=25,
+ NET_IPV6_ACCEPT_RA_FROM_LOCAL=26,
+ NET_IPV6_ACCEPT_RA_RT_INFO_MIN_PLEN=27,
__NET_IPV6_MAX
};
diff --git a/include/uapi/linux/usb/functionfs.h b/include/uapi/linux/usb/functionfs.h
index 0f8f7be..ccaa477 100644
--- a/include/uapi/linux/usb/functionfs.h
+++ b/include/uapi/linux/usb/functionfs.h
@@ -169,6 +169,17 @@
*/
#define FUNCTIONFS_ENDPOINT_REVMAP _IO('g', 129)
+/*
+ * Returns endpoint descriptor. If function is not active returns -ENODEV.
+ */
+#define FUNCTIONFS_ENDPOINT_DESC _IOR('g', 130, \
+ struct usb_endpoint_descriptor)
+
+/*
+ * Sets a buffer length for which all r/w operations under that size use a
+ * preallocated buffer. Behavior of larger operations does not change.
+ */
+#define FUNCTIONFS_ENDPOINT_ALLOC _IOR('g', 231, __u32)
#endif /* _UAPI__LINUX_FUNCTIONFS_H__ */
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index bb0248f..88d97d2 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -754,7 +754,7 @@
}
mode &= ~current_umask();
- ret = vfs_create(dir, path->dentry, mode, true);
+ ret = vfs_create2(path->mnt, dir, path->dentry, mode, true);
path->dentry->d_fsdata = NULL;
if (ret)
return ERR_PTR(ret);
@@ -770,7 +770,7 @@
if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY))
return ERR_PTR(-EINVAL);
acc = oflag2acc[oflag & O_ACCMODE];
- if (inode_permission(path->dentry->d_inode, acc))
+ if (inode_permission2(path->mnt, path->dentry->d_inode, acc))
return ERR_PTR(-EACCES);
return dentry_open(path, oflag, current_cred());
}
@@ -803,7 +803,7 @@
ro = mnt_want_write(mnt); /* we'll drop it in any case */
error = 0;
mutex_lock(&root->d_inode->i_mutex);
- path.dentry = lookup_one_len(name->name, root, strlen(name->name));
+ path.dentry = lookup_one_len2(name->name, mnt, root, strlen(name->name));
if (IS_ERR(path.dentry)) {
error = PTR_ERR(path.dentry);
goto out_putfd;
@@ -874,7 +874,7 @@
if (err)
goto out_name;
mutex_lock_nested(&mnt->mnt_root->d_inode->i_mutex, I_MUTEX_PARENT);
- dentry = lookup_one_len(name->name, mnt->mnt_root,
+ dentry = lookup_one_len2(name->name, mnt, mnt->mnt_root,
strlen(name->name));
if (IS_ERR(dentry)) {
err = PTR_ERR(dentry);
@@ -886,7 +886,7 @@
err = -ENOENT;
} else {
ihold(inode);
- err = vfs_unlink(dentry->d_parent->d_inode, dentry);
+ err = vfs_unlink2(mnt, dentry->d_parent->d_inode, dentry);
}
dput(dentry);
diff --git a/kernel/printk.c b/kernel/printk.c
index 4dfa82a..c35e62c 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -3235,7 +3235,7 @@
{
dump_stack_print_info(log_lvl);
- printk("%stask: %p ti: %p task.ti: %p\n",
+ printk("%stask: %pP ti: %pP task.ti: %pP\n",
log_lvl, current, current_thread_info(),
task_thread_info(current));
}
diff --git a/kernel/seccomp.c b/kernel/seccomp.c
index 0208bbe..944ef7f 100644
--- a/kernel/seccomp.c
+++ b/kernel/seccomp.c
@@ -342,24 +342,24 @@
put_seccomp_filter(thread);
smp_store_release(&thread->seccomp.filter,
caller->seccomp.filter);
+
+ /*
+ * Don't let an unprivileged task work around
+ * the no_new_privs restriction by creating
+ * a thread that sets it up, enters seccomp,
+ * then dies.
+ */
+ if (task_no_new_privs(caller))
+ task_set_no_new_privs(thread);
+
/*
* Opt the other thread into seccomp if needed.
* As threads are considered to be trust-realm
* equivalent (see ptrace_may_access), it is safe to
* allow one thread to transition the other.
*/
- if (thread->seccomp.mode == SECCOMP_MODE_DISABLED) {
- /*
- * Don't let an unprivileged task work around
- * the no_new_privs restriction by creating
- * a thread that sets it up, enters seccomp,
- * then dies.
- */
- if (task_no_new_privs(caller))
- task_set_no_new_privs(thread);
-
+ if (thread->seccomp.mode == SECCOMP_MODE_DISABLED)
seccomp_assign_mode(thread, SECCOMP_MODE_FILTER);
- }
}
}
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 1d09b87..eaa062a 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -129,6 +129,7 @@
static int __maybe_unused one = 1;
static int __maybe_unused two = 2;
static int __maybe_unused three = 3;
+static int __maybe_unused four = 4;
static unsigned long one_ul = 1;
static int one_hundred = 100;
#ifdef CONFIG_PRINTK
@@ -953,7 +954,7 @@
.mode = 0644,
.proc_handler = proc_dointvec_minmax_sysadmin,
.extra1 = &zero,
- .extra2 = &two,
+ .extra2 = &four,
},
#endif
{
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 3f2fb33..5dc3603 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -39,6 +39,7 @@
cred->cap_inheritable = CAP_EMPTY_SET;
cred->cap_permitted = CAP_FULL_SET;
cred->cap_effective = CAP_FULL_SET;
+ cred->cap_ambient = CAP_EMPTY_SET;
cred->cap_bset = CAP_FULL_SET;
#ifdef CONFIG_KEYS
key_put(cred->request_key_auth);
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 3784453..7f5a08b 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -377,6 +377,32 @@
s16 precision; /* # of digits/chars */
};
+int kptr_restrict __read_mostly = 4;
+
+/*
+ * Always cleanse %p and %pK specifiers
+ */
+static inline int kptr_restrict_always_cleanse_pointers(void)
+{
+ return kptr_restrict >= 3;
+}
+
+/*
+ * Always cleanse physical addresses (%pa* specifiers)
+ */
+static inline int kptr_restrict_cleanse_addresses(void)
+{
+ return kptr_restrict >= 4;
+}
+
+/*
+ * Always cleanse resource addresses (%p[rR] specifiers)
+ */
+static inline int kptr_restrict_cleanse_resources(void)
+{
+ return kptr_restrict >= 4;
+}
+
static noinline_for_stack
char *number(char *buf, char *end, unsigned long long num,
struct printf_spec spec)
@@ -620,6 +646,7 @@
char *p = sym, *pend = sym + sizeof(sym);
int decode = (fmt[0] == 'R') ? 1 : 0;
+ int cleanse = kptr_restrict_cleanse_resources();
const struct printf_spec *specp;
*p++ = '[';
@@ -643,10 +670,11 @@
specp = &mem_spec;
decode = 0;
}
- p = number(p, pend, res->start, *specp);
+ p = number(p, pend, cleanse ? 0UL : res->start, *specp);
if (res->start != res->end) {
*p++ = '-';
- p = number(p, pend, res->end, *specp);
+ p = number(p, pend,
+ cleanse ? res->end - res->start : res->end, *specp);
}
if (decode) {
if (res->flags & IORESOURCE_MEM_64)
@@ -665,6 +693,7 @@
*p = '\0';
return string(buf, end, sym, spec);
+
}
static noinline_for_stack
@@ -982,8 +1011,6 @@
return number(buf, end, *(const netdev_features_t *)addr, spec);
}
-int kptr_restrict __read_mostly;
-
/*
* Show a '%p' thing. A kernel extension is that the '%p' is followed
* by an extra set of alphanumeric characters that are extended format
@@ -1031,6 +1058,7 @@
* Do not use this feature without some mechanism to verify the
* correctness of the format string and va_list arguments.
* - 'K' For a kernel pointer that should be hidden from unprivileged users
+ * - 'P' For a kernel pointer that should be shown to all users
* - 'NF' For a netdev_features_t
* - 'h[CDN]' For a variable-length buffer, it prints it as a hex string with
* a certain separator (' ' by default):
@@ -1044,6 +1072,15 @@
* Note: The difference between 'S' and 'F' is that on ia64 and ppc64
* function pointers are really function descriptors, which contain a
* pointer to the real address.
+ *
+ * Note: That for kptr_restrict set to 3, %p and %pK have the same
+ * meaning.
+ *
+ * Note: That for kptr_restrict set to 4, %pa will null out the physical
+ * address.
+ *
+ * Note: That for kptr_restrict set to 4, %p[rR] will null out the memory
+ * address.
*/
static noinline_for_stack
char *pointer(const char *fmt, char *buf, char *end, void *ptr,
@@ -1051,7 +1088,7 @@
{
int default_width = 2 * sizeof(void *) + (spec.flags & SPECIAL ? 2 : 0);
- if (!ptr && *fmt != 'K') {
+ if (!ptr && *fmt != 'K' && !kptr_restrict_always_cleanse_pointers()) {
/*
* Print (null) with the same width as a pointer so it makes
* tabular output look nice.
@@ -1108,48 +1145,6 @@
va_end(va);
return buf;
}
- case 'K':
- /*
- * %pK cannot be used in IRQ context because its test
- * for CAP_SYSLOG would be meaningless.
- */
- if (kptr_restrict && (in_irq() || in_serving_softirq() ||
- in_nmi())) {
- if (spec.field_width == -1)
- spec.field_width = default_width;
- return string(buf, end, "pK-error", spec);
- }
-
- switch (kptr_restrict) {
- case 0:
- /* Always print %pK values */
- break;
- case 1: {
- /*
- * Only print the real pointer value if the current
- * process has CAP_SYSLOG and is running with the
- * same credentials it started with. This is because
- * access to files is checked at open() time, but %pK
- * checks permission at read() time. We don't want to
- * leak pointer values if a binary opens a file using
- * %pK and then elevates privileges before reading it.
- */
- const struct cred *cred = current_cred();
-
- if (!has_capability_noaudit(current, CAP_SYSLOG) ||
- !uid_eq(cred->euid, cred->uid) ||
- !gid_eq(cred->egid, cred->gid))
- ptr = NULL;
- break;
- }
- case 2:
- default:
- /* Always print 0's for %pK */
- ptr = NULL;
- break;
- }
- break;
-
case 'N':
switch (fmt[1]) {
case 'F':
@@ -1157,11 +1152,72 @@
}
break;
case 'a':
- spec.flags |= SPECIAL | SMALL | ZEROPAD;
- spec.field_width = sizeof(phys_addr_t) * 2 + 2;
- spec.base = 16;
- return number(buf, end,
- (unsigned long long) *((phys_addr_t *)ptr), spec);
+ {
+ unsigned long long addr = *((phys_addr_t *)ptr);
+ spec.flags |= SPECIAL | SMALL | ZEROPAD;
+ spec.field_width = sizeof(phys_addr_t) * 2 + 2;
+ spec.base = 16;
+ return number(buf, end,
+ kptr_restrict_cleanse_addresses() ? 0UL : addr,
+ spec);
+ }
+ case 'P':
+ /*
+ * an explicitly whitelisted kernel pointer should never be
+ * cleansed
+ */
+ break;
+ default:
+ /*
+ * plain %p, no extension, check if we should always cleanse and
+ * treat like %pK.
+ */
+ if (!kptr_restrict_always_cleanse_pointers()) {
+ break;
+ }
+ /* fallthrough */
+ case 'K':
+ switch (kptr_restrict) {
+ case 0:
+ /* Always print %p values */
+ break;
+ case 1: {
+ const struct cred *cred;
+
+ /*
+ * kptr_restrict==1 cannot be used in IRQ context
+ * because its test for CAP_SYSLOG would be meaningless.
+ */
+ if (in_irq() || in_serving_softirq() || in_nmi()) {
+ if (spec.field_width == -1)
+ spec.field_width = default_width;
+ return string(buf, end, "pK-error", spec);
+ }
+
+ /*
+ * Only print the real pointer value if the current
+ * process has CAP_SYSLOG and is running with the
+ * same credentials it started with. This is because
+ * access to files is checked at open() time, but %p
+ * checks permission at read() time. We don't want to
+ * leak pointer values if a binary opens a file using
+ * %pK and then elevates privileges before reading it.
+ */
+ cred = current_cred();
+ if (!has_capability_noaudit(current, CAP_SYSLOG) ||
+ !uid_eq(cred->euid, cred->uid) ||
+ !gid_eq(cred->egid, cred->gid))
+ ptr = NULL;
+ break;
+ }
+ case 2: /* restrict only %pK */
+ case 3: /* restrict all non-extensioned %p and %pK */
+ case 4: /* restrict all non-extensioned %p, %pK, %pa*, %p[rR] */
+ default:
+ ptr = NULL;
+ break;
+ }
+ break;
}
spec.flags |= SMALL;
if (spec.field_width == -1) {
@@ -1170,7 +1226,7 @@
}
spec.base = 16;
- return number(buf, end, (unsigned long) ptr, spec);
+ return number(buf, end, (unsigned long long) ptr, spec);
}
/*
diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c
index da78f5c..59b632f 100644
--- a/net/core/fib_rules.c
+++ b/net/core/fib_rules.c
@@ -17,6 +17,11 @@
#include <net/sock.h>
#include <net/fib_rules.h>
+static const struct fib_kuid_range fib_kuid_range_unset = {
+ KUIDT_INIT(0),
+ KUIDT_INIT(~0),
+};
+
int fib_default_rule_add(struct fib_rules_ops *ops,
u32 pref, u32 table, u32 flags)
{
@@ -31,9 +36,8 @@
r->pref = pref;
r->table = table;
r->flags = flags;
- r->uid_start = INVALID_UID;
- r->uid_end = INVALID_UID;
r->fr_net = hold_net(ops->fro_net);
+ r->uid_range = fib_kuid_range_unset;
/* The lock is not required here, the list in unreacheable
* at the moment this function is called */
@@ -181,21 +185,32 @@
}
EXPORT_SYMBOL_GPL(fib_rules_unregister);
-static inline kuid_t fib_nl_uid(struct nlattr *nla)
+static int uid_range_set(struct fib_kuid_range *range)
{
- return make_kuid(current_user_ns(), nla_get_u32(nla));
+ return uid_valid(range->start) && uid_valid(range->end);
}
-static int nla_put_uid(struct sk_buff *skb, int idx, kuid_t uid)
+static struct fib_kuid_range nla_get_kuid_range(struct nlattr **tb)
{
- return nla_put_u32(skb, idx, from_kuid_munged(current_user_ns(), uid));
+ struct fib_rule_uid_range *in;
+ struct fib_kuid_range out;
+
+ in = (struct fib_rule_uid_range *)nla_data(tb[FRA_UID_RANGE]);
+
+ out.start = make_kuid(current_user_ns(), in->start);
+ out.end = make_kuid(current_user_ns(), in->end);
+
+ return out;
}
-static int fib_uid_range_match(struct flowi *fl, struct fib_rule *rule)
+static int nla_put_uid_range(struct sk_buff *skb, struct fib_kuid_range *range)
{
- return (!uid_valid(rule->uid_start) && !uid_valid(rule->uid_end)) ||
- (uid_gte(fl->flowi_uid, rule->uid_start) &&
- uid_lte(fl->flowi_uid, rule->uid_end));
+ struct fib_rule_uid_range out = {
+ from_kuid_munged(current_user_ns(), range->start),
+ from_kuid_munged(current_user_ns(), range->end)
+ };
+
+ return nla_put(skb, FRA_UID_RANGE, sizeof(out), &out);
}
static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops,
@@ -212,7 +227,8 @@
if ((rule->mark ^ fl->flowi_mark) & rule->mark_mask)
goto out;
- if (!fib_uid_range_match(fl, rule))
+ if (uid_lt(fl->flowi_uid, rule->uid_range.start) ||
+ uid_gt(fl->flowi_uid, rule->uid_range.end))
goto out;
ret = ops->match(rule, fl, flags);
@@ -385,17 +401,19 @@
} else if (rule->action == FR_ACT_GOTO)
goto errout_free;
- /* UID start and end must either both be valid or both unspecified. */
- rule->uid_start = rule->uid_end = INVALID_UID;
- if (tb[FRA_UID_START] || tb[FRA_UID_END]) {
- if (tb[FRA_UID_START] && tb[FRA_UID_END]) {
- rule->uid_start = fib_nl_uid(tb[FRA_UID_START]);
- rule->uid_end = fib_nl_uid(tb[FRA_UID_END]);
+ if (tb[FRA_UID_RANGE]) {
+ if (current_user_ns() != net->user_ns) {
+ err = -EPERM;
+ goto errout_free;
}
- if (!uid_valid(rule->uid_start) ||
- !uid_valid(rule->uid_end) ||
- !uid_lte(rule->uid_start, rule->uid_end))
- goto errout_free;
+
+ rule->uid_range = nla_get_kuid_range(tb);
+
+ if (!uid_range_set(&rule->uid_range) ||
+ !uid_lte(rule->uid_range.start, rule->uid_range.end))
+ goto errout_free;
+ } else {
+ rule->uid_range = fib_kuid_range_unset;
}
err = ops->configure(rule, skb, frh, tb);
@@ -457,6 +475,7 @@
struct fib_rules_ops *ops = NULL;
struct fib_rule *rule, *tmp;
struct nlattr *tb[FRA_MAX+1];
+ struct fib_kuid_range range;
int err = -EINVAL;
if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh)))
@@ -476,6 +495,14 @@
if (err < 0)
goto errout;
+ if (tb[FRA_UID_RANGE]) {
+ range = nla_get_kuid_range(tb);
+ if (!uid_range_set(&range))
+ goto errout;
+ } else {
+ range = fib_kuid_range_unset;
+ }
+
list_for_each_entry(rule, &ops->rules_list, list) {
if (frh->action && (frh->action != rule->action))
continue;
@@ -504,12 +531,9 @@
(rule->mark_mask != nla_get_u32(tb[FRA_FWMASK])))
continue;
- if (tb[FRA_UID_START] &&
- !uid_eq(rule->uid_start, fib_nl_uid(tb[FRA_UID_START])))
- continue;
-
- if (tb[FRA_UID_END] &&
- !uid_eq(rule->uid_end, fib_nl_uid(tb[FRA_UID_END])))
+ if (uid_range_set(&range) &&
+ (!uid_eq(rule->uid_range.start, range.start) ||
+ !uid_eq(rule->uid_range.end, range.end)))
continue;
if (!ops->compare(rule, frh, tb))
@@ -569,8 +593,7 @@
+ nla_total_size(4) /* FRA_TABLE */
+ nla_total_size(4) /* FRA_FWMARK */
+ nla_total_size(4) /* FRA_FWMASK */
- + nla_total_size(4) /* FRA_UID_START */
- + nla_total_size(4); /* FRA_UID_END */
+ + nla_total_size(sizeof(struct fib_kuid_range));
if (ops->nlmsg_payload)
payload += ops->nlmsg_payload(rule);
@@ -625,10 +648,8 @@
nla_put_u32(skb, FRA_FWMASK, rule->mark_mask)) ||
(rule->target &&
nla_put_u32(skb, FRA_GOTO, rule->target)) ||
- (uid_valid(rule->uid_start) &&
- nla_put_uid(skb, FRA_UID_START, rule->uid_start)) ||
- (uid_valid(rule->uid_end) &&
- nla_put_uid(skb, FRA_UID_END, rule->uid_end)))
+ (uid_range_set(&rule->uid_range) &&
+ nla_put_uid_range(skb, &rule->uid_range)))
goto nla_put_failure;
if (ops->fill(rule, skb, frh) < 0)
goto nla_put_failure;
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index cdd7716..2bc6c11 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -700,7 +700,7 @@
NEIGH_CACHE_STAT_INC(neigh->tbl, destroys);
if (!neigh->dead) {
- pr_warn("Destroying alive neighbour %p\n", neigh);
+ pr_warn("Destroying alive neighbour %pK\n", neigh);
dump_stack();
return;
}
@@ -1317,7 +1317,7 @@
out:
return rc;
discard:
- neigh_dbg(1, "%s: dst=%p neigh=%p\n", __func__, dst, neigh);
+ neigh_dbg(1, "%s: dst=%pK neigh=%pK\n", __func__, dst, neigh);
out_kfree_skb:
rc = -EINVAL;
kfree_skb(skb);
diff --git a/net/core/sock.c b/net/core/sock.c
index de1074c..4e3d16d 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -2292,8 +2292,11 @@
sk->sk_type = sock->type;
sk->sk_wq = sock->wq;
sock->sk = sk;
- } else
+ sk->sk_uid = SOCK_INODE(sock)->i_uid;
+ } else {
sk->sk_wq = NULL;
+ sk->sk_uid = make_kuid(sock_net(sk)->user_ns, 0);
+ }
spin_lock_init(&sk->sk_dst_lock);
rwlock_init(&sk->sk_callback_lock);
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index f5b5586..78c886a 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -89,6 +89,7 @@
#include <linux/netfilter_ipv4.h>
#include <linux/random.h>
#include <linux/slab.h>
+#include <linux/netfilter/xt_qtaguid.h>
#include <asm/uaccess.h>
@@ -445,6 +446,9 @@
if (sk) {
long timeout;
+#ifdef CONFIG_NETFILTER_XT_MATCH_QTAGUID
+ qtaguid_untag(sock, true);
+#endif
sock_rps_reset_flow(sk);
/* Applications forget to leave groups before exiting */
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 5af8781..59e63dc 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -364,6 +364,7 @@
fl4.daddr = daddr;
fl4.saddr = saddr;
fl4.flowi4_mark = mark;
+ fl4.flowi4_uid = sock_net_uid(net, NULL);
fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos);
fl4.flowi4_proto = IPPROTO_ICMP;
security_skb_classify_flow(skb, flowi4_to_flowi(&fl4));
@@ -395,6 +396,7 @@
param->replyopts.opt.opt.faddr : iph->saddr);
fl4->saddr = saddr;
fl4->flowi4_mark = mark;
+ fl4->flowi4_uid = sock_net_uid(net, NULL);
fl4->flowi4_tos = RT_TOS(tos);
fl4->flowi4_proto = IPPROTO_ICMP;
fl4->fl4_icmp_type = type;
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index 008b52b..912cb4d 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -430,7 +430,7 @@
flags,
(opt && opt->opt.srr) ? opt->opt.faddr : ireq->rmt_addr,
ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport,
- sock_i_uid(sk));
+ sk->sk_uid);
security_req_classify_flow(req, flowi4_to_flowi(fl4));
rt = ip_route_output_flow(net, fl4, sk);
if (IS_ERR(rt))
@@ -467,7 +467,7 @@
sk->sk_protocol, inet_sk_flowi_flags(sk),
(opt && opt->opt.srr) ? opt->opt.faddr : ireq->rmt_addr,
ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport,
- sock_i_uid(sk));
+ sk->sk_uid);
security_req_classify_flow(req, flowi4_to_flowi(fl4));
rt = ip_route_output_flow(net, fl4, sk);
if (IS_ERR(rt))
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
index aaa70dd..32703d4 100644
--- a/net/ipv4/ping.c
+++ b/net/ipv4/ping.c
@@ -783,7 +783,7 @@
flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos,
RT_SCOPE_UNIVERSE, sk->sk_protocol,
inet_sk_flowi_flags(sk), faddr, saddr, 0, 0,
- sock_i_uid(sk));
+ sk->sk_uid);
security_sk_classify_flow(sk, flowi4_to_flowi(&fl4));
rt = ip_route_output_flow(net, &fl4, sk);
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index b9c87c8..c9b79f4 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -573,8 +573,7 @@
inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol,
inet_sk_flowi_flags(sk) | FLOWI_FLAG_CAN_SLEEP |
(inet->hdrincl ? FLOWI_FLAG_KNOWN_NH : 0),
- daddr, saddr, 0, 0,
- sock_i_uid(sk));
+ daddr, saddr, 0, 0, sk->sk_uid);
if (!inet->hdrincl) {
err = raw_probe_proto_opt(&fl4, msg);
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 5778011..4f25f88 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -515,7 +515,8 @@
}
EXPORT_SYMBOL(__ip_select_ident);
-static void __build_flow_key(struct flowi4 *fl4, struct sock *sk,
+static void __build_flow_key(const struct net *net, struct flowi4 *fl4,
+ const struct sock *sk,
const struct iphdr *iph,
int oif, u8 tos,
u8 prot, u32 mark, int flow_flags)
@@ -532,22 +533,23 @@
RT_SCOPE_UNIVERSE, prot,
flow_flags,
iph->daddr, iph->saddr, 0, 0,
- sk ? sock_i_uid(sk) : 0);
+ sock_net_uid(net, sk));
}
static void build_skb_flow_key(struct flowi4 *fl4, const struct sk_buff *skb,
- struct sock *sk)
+ const struct sock *sk)
{
+ const struct net *net = dev_net(skb->dev);
const struct iphdr *iph = ip_hdr(skb);
int oif = skb->dev->ifindex;
u8 tos = RT_TOS(iph->tos);
u8 prot = iph->protocol;
u32 mark = skb->mark;
- __build_flow_key(fl4, sk, iph, oif, tos, prot, mark, 0);
+ __build_flow_key(net, fl4, sk, iph, oif, tos, prot, mark, 0);
}
-static void build_sk_flow_key(struct flowi4 *fl4, struct sock *sk)
+static void build_sk_flow_key(struct flowi4 *fl4, const struct sock *sk)
{
const struct inet_sock *inet = inet_sk(sk);
const struct ip_options_rcu *inet_opt;
@@ -561,12 +563,11 @@
RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE,
inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol,
inet_sk_flowi_flags(sk),
- daddr, inet->inet_saddr, 0, 0,
- sock_i_uid(sk));
+ daddr, inet->inet_saddr, 0, 0, sk->sk_uid);
rcu_read_unlock();
}
-static void ip_rt_build_flow_key(struct flowi4 *fl4, struct sock *sk,
+static void ip_rt_build_flow_key(struct flowi4 *fl4, const struct sock *sk,
const struct sk_buff *skb)
{
if (skb)
@@ -762,7 +763,7 @@
rt = (struct rtable *) dst;
- __build_flow_key(&fl4, sk, iph, oif, tos, prot, mark, 0);
+ __build_flow_key(sock_net(sk), &fl4, sk, iph, oif, tos, prot, mark, 0);
__ip_do_redirect(rt, skb, &fl4, true);
}
@@ -976,7 +977,7 @@
if (!mark)
mark = IP4_REPLY_MARK(net, skb->mark);
- __build_flow_key(&fl4, NULL, iph, oif,
+ __build_flow_key(net, &fl4, NULL, iph, oif,
RT_TOS(iph->tos), protocol, mark, flow_flags);
rt = __ip_route_output_key(net, &fl4);
if (!IS_ERR(rt)) {
@@ -992,7 +993,7 @@
struct flowi4 fl4;
struct rtable *rt;
- __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0);
+ __build_flow_key(sock_net(sk), &fl4, sk, iph, 0, 0, 0, 0, 0);
if (!fl4.flowi4_mark)
fl4.flowi4_mark = IP4_REPLY_MARK(sock_net(sk), skb->mark);
@@ -1011,6 +1012,7 @@
struct rtable *rt;
struct dst_entry *odst = NULL;
bool new = false;
+ struct net *net = sock_net(sk);
bh_lock_sock(sk);
odst = sk_dst_get(sk);
@@ -1020,7 +1022,7 @@
goto out;
}
- __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0);
+ __build_flow_key(net, &fl4, sk, iph, 0, 0, 0, 0, 0);
rt = (struct rtable *)odst;
if (odst->obsolete && odst->ops->check(odst, 0) == NULL) {
@@ -1060,7 +1062,7 @@
struct flowi4 fl4;
struct rtable *rt;
- __build_flow_key(&fl4, NULL, iph, oif,
+ __build_flow_key(net, &fl4, NULL, iph, oif,
RT_TOS(iph->tos), protocol, mark, flow_flags);
rt = __ip_route_output_key(net, &fl4);
if (!IS_ERR(rt)) {
@@ -1075,9 +1077,10 @@
const struct iphdr *iph = (const struct iphdr *) skb->data;
struct flowi4 fl4;
struct rtable *rt;
+ struct net *net = sock_net(sk);
- __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0);
- rt = __ip_route_output_key(sock_net(sk), &fl4);
+ __build_flow_key(net, &fl4, sk, iph, 0, 0, 0, 0, 0);
+ rt = __ip_route_output_key(net, &fl4);
if (!IS_ERR(rt)) {
__ip_do_redirect(rt, skb, &fl4, false);
ip_rt_put(rt);
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index c94032b..296a0a4 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -353,8 +353,7 @@
RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, IPPROTO_TCP,
inet_sk_flowi_flags(sk),
(opt && opt->srr) ? opt->faddr : ireq->rmt_addr,
- ireq->loc_addr, th->source, th->dest,
- sock_i_uid(sk));
+ ireq->loc_addr, th->source, th->dest, sk->sk_uid);
security_req_classify_flow(req, flowi4_to_flowi(&fl4));
rt = ip_route_output_key(sock_net(sk), &fl4);
if (IS_ERR(rt)) {
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index e2f1dff..b15acfc 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -647,6 +647,7 @@
arg.iov[0].iov_base = (unsigned char *)&rep;
arg.iov[0].iov_len = sizeof(rep.th);
+ net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev);
#ifdef CONFIG_TCP_MD5SIG
hash_location = tcp_parse_md5sig_option(th);
if (!sk && hash_location) {
@@ -657,7 +658,7 @@
* Incoming packet is checked with md5 hash with finding key,
* no RST generated if md5 hash doesn't match.
*/
- sk1 = __inet_lookup_listener(dev_net(skb_dst(skb)->dev),
+ sk1 = __inet_lookup_listener(net,
&tcp_hashinfo, ip_hdr(skb)->saddr,
th->source, ip_hdr(skb)->daddr,
ntohs(th->source), inet_iif(skb));
@@ -705,8 +706,8 @@
if (sk)
arg.bound_dev_if = sk->sk_bound_dev_if;
- net = dev_net(skb_dst(skb)->dev);
arg.tos = ip_hdr(skb)->tos;
+ arg.uid = sock_net_uid(net, sk && sk_fullsock(sk) ? sk : NULL);
ip_send_unicast_reply(*this_cpu_ptr(net->ipv4.tcp_sk),
skb, ip_hdr(skb)->saddr,
ip_hdr(skb)->daddr, &arg, arg.iov[0].iov_len);
@@ -727,7 +728,8 @@
outside socket context is ugly, certainly. What can I do?
*/
-static void tcp_v4_send_ack(struct sk_buff *skb, u32 seq, u32 ack,
+static void tcp_v4_send_ack(const struct sock *sk, struct sk_buff *skb,
+ u32 seq, u32 ack,
u32 win, u32 tsval, u32 tsecr, int oif,
struct tcp_md5sig_key *key,
int reply_flags, u8 tos)
@@ -742,7 +744,7 @@
];
} rep;
struct ip_reply_arg arg;
- struct net *net = dev_net(skb_dst(skb)->dev);
+ struct net *net = sock_net(sk);
memset(&rep.th, 0, sizeof(struct tcphdr));
memset(&arg, 0, sizeof(arg));
@@ -791,6 +793,7 @@
if (oif)
arg.bound_dev_if = oif;
arg.tos = tos;
+ arg.uid = sock_net_uid(net, sk_fullsock(sk) ? sk : NULL);
ip_send_unicast_reply(*this_cpu_ptr(net->ipv4.tcp_sk),
skb, ip_hdr(skb)->saddr,
ip_hdr(skb)->daddr, &arg, arg.iov[0].iov_len);
@@ -803,7 +806,7 @@
struct inet_timewait_sock *tw = inet_twsk(sk);
struct tcp_timewait_sock *tcptw = tcp_twsk(sk);
- tcp_v4_send_ack(skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
+ tcp_v4_send_ack(sk, skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale,
tcp_time_stamp + tcptw->tw_ts_offset,
tcptw->tw_ts_recent,
@@ -822,7 +825,7 @@
/* sk->sk_state == TCP_LISTEN -> for regular TCP_SYN_RECV
* sk->sk_state == TCP_SYN_RECV -> for Fast Open.
*/
- tcp_v4_send_ack(skb, (sk->sk_state == TCP_LISTEN) ?
+ tcp_v4_send_ack(sk, skb, (sk->sk_state == TCP_LISTEN) ?
tcp_rsk(req)->snt_isn + 1 : tcp_sk(sk)->snd_nxt,
tcp_rsk(req)->rcv_nxt, req->rcv_wnd,
tcp_time_stamp,
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 6d181ec..333dcb2 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -970,7 +970,7 @@
RT_SCOPE_UNIVERSE, sk->sk_protocol,
inet_sk_flowi_flags(sk)|FLOWI_FLAG_CAN_SLEEP,
faddr, saddr, dport, inet->inet_sport,
- sock_i_uid(sk));
+ sk->sk_uid);
security_sk_classify_flow(sk, flowi4_to_flowi(fl4));
rt = ip_route_output_flow(net, fl4, sk);
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index f96b907..57da416 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -113,6 +113,27 @@
return (cstamp - INITIAL_JIFFIES) * 100UL / HZ;
}
+static inline s32 rfc3315_s14_backoff_init(s32 irt)
+{
+ /* multiply 'initial retransmission time' by 0.9 .. 1.1 */
+ u64 tmp = (900000 + prandom_u32() % 200001) * (u64)irt;
+ do_div(tmp, 1000000);
+ return (s32)tmp;
+}
+
+static inline s32 rfc3315_s14_backoff_update(s32 rt, s32 mrt)
+{
+ /* multiply 'retransmission timeout' by 1.9 .. 2.1 */
+ u64 tmp = (1900000 + prandom_u32() % 200001) * (u64)rt;
+ do_div(tmp, 1000000);
+ if ((s32)tmp > mrt) {
+ /* multiply 'maximum retransmission time' by 0.9 .. 1.1 */
+ tmp = (900000 + prandom_u32() % 200001) * (u64)mrt;
+ do_div(tmp, 1000000);
+ }
+ return (s32)tmp;
+}
+
#ifdef CONFIG_SYSCTL
static void addrconf_sysctl_register(struct inet6_dev *idev);
static void addrconf_sysctl_unregister(struct inet6_dev *idev);
@@ -182,6 +203,7 @@
.dad_transmits = 1,
.rtr_solicits = MAX_RTR_SOLICITATIONS,
.rtr_solicit_interval = RTR_SOLICITATION_INTERVAL,
+ .rtr_solicit_max_interval = RTR_SOLICITATION_MAX_INTERVAL,
.rtr_solicit_delay = MAX_RTR_SOLICITATION_DELAY,
#ifdef CONFIG_IPV6_PRIVACY
.use_tempaddr = 0,
@@ -197,6 +219,7 @@
.accept_ra_rtr_pref = 1,
.rtr_probe_interval = 60 * HZ,
#ifdef CONFIG_IPV6_ROUTE_INFO
+ .accept_ra_rt_info_min_plen = 0,
.accept_ra_rt_info_max_plen = 0,
#endif
#endif
@@ -220,6 +243,7 @@
.dad_transmits = 1,
.rtr_solicits = MAX_RTR_SOLICITATIONS,
.rtr_solicit_interval = RTR_SOLICITATION_INTERVAL,
+ .rtr_solicit_max_interval = RTR_SOLICITATION_MAX_INTERVAL,
.rtr_solicit_delay = MAX_RTR_SOLICITATION_DELAY,
#ifdef CONFIG_IPV6_PRIVACY
.use_tempaddr = 0,
@@ -235,6 +259,7 @@
.accept_ra_rtr_pref = 1,
.rtr_probe_interval = 60 * HZ,
#ifdef CONFIG_IPV6_ROUTE_INFO
+ .accept_ra_rt_info_min_plen = 0,
.accept_ra_rt_info_max_plen = 0,
#endif
#endif
@@ -3259,12 +3284,14 @@
goto out;
spin_lock(&ifp->lock);
- if (ifp->probes++ < idev->cnf.rtr_solicits) {
+ if (ifp->probes++ < idev->cnf.rtr_solicits || idev->cnf.rtr_solicits < 0) {
+ idev->rs_interval = rfc3315_s14_backoff_update(
+ idev->rs_interval, idev->cnf.rtr_solicit_max_interval);
/* The wait after the last probe can be shorter */
addrconf_mod_timer(ifp, AC_RS,
(ifp->probes == idev->cnf.rtr_solicits) ?
idev->cnf.rtr_solicit_delay :
- idev->cnf.rtr_solicit_interval);
+ idev->rs_interval);
spin_unlock(&ifp->lock);
ndisc_send_rs(idev->dev, &ifp->addr, &in6addr_linklocal_allrouters);
@@ -3421,7 +3448,7 @@
*/
if (ipv6_accept_ra(ifp->idev) &&
- ifp->idev->cnf.rtr_solicits > 0 &&
+ ifp->idev->cnf.rtr_solicits != 0 &&
(dev->flags&IFF_LOOPBACK) == 0 &&
(ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) {
/*
@@ -3432,9 +3459,11 @@
ndisc_send_rs(ifp->idev->dev, &ifp->addr, &in6addr_linklocal_allrouters);
spin_lock_bh(&ifp->lock);
+ ifp->idev->rs_interval = rfc3315_s14_backoff_init(
+ ifp->idev->cnf.rtr_solicit_interval);
ifp->probes = 1;
ifp->idev->if_flags |= IF_RS_SENT;
- addrconf_mod_timer(ifp, AC_RS, ifp->idev->cnf.rtr_solicit_interval);
+ addrconf_mod_timer(ifp, AC_RS, ifp->idev->rs_interval);
spin_unlock_bh(&ifp->lock);
}
}
@@ -4270,6 +4299,8 @@
array[DEVCONF_RTR_SOLICITS] = cnf->rtr_solicits;
array[DEVCONF_RTR_SOLICIT_INTERVAL] =
jiffies_to_msecs(cnf->rtr_solicit_interval);
+ array[DEVCONF_RTR_SOLICIT_MAX_INTERVAL] =
+ jiffies_to_msecs(cnf->rtr_solicit_max_interval);
array[DEVCONF_RTR_SOLICIT_DELAY] =
jiffies_to_msecs(cnf->rtr_solicit_delay);
array[DEVCONF_FORCE_MLD_VERSION] = cnf->force_mld_version;
@@ -4288,6 +4319,7 @@
array[DEVCONF_RTR_PROBE_INTERVAL] =
jiffies_to_msecs(cnf->rtr_probe_interval);
#ifdef CONFIG_IPV6_ROUTE_INFO
+ array[DEVCONF_ACCEPT_RA_RT_INFO_MIN_PLEN] = cnf->accept_ra_rt_info_min_plen;
array[DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN] = cnf->accept_ra_rt_info_max_plen;
#endif
#endif
@@ -4452,7 +4484,7 @@
return -EINVAL;
if (!ipv6_accept_ra(idev))
return -EINVAL;
- if (idev->cnf.rtr_solicits <= 0)
+ if (idev->cnf.rtr_solicits == 0)
return -EINVAL;
write_lock_bh(&idev->lock);
@@ -4907,6 +4939,13 @@
.proc_handler = proc_dointvec_jiffies,
},
{
+ .procname = "router_solicitation_max_interval",
+ .data = &ipv6_devconf.rtr_solicit_max_interval,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_jiffies,
+ },
+ {
.procname = "router_solicitation_delay",
.data = &ipv6_devconf.rtr_solicit_delay,
.maxlen = sizeof(int),
@@ -4995,6 +5034,13 @@
},
#ifdef CONFIG_IPV6_ROUTE_INFO
{
+ .procname = "accept_ra_rt_info_min_plen",
+ .data = &ipv6_devconf.accept_ra_rt_info_min_plen,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
.procname = "accept_ra_rt_info_max_plen",
.data = &ipv6_devconf.accept_ra_rt_info_max_plen,
.maxlen = sizeof(int),
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 04e88b5..e3a2601 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -696,7 +696,7 @@
fl6.flowi6_mark = sk->sk_mark;
fl6.fl6_dport = inet->inet_dport;
fl6.fl6_sport = inet->inet_sport;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
rcu_read_lock();
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c
index b903e19..15e41c5 100644
--- a/net/ipv6/ah6.c
+++ b/net/ipv6/ah6.c
@@ -628,9 +628,9 @@
return;
if (type == NDISC_REDIRECT)
- ip6_redirect(skb, net, 0, 0);
+ ip6_redirect(skb, net, 0, 0, sock_net_uid(net, NULL));
else
- ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID);
+ ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
xfrm_state_put(x);
}
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index 7d76630..61c7a13 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -162,7 +162,7 @@
fl6.flowi6_mark = sk->sk_mark;
fl6.fl6_dport = inet->inet_dport;
fl6.fl6_sport = inet->inet_sport;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
if (!fl6.flowi6_oif && (addr_type&IPV6_ADDR_MULTICAST))
fl6.flowi6_oif = np->mcast_oif;
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index fdc81cb..4ad6525 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -447,9 +447,9 @@
return;
if (type == NDISC_REDIRECT)
- ip6_redirect(skb, net, 0, 0);
+ ip6_redirect(skb, net, 0, 0, sock_net_uid(net, NULL));
else
- ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID);
+ ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
xfrm_state_put(x);
}
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 2a6a0e7..b022004 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -90,9 +90,9 @@
struct net *net = dev_net(skb->dev);
if (type == ICMPV6_PKT_TOOBIG)
- ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID);
+ ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
else if (type == NDISC_REDIRECT)
- ip6_redirect(skb, net, 0, 0);
+ ip6_redirect(skb, net, 0, 0, sock_net_uid(net, NULL));
if (!(type & ICMPV6_INFOMSG_MASK))
if (icmp6->icmp6_type == ICMPV6_ECHO_REQUEST)
@@ -467,6 +467,7 @@
fl6.flowi6_oif = iif;
fl6.fl6_icmp_type = type;
fl6.fl6_icmp_code = code;
+ fl6.flowi6_uid = sock_net_uid(net, NULL);
security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
sk = icmpv6_xmit_lock(net);
@@ -572,6 +573,7 @@
fl6.flowi6_oif = skb->dev->ifindex;
fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY;
fl6.flowi6_mark = mark;
+ fl6.flowi6_uid = sock_net_uid(net, NULL);
security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
sk = icmpv6_xmit_lock(net);
diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c
index 157f2b2..a2072a8 100644
--- a/net/ipv6/inet6_connection_sock.c
+++ b/net/ipv6/inet6_connection_sock.c
@@ -86,7 +86,7 @@
fl6->flowi6_mark = inet_rsk(req)->ir_mark;
fl6->fl6_dport = inet_rsk(req)->rmt_port;
fl6->fl6_sport = inet_rsk(req)->loc_port;
- fl6->flowi6_uid = sock_i_uid(sk);
+ fl6->flowi6_uid = sk->sk_uid;
security_req_classify_flow(req, flowi6_to_flowi(fl6));
dst = ip6_dst_lookup_flow(sk, fl6, final_p, false);
@@ -214,7 +214,7 @@
fl6->flowi6_mark = sk->sk_mark;
fl6->fl6_sport = inet->inet_sport;
fl6->fl6_dport = inet->inet_dport;
- fl6->flowi6_uid = sock_i_uid(sk);
+ fl6->flowi6_uid = sk->sk_uid;
security_sk_classify_flow(sk, flowi6_to_flowi(fl6));
rcu_read_lock();
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 6391c7d..3a9420e 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -799,6 +799,8 @@
if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)
fl6.flowi6_mark = skb->mark;
+ fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
+
err = ip6gre_xmit2(skb, dev, dsfield, &fl6, encap_limit, &mtu);
if (err != 0) {
/* XXX: send ICMP error even if DF is not set. */
@@ -849,6 +851,8 @@
if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)
fl6.flowi6_mark = skb->mark;
+ fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
+
err = ip6gre_xmit2(skb, dev, dsfield, &fl6, encap_limit, &mtu);
if (err != 0) {
if (err == -EMSGSIZE)
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 14f46af..cb30f13 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -1066,6 +1066,8 @@
memcpy(&fl6, &t->fl.u.ip6, sizeof (fl6));
fl6.flowi6_proto = IPPROTO_IPIP;
+ fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
+
dsfield = ipv4_get_dsfield(iph);
if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)
@@ -1117,6 +1119,7 @@
memcpy(&fl6, &t->fl.u.ip6, sizeof (fl6));
fl6.flowi6_proto = IPPROTO_IPV6;
+ fl6.flowi6_uid = sock_net_uid(dev_net(dev), NULL);
dsfield = ipv6_get_dsfield(ipv6h);
if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)
diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c
index a1beb59..a4a4e1c 100644
--- a/net/ipv6/ipcomp6.c
+++ b/net/ipv6/ipcomp6.c
@@ -76,9 +76,9 @@
return;
if (type == NDISC_REDIRECT)
- ip6_redirect(skb, net, 0, 0);
+ ip6_redirect(skb, net, 0, 0, sock_net_uid(net, NULL));
else
- ip6_update_pmtu(skb, net, info, 0, 0, INVALID_UID);
+ ip6_update_pmtu(skb, net, info, 0, 0, sock_net_uid(net, NULL));
xfrm_state_put(x);
}
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 3939f77..ddff922 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -1286,6 +1286,8 @@
ri->prefix_len == 0)
continue;
#endif
+ if (ri->prefix_len < in6_dev->cnf.accept_ra_rt_info_min_plen)
+ continue;
if (ri->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)
continue;
rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,
diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c
index d38e6a8..e48d26b 100644
--- a/net/ipv6/netfilter.c
+++ b/net/ipv6/netfilter.c
@@ -27,6 +27,7 @@
struct flowi6 fl6 = {
.flowi6_oif = skb->sk ? skb->sk->sk_bound_dev_if : 0,
.flowi6_mark = skb->mark,
+ .flowi6_uid = sock_net_uid(net, skb->sk),
.daddr = iph->daddr,
.saddr = iph->saddr,
};
diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c
index 27de137..6a38935 100644
--- a/net/ipv6/ping.c
+++ b/net/ipv6/ping.c
@@ -167,7 +167,7 @@
fl6.daddr = *daddr;
fl6.flowi6_oif = oif;
fl6.flowi6_mark = sk->sk_mark;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
fl6.fl6_icmp_type = user_icmph.icmp6_type;
fl6.fl6_icmp_code = user_icmph.icmp6_code;
security_sk_classify_flow(sk, flowi6_to_flowi(&fl6));
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index 5693cf2..c9ccdd7 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -760,7 +760,7 @@
memset(&fl6, 0, sizeof(fl6));
fl6.flowi6_mark = sk->sk_mark;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
if (sin6) {
if (addr_len < SIN6_LEN_RFC2133)
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 0bc8046..51fe515 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -1171,11 +1171,12 @@
void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
{
ip6_update_pmtu(skb, sock_net(sk), mtu,
- sk->sk_bound_dev_if, sk->sk_mark, sock_i_uid(sk));
+ sk->sk_bound_dev_if, sk->sk_mark, sk->sk_uid);
}
EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
-void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
+void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark,
+ kuid_t uid)
{
const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
struct dst_entry *dst;
@@ -1188,6 +1189,7 @@
fl6.daddr = iph->daddr;
fl6.saddr = iph->saddr;
fl6.flowlabel = ip6_flowinfo(iph);
+ fl6.flowi6_uid = uid;
dst = ip6_route_output(net, NULL, &fl6);
if (!dst->error)
@@ -1198,7 +1200,8 @@
void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
{
- ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
+ ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark,
+ sk->sk_uid);
}
EXPORT_SYMBOL_GPL(ip6_sk_redirect);
@@ -2635,6 +2638,9 @@
if (tb[RTA_OIF])
oif = nla_get_u32(tb[RTA_OIF]);
+ if (tb[RTA_MARK])
+ fl6.flowi6_mark = nla_get_u32(tb[RTA_MARK]);
+
if (tb[RTA_UID])
fl6.flowi6_uid = make_kuid(current_user_ns(),
nla_get_u32(tb[RTA_UID]));
diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c
index 701d065..0428544 100644
--- a/net/ipv6/syncookies.c
+++ b/net/ipv6/syncookies.c
@@ -243,7 +243,7 @@
fl6.flowi6_mark = ireq->ir_mark;
fl6.fl6_dport = inet_rsk(req)->rmt_port;
fl6.fl6_sport = inet_sk(sk)->inet_sport;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
security_req_classify_flow(req, flowi6_to_flowi(&fl6));
dst = ip6_dst_lookup_flow(sk, &fl6, final_p, false);
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 6a9e5db..3ba57fc8 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -253,7 +253,7 @@
fl6.flowi6_mark = sk->sk_mark;
fl6.fl6_dport = usin->sin6_port;
fl6.fl6_sport = inet->inet_sport;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
opt = rcu_dereference_protected(np->opt, sock_owned_by_user(sk));
final_p = fl6_update_dst(&fl6, opt, &final);
@@ -722,15 +722,15 @@
};
#endif
-static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win,
- u32 tsval, u32 tsecr,
+static void tcp_v6_send_response(struct sock *sk, struct sk_buff *skb, u32 seq,
+ u32 ack, u32 win, u32 tsval, u32 tsecr,
struct tcp_md5sig_key *key, int rst, u8 tclass)
{
const struct tcphdr *th = tcp_hdr(skb);
struct tcphdr *t1;
struct sk_buff *buff;
struct flowi6 fl6;
- struct net *net = dev_net(skb_dst(skb)->dev);
+ struct net *net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev);
struct sock *ctl_sk = net->ipv6.tcp_sk;
unsigned int tot_len = sizeof(struct tcphdr);
struct dst_entry *dst;
@@ -798,6 +798,7 @@
fl6.flowi6_mark = IP6_REPLY_MARK(net, skb->mark);
fl6.fl6_dport = t1->dest;
fl6.fl6_sport = t1->source;
+ fl6.flowi6_uid = sock_net_uid(net, sk && sk_fullsock(sk) ? sk : NULL);
security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
/* Pass a socket to ip6_dst_lookup either it is for RST
@@ -872,7 +873,7 @@
ack_seq = ntohl(th->seq) + th->syn + th->fin + skb->len -
(th->doff << 2);
- tcp_v6_send_response(skb, seq, ack_seq, 0, 0, 0, key, 1, 0);
+ tcp_v6_send_response(sk, skb, seq, ack_seq, 0, 0, 0, key, 1, 0);
#ifdef CONFIG_TCP_MD5SIG
release_sk1:
@@ -883,11 +884,12 @@
#endif
}
-static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack,
- u32 win, u32 tsval, u32 tsecr,
+static void tcp_v6_send_ack(struct sock *sk, struct sk_buff *skb,
+ u32 seq, u32 ack, u32 win, u32 tsval, u32 tsecr,
struct tcp_md5sig_key *key, u8 tclass)
{
- tcp_v6_send_response(skb, seq, ack, win, tsval, tsecr, key, 0, tclass);
+ tcp_v6_send_response(sk, skb, seq, ack, win, tsval, tsecr, key, 0,
+ tclass);
}
static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb)
@@ -895,7 +897,7 @@
struct inet_timewait_sock *tw = inet_twsk(sk);
struct tcp_timewait_sock *tcptw = tcp_twsk(sk);
- tcp_v6_send_ack(skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
+ tcp_v6_send_ack(sk, skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale,
tcp_time_stamp + tcptw->tw_ts_offset,
tcptw->tw_ts_recent, tcp_twsk_md5_key(tcptw),
@@ -907,7 +909,7 @@
static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
struct request_sock *req)
{
- tcp_v6_send_ack(skb, tcp_rsk(req)->snt_isn + 1, tcp_rsk(req)->rcv_isn + 1,
+ tcp_v6_send_ack(sk, skb, tcp_rsk(req)->snt_isn + 1, tcp_rsk(req)->rcv_isn + 1,
req->rcv_wnd, tcp_time_stamp, req->ts_recent,
tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr), 0);
}
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 1791e97..9fc2637 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -1150,7 +1150,7 @@
fl6.flowi6_oif = np->sticky_pktinfo.ipi6_ifindex;
fl6.flowi6_mark = sk->sk_mark;
- fl6.flowi6_uid = sock_i_uid(sk);
+ fl6.flowi6_uid = sk->sk_uid;
if (msg->msg_controllen) {
opt = &opt_space;
diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c
index 19705cb..e0d1c86 100644
--- a/net/l2tp/l2tp_ip6.c
+++ b/net/l2tp/l2tp_ip6.c
@@ -515,6 +515,7 @@
memset(&fl6, 0, sizeof(fl6));
fl6.flowi6_mark = sk->sk_mark;
+ fl6.flowi6_uid = sk->sk_uid;
if (lsa) {
if (addr_len < SIN6_LEN_RFC2133)
diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c
index 9ce6228..0642f32 100644
--- a/net/netfilter/xt_qtaguid.c
+++ b/net/netfilter/xt_qtaguid.c
@@ -320,7 +320,7 @@
st_entry->tag,
get_uid_from_tag(st_entry->tag));
rb_erase(&st_entry->sock_node, st_to_free_tree);
- sockfd_put(st_entry->socket);
+ sock_put(st_entry->sk);
kfree(st_entry);
}
}
@@ -1791,8 +1791,11 @@
}
#ifdef DDEBUG
-/* This function is not in xt_qtaguid_print.c because of locks visibility */
-static void prdebug_full_state(int indent_level, const char *fmt, ...)
+/*
+ * This function is not in xt_qtaguid_print.c because of locks visibility.
+ * The lock of sock_tag_list must be aquired before calling this function
+ */
+static void prdebug_full_state_locked(int indent_level, const char *fmt, ...)
{
va_list args;
char *fmt_buff;
@@ -1813,16 +1816,12 @@
kfree(buff);
va_end(args);
- spin_lock_bh(&sock_tag_list_lock);
prdebug_sock_tag_tree(indent_level, &sock_tag_tree);
- spin_unlock_bh(&sock_tag_list_lock);
- spin_lock_bh(&sock_tag_list_lock);
spin_lock_bh(&uid_tag_data_tree_lock);
prdebug_uid_tag_data_tree(indent_level, &uid_tag_data_tree);
prdebug_proc_qtu_data_tree(indent_level, &proc_qtu_data_tree);
spin_unlock_bh(&uid_tag_data_tree_lock);
- spin_unlock_bh(&sock_tag_list_lock);
spin_lock_bh(&iface_stat_list_lock);
prdebug_iface_stat_list(indent_level, &iface_stat_list);
@@ -1831,7 +1830,7 @@
pr_debug("qtaguid: %s(): }\n", __func__);
}
#else
-static void prdebug_full_state(int indent_level, const char *fmt, ...) {}
+static void prdebug_full_state_locked(int indent_level, const char *fmt, ...) {}
#endif
struct proc_ctrl_print_info {
@@ -1907,12 +1906,12 @@
{
struct sock_tag *sock_tag_entry = v;
uid_t uid;
- long f_count;
CT_DEBUG("qtaguid: proc ctrl pid=%u tgid=%u uid=%u\n",
current->pid, current->tgid, current_fsuid());
if (sock_tag_entry != SEQ_START_TOKEN) {
+ int sk_ref_count;
uid = get_uid_from_tag(sock_tag_entry->tag);
CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%u) "
"pid=%u\n",
@@ -1921,13 +1920,13 @@
uid,
sock_tag_entry->pid
);
- f_count = atomic_long_read(
- &sock_tag_entry->socket->file->f_count);
+ sk_ref_count = atomic_read(
+ &sock_tag_entry->sk->sk_refcnt);
seq_printf(m, "sock=%pK tag=0x%llx (uid=%u) pid=%u "
- "f_count=%lu\n",
+ "f_count=%d\n",
sock_tag_entry->sk,
sock_tag_entry->tag, uid,
- sock_tag_entry->pid, f_count);
+ sock_tag_entry->pid, sk_ref_count);
} else {
seq_printf(m, "events: sockets_tagged=%llu "
"sockets_untagged=%llu "
@@ -1954,8 +1953,11 @@
(u64)atomic64_read(&qtu_events.match_no_sk),
(u64)atomic64_read(&qtu_events.match_no_sk_file));
- /* Count the following as part of the last item_index */
- prdebug_full_state(0, "proc ctrl");
+ /* Count the following as part of the last item_index. No need
+ * to lock the sock_tag_list here since it is already locked when
+ * starting the seq_file operation
+ */
+ prdebug_full_state_locked(0, "proc ctrl");
}
return 0;
@@ -2215,8 +2217,8 @@
current_fsuid());
goto err;
}
- CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n",
- input, atomic_long_read(&el_socket->file->f_count),
+ CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->sk_refcnt=%d ->sk=%p\n",
+ input, atomic_read(&el_socket->sk->sk_refcnt),
el_socket->sk);
if (argc < 3) {
acct_tag = make_atag_from_value(0);
@@ -2257,16 +2259,9 @@
struct tag_ref *prev_tag_ref_entry;
CT_DEBUG("qtaguid: ctrl_tag(%s): retag for sk=%p "
- "st@%p ...->f_count=%ld\n",
+ "st@%p ...->sk_refcnt=%d\n",
input, el_socket->sk, sock_tag_entry,
- atomic_long_read(&el_socket->file->f_count));
- /*
- * This is a re-tagging, so release the sock_fd that was
- * locked at the time of the 1st tagging.
- * There is still the ref from this call's sockfd_lookup() so
- * it can be done within the spinlock.
- */
- sockfd_put(sock_tag_entry->socket);
+ atomic_read(&el_socket->sk->sk_refcnt));
prev_tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag,
&uid_tag_data_entry);
BUG_ON(IS_ERR_OR_NULL(prev_tag_ref_entry));
@@ -2286,8 +2281,12 @@
res = -ENOMEM;
goto err_tag_unref_put;
}
+ /*
+ * Hold the sk refcount here to make sure the sk pointer cannot
+ * be freed and reused
+ */
+ sock_hold(el_socket->sk);
sock_tag_entry->sk = el_socket->sk;
- sock_tag_entry->socket = el_socket;
sock_tag_entry->pid = current->tgid;
sock_tag_entry->tag = combine_atag_with_uid(acct_tag,
uid);
@@ -2315,10 +2314,11 @@
atomic64_inc(&qtu_events.sockets_tagged);
}
spin_unlock_bh(&sock_tag_list_lock);
- /* We keep the ref to the socket (file) until it is untagged */
- CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->f_count=%ld\n",
+ /* We keep the ref to the sk until it is untagged */
+ CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->sk_refcnt=%d\n",
input, sock_tag_entry,
- atomic_long_read(&el_socket->file->f_count));
+ atomic_read(&el_socket->sk->sk_refcnt));
+ sockfd_put(el_socket);
return 0;
err_tag_unref_put:
@@ -2326,8 +2326,8 @@
tag_ref_entry->num_sock_tags--;
free_tag_ref_from_utd_entry(tag_ref_entry, uid_tag_data_entry);
err_put:
- CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->f_count=%ld\n",
- input, atomic_long_read(&el_socket->file->f_count) - 1);
+ CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->sk_refcnt=%d\n",
+ input, atomic_read(&el_socket->sk->sk_refcnt) - 1);
/* Release the sock_fd that was grabbed by sockfd_lookup(). */
sockfd_put(el_socket);
return res;
@@ -2343,17 +2343,13 @@
int sock_fd = 0;
struct socket *el_socket;
int res, argc;
- struct sock_tag *sock_tag_entry;
- struct tag_ref *tag_ref_entry;
- struct uid_tag_data *utd_entry;
- struct proc_qtu_data *pqd_entry;
argc = sscanf(input, "%c %d", &cmd, &sock_fd);
CT_DEBUG("qtaguid: ctrl_untag(%s): argc=%d cmd=%c sock_fd=%d\n",
input, argc, cmd, sock_fd);
if (argc < 2) {
res = -EINVAL;
- goto err;
+ return res;
}
el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */
if (!el_socket) {
@@ -2361,17 +2357,31 @@
" sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n",
input, sock_fd, res, current->pid, current->tgid,
current_fsuid());
- goto err;
+ return res;
}
CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n",
input, atomic_long_read(&el_socket->file->f_count),
el_socket->sk);
+ res = qtaguid_untag(el_socket, false);
+ sockfd_put(el_socket);
+ return res;
+}
+
+int qtaguid_untag(struct socket *el_socket, bool kernel)
+{
+ int res;
+ pid_t pid;
+ struct sock_tag *sock_tag_entry;
+ struct tag_ref *tag_ref_entry;
+ struct uid_tag_data *utd_entry;
+ struct proc_qtu_data *pqd_entry;
+
spin_lock_bh(&sock_tag_list_lock);
sock_tag_entry = get_sock_stat_nl(el_socket->sk);
if (!sock_tag_entry) {
spin_unlock_bh(&sock_tag_list_lock);
res = -EINVAL;
- goto err_put;
+ return res;
}
/*
* The socket already belongs to the current process
@@ -2383,20 +2393,26 @@
BUG_ON(!tag_ref_entry);
BUG_ON(tag_ref_entry->num_sock_tags <= 0);
spin_lock_bh(&uid_tag_data_tree_lock);
+ if (kernel)
+ pid = sock_tag_entry->pid;
+ else
+ pid = current->tgid;
pqd_entry = proc_qtu_data_tree_search(
- &proc_qtu_data_tree, current->tgid);
+ &proc_qtu_data_tree, pid);
/*
* TODO: remove if, and start failing.
* At first, we want to catch user-space code that is not
* opening the /dev/xt_qtaguid.
*/
- if (IS_ERR_OR_NULL(pqd_entry))
+ if (IS_ERR_OR_NULL(pqd_entry) || !sock_tag_entry->list.next) {
pr_warn_once("qtaguid: %s(): "
"User space forgot to open /dev/xt_qtaguid? "
- "pid=%u tgid=%u uid=%u\n", __func__,
- current->pid, current->tgid, current_fsuid());
- else
+ "pid=%u tgid=%u sk_pid=%u, uid=%u\n", __func__,
+ current->pid, current->tgid, sock_tag_entry->pid,
+ from_kuid(&init_user_ns, current_fsuid()));
+ } else {
list_del(&sock_tag_entry->list);
+ }
spin_unlock_bh(&uid_tag_data_tree_lock);
/*
* We don't free tag_ref from the utd_entry here,
@@ -2405,30 +2421,17 @@
tag_ref_entry->num_sock_tags--;
spin_unlock_bh(&sock_tag_list_lock);
/*
- * Release the sock_fd that was grabbed at tag time,
- * and once more for the sockfd_lookup() here.
+ * Release the sock_fd that was grabbed at tag time.
*/
- sockfd_put(sock_tag_entry->socket);
- CT_DEBUG("qtaguid: ctrl_untag(%s): done. st@%p ...->f_count=%ld\n",
- input, sock_tag_entry,
- atomic_long_read(&el_socket->file->f_count) - 1);
- sockfd_put(el_socket);
+ sock_put(sock_tag_entry->sk);
+ CT_DEBUG("qtaguid: done. st@%p ...->sk_refcnt=%d\n",
+ sock_tag_entry,
+ atomic_read(&el_socket->sk->sk_refcnt));
kfree(sock_tag_entry);
atomic64_inc(&qtu_events.sockets_untagged);
return 0;
-
-err_put:
- CT_DEBUG("qtaguid: ctrl_untag(%s): done. socket->...->f_count=%ld\n",
- input, atomic_long_read(&el_socket->file->f_count) - 1);
- /* Release the sock_fd that was grabbed by sockfd_lookup(). */
- sockfd_put(el_socket);
- return res;
-
-err:
- CT_DEBUG("qtaguid: ctrl_untag(%s): done.\n", input);
- return res;
}
static int qtaguid_ctrl_parse(const char *input, int count)
@@ -2860,8 +2863,10 @@
sock_tag_tree_erase(&st_to_free_tree);
- prdebug_full_state(0, "%s(): pid=%u tgid=%u", __func__,
+ spin_lock_bh(&sock_tag_list_lock);
+ prdebug_full_state_locked(0, "%s(): pid=%u tgid=%u", __func__,
current->pid, current->tgid);
+ spin_unlock_bh(&sock_tag_list_lock);
return 0;
}
diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h
index 6dc14a9..8178fbd 100644
--- a/net/netfilter/xt_qtaguid_internal.h
+++ b/net/netfilter/xt_qtaguid_internal.h
@@ -256,8 +256,6 @@
struct sock_tag {
struct rb_node sock_node;
struct sock *sk; /* Only used as a number, never dereferenced */
- /* The socket is needed for sockfd_put() */
- struct socket *socket;
/* Used to associate with a given pid */
struct list_head list; /* in proc_qtu_data.sock_tag_list */
pid_t pid;
diff --git a/net/netfilter/xt_qtaguid_print.c b/net/netfilter/xt_qtaguid_print.c
index f6a00a3..2a7190d 100644
--- a/net/netfilter/xt_qtaguid_print.c
+++ b/net/netfilter/xt_qtaguid_print.c
@@ -24,7 +24,7 @@
#include <linux/rbtree.h>
#include <linux/slab.h>
#include <linux/spinlock_types.h>
-
+#include <net/sock.h>
#include "xt_qtaguid_internal.h"
#include "xt_qtaguid_print.h"
@@ -237,10 +237,10 @@
tag_str = pp_tag_t(&st->tag);
res = kasprintf(GFP_ATOMIC, "sock_tag@%p{"
"sock_node=rb_node{...}, "
- "sk=%p socket=%p (f_count=%lu), list=list_head{...}, "
+ "sk=%p (f_count=%d), list=list_head{...}, "
"pid=%u, tag=%s}",
- st, st->sk, st->socket, atomic_long_read(
- &st->socket->file->f_count),
+ st, st->sk, atomic_read(
+ &st->sk->sk_refcnt),
st->pid, tag_str);
_bug_on_err_or_null(res);
kfree(tag_str);
diff --git a/net/socket.c b/net/socket.c
index 59c9896..aea6b11 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -526,9 +526,23 @@
return used;
}
+int sockfs_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+ int err = simple_setattr(dentry, iattr);
+
+ if (!err && (iattr->ia_valid & ATTR_UID)) {
+ struct socket *sock = SOCKET_I(dentry->d_inode);
+
+ sock->sk->sk_uid = iattr->ia_uid;
+ }
+
+ return err;
+}
+
static const struct inode_operations sockfs_inode_ops = {
.getxattr = sockfs_getxattr,
.listxattr = sockfs_listxattr,
+ .setattr = sockfs_setattr,
};
/**
diff --git a/net/xfrm/xfrm_algo.c b/net/xfrm/xfrm_algo.c
index ab4ef72..daed579d 100644
--- a/net/xfrm/xfrm_algo.c
+++ b/net/xfrm/xfrm_algo.c
@@ -220,7 +220,7 @@
.uinfo = {
.auth = {
- .icv_truncbits = 96,
+ .icv_truncbits = 128,
.icv_fullbits = 256,
}
},
diff --git a/security/commoncap.c b/security/commoncap.c
index 0405522..0add393 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -283,6 +283,16 @@
new->cap_effective = *effective;
new->cap_inheritable = *inheritable;
new->cap_permitted = *permitted;
+
+ /*
+ * Mask off ambient bits that are no longer both permitted and
+ * inheritable.
+ */
+ new->cap_ambient = cap_intersect(new->cap_ambient,
+ cap_intersect(*permitted,
+ *inheritable));
+ if (WARN_ON(!cap_ambient_invariant_ok(new)))
+ return -EINVAL;
return 0;
}
@@ -363,6 +373,7 @@
/*
* pP' = (X & fP) | (pI & fI)
+ * The addition of pA' is handled later.
*/
new->cap_permitted.cap[i] =
(new->cap_bset.cap[i] & permitted) |
@@ -494,10 +505,13 @@
{
const struct cred *old = current_cred();
struct cred *new = bprm->cred;
- bool effective, has_cap = false;
+ bool effective, has_cap = false, is_setid;
int ret;
kuid_t root_uid;
+ if (WARN_ON(!cap_ambient_invariant_ok(old)))
+ return -EPERM;
+
effective = false;
ret = get_file_caps(bprm, &effective, &has_cap);
if (ret < 0)
@@ -542,8 +556,9 @@
*
* In addition, if NO_NEW_PRIVS, then ensure we get no new privs.
*/
- if ((!uid_eq(new->euid, old->uid) ||
- !gid_eq(new->egid, old->gid) ||
+ is_setid = !uid_eq(new->euid, old->uid) || !gid_eq(new->egid, old->gid);
+
+ if ((is_setid ||
!cap_issubset(new->cap_permitted, old->cap_permitted)) &&
bprm->unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
/* downgrade; they get no more than they had, and maybe less */
@@ -559,10 +574,28 @@
new->suid = new->fsuid = new->euid;
new->sgid = new->fsgid = new->egid;
+ /* File caps or setid cancels ambient. */
+ if (has_cap || is_setid)
+ cap_clear(new->cap_ambient);
+
+ /*
+ * Now that we've computed pA', update pP' to give:
+ * pP' = (X & fP) | (pI & fI) | pA'
+ */
+ new->cap_permitted = cap_combine(new->cap_permitted, new->cap_ambient);
+
+ /*
+ * Set pE' = (fE ? pP' : pA'). Because pA' is zero if fE is set,
+ * this is the same as pE' = (fE ? pP' : 0) | pA'.
+ */
if (effective)
new->cap_effective = new->cap_permitted;
else
- cap_clear(new->cap_effective);
+ new->cap_effective = new->cap_ambient;
+
+ if (WARN_ON(!cap_ambient_invariant_ok(new)))
+ return -EPERM;
+
bprm->cap_effective = effective;
/*
@@ -577,7 +610,7 @@
* Number 1 above might fail if you don't have a full bset, but I think
* that is interesting information to audit.
*/
- if (!cap_isclear(new->cap_effective)) {
+ if (!cap_issubset(new->cap_effective, new->cap_ambient)) {
if (!cap_issubset(CAP_FULL_SET, new->cap_effective) ||
!uid_eq(new->euid, root_uid) || !uid_eq(new->uid, root_uid) ||
issecure(SECURE_NOROOT)) {
@@ -588,6 +621,10 @@
}
new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
+
+ if (WARN_ON(!cap_ambient_invariant_ok(new)))
+ return -EPERM;
+
return 0;
}
@@ -609,7 +646,7 @@
if (!uid_eq(cred->uid, root_uid)) {
if (bprm->cap_effective)
return 1;
- if (!cap_isclear(cred->cap_permitted))
+ if (!cap_issubset(cred->cap_permitted, cred->cap_ambient))
return 1;
}
@@ -711,10 +748,18 @@
uid_eq(old->suid, root_uid)) &&
(!uid_eq(new->uid, root_uid) &&
!uid_eq(new->euid, root_uid) &&
- !uid_eq(new->suid, root_uid)) &&
- !issecure(SECURE_KEEP_CAPS)) {
- cap_clear(new->cap_permitted);
- cap_clear(new->cap_effective);
+ !uid_eq(new->suid, root_uid))) {
+ if (!issecure(SECURE_KEEP_CAPS)) {
+ cap_clear(new->cap_permitted);
+ cap_clear(new->cap_effective);
+ }
+
+ /*
+ * Pre-ambient programs expect setresuid to nonroot followed
+ * by exec to drop capabilities. We should make sure that
+ * this remains the case.
+ */
+ cap_clear(new->cap_ambient);
}
if (uid_eq(old->euid, root_uid) && !uid_eq(new->euid, root_uid))
cap_clear(new->cap_effective);
@@ -836,15 +881,20 @@
* Implement PR_CAPBSET_DROP. Attempt to remove the specified capability from
* the current task's bounding set. Returns 0 on success, -ve on error.
*/
-static long cap_prctl_drop(struct cred *new, unsigned long cap)
+static int cap_prctl_drop(unsigned long cap)
{
- if (!capable(CAP_SETPCAP))
+ struct cred *new;
+
+ if (!ns_capable(current_user_ns(), CAP_SETPCAP))
return -EPERM;
if (!cap_valid(cap))
return -EINVAL;
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
cap_lower(new->cap_bset, cap);
- return 0;
+ return commit_creds(new);
}
/**
@@ -862,26 +912,17 @@
int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5)
{
+ const struct cred *old = current_cred();
struct cred *new;
- long error = 0;
-
- new = prepare_creds();
- if (!new)
- return -ENOMEM;
switch (option) {
case PR_CAPBSET_READ:
- error = -EINVAL;
if (!cap_valid(arg2))
- goto error;
- error = !!cap_raised(new->cap_bset, arg2);
- goto no_change;
+ return -EINVAL;
+ return !!cap_raised(old->cap_bset, arg2);
case PR_CAPBSET_DROP:
- error = cap_prctl_drop(new, arg2);
- if (error < 0)
- goto error;
- goto changed;
+ return cap_prctl_drop(arg2);
/*
* The next four prctl's remain to assist with transitioning a
@@ -903,10 +944,9 @@
* capability-based-privilege environment.
*/
case PR_SET_SECUREBITS:
- error = -EPERM;
- if ((((new->securebits & SECURE_ALL_LOCKS) >> 1)
- & (new->securebits ^ arg2)) /*[1]*/
- || ((new->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/
+ if ((((old->securebits & SECURE_ALL_LOCKS) >> 1)
+ & (old->securebits ^ arg2)) /*[1]*/
+ || ((old->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/
|| (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/
|| (cap_capable(current_cred(),
current_cred()->user_ns, CAP_SETPCAP,
@@ -920,46 +960,76 @@
*/
)
/* cannot change a locked bit */
- goto error;
+ return -EPERM;
+
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
new->securebits = arg2;
- goto changed;
+ return commit_creds(new);
case PR_GET_SECUREBITS:
- error = new->securebits;
- goto no_change;
+ return old->securebits;
case PR_GET_KEEPCAPS:
- if (issecure(SECURE_KEEP_CAPS))
- error = 1;
- goto no_change;
+ return !!issecure(SECURE_KEEP_CAPS);
case PR_SET_KEEPCAPS:
- error = -EINVAL;
if (arg2 > 1) /* Note, we rely on arg2 being unsigned here */
- goto error;
- error = -EPERM;
+ return -EINVAL;
if (issecure(SECURE_KEEP_CAPS_LOCKED))
- goto error;
+ return -EPERM;
+
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
if (arg2)
new->securebits |= issecure_mask(SECURE_KEEP_CAPS);
else
new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
- goto changed;
+ return commit_creds(new);
+
+ case PR_CAP_AMBIENT:
+ if (arg2 == PR_CAP_AMBIENT_CLEAR_ALL) {
+ if (arg3 | arg4 | arg5)
+ return -EINVAL;
+
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+ cap_clear(new->cap_ambient);
+ return commit_creds(new);
+ }
+
+ if (((!cap_valid(arg3)) | arg4 | arg5))
+ return -EINVAL;
+
+ if (arg2 == PR_CAP_AMBIENT_IS_SET) {
+ return !!cap_raised(current_cred()->cap_ambient, arg3);
+ } else if (arg2 != PR_CAP_AMBIENT_RAISE &&
+ arg2 != PR_CAP_AMBIENT_LOWER) {
+ return -EINVAL;
+ } else {
+ if (arg2 == PR_CAP_AMBIENT_RAISE &&
+ (!cap_raised(current_cred()->cap_permitted, arg3) ||
+ !cap_raised(current_cred()->cap_inheritable,
+ arg3)))
+ return -EPERM;
+
+ new = prepare_creds();
+ if (!new)
+ return -ENOMEM;
+ if (arg2 == PR_CAP_AMBIENT_RAISE)
+ cap_raise(new->cap_ambient, arg3);
+ else
+ cap_lower(new->cap_ambient, arg3);
+ return commit_creds(new);
+ }
default:
/* No functionality available - continue with default */
- error = -ENOSYS;
- goto error;
+ return -ENOSYS;
}
-
- /* Functionality provided */
-changed:
- return commit_creds(new);
-
-no_change:
-error:
- abort_creds(new);
- return error;
}
/**
diff --git a/security/inode.c b/security/inode.c
index 43ce6e1..94466a5e 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -105,7 +105,7 @@
dir = parent->d_inode;
mutex_lock(&dir->i_mutex);
- dentry = lookup_one_len(name, parent, strlen(name));
+ dentry = lookup_one_len2(name, mount, parent, strlen(name));
if (IS_ERR(dentry))
goto out;
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index cd871dc..28380e5 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -847,6 +847,7 @@
new->cap_inheritable = old->cap_inheritable;
new->cap_permitted = old->cap_permitted;
new->cap_effective = old->cap_effective;
+ new->cap_ambient = old->cap_ambient;
new->cap_bset = old->cap_bset;
new->jit_keyring = old->jit_keyring;
diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c
index 08c70c6..c804e56 100755
--- a/sound/soc/codecs/max98925.c
+++ b/sound/soc/codecs/max98925.c
@@ -814,8 +814,9 @@
}
}
- pr_debug("%s: sample rate is %d, returning %d\n", __func__,
- rate_table[i].rate, *value);
+ if (!ret)
+ pr_debug("%s: sample rate is %d, returning %d\n", __func__,
+ rate_table[i].rate, *value);
return ret;
}
diff --git a/sound/soc/msm/msm-cpe-lsm.c b/sound/soc/msm/msm-cpe-lsm.c
index 85ec34a..b5520a9 100644
--- a/sound/soc/msm/msm-cpe-lsm.c
+++ b/sound/soc/msm/msm-cpe-lsm.c
@@ -1630,7 +1630,7 @@
event_status->payload_size;
memcpy(udata_32->payload,
event_status->payload,
- u_pld_size);
+ udata_32->payload_size);
}
}
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
index e2051a7..14c1fa3 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -3890,10 +3890,10 @@
int flag = 0, i = 0, rc = 0, idx = 0;
int be_index = 0, port_id, topo_id;
unsigned int session_id = 0;
- uint16_t op_FL_ip_FL_weight;
- uint16_t op_FL_ip_FR_weight;
- uint16_t op_FR_ip_FL_weight;
- uint16_t op_FR_ip_FR_weight;
+ uint16_t op_FL_ip_FL_weight = 0;
+ uint16_t op_FL_ip_FR_weight = 0;
+ uint16_t op_FR_ip_FL_weight = 0;
+ uint16_t op_FR_ip_FR_weight = 0;
flag = ucontrol->value.integer.value[0];
pr_debug("%s E flag %d\n", __func__, flag);