summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorBlagovest Kolenichev <bkolenichev@codeaurora.org>2017-05-31 16:59:54 -0700
committerBlagovest Kolenichev <bkolenichev@codeaurora.org>2017-06-07 09:31:32 -0700
commit2025064255a87606e2da561de77dcd68daf8b26d (patch)
tree4949f847e1660c18ee8faee4404c34178d95d7f2 /fs
parent25865f691b22d9b013cce032c06d3c0ed2485495 (diff)
parent9bc462220dab03c44f3f8fe0cf0d2d5f14fef7bd (diff)
Merge branch 'android-4.4@9bc4622' into branch 'msm-4.4'
* refs/heads/tmp-9bc4622: Linux 4.4.70 drivers: char: mem: Check for address space wraparound with mmap() nfsd: encoders mustn't use unitialized values in error cases drm/edid: Add 10 bpc quirk for LGD 764 panel in HP zBook 17 G2 PCI: Freeze PME scan before suspending devices PCI: Fix pci_mmap_fits() for HAVE_PCI_RESOURCE_TO_USER platforms tracing/kprobes: Enforce kprobes teardown after testing osf_wait4(): fix infoleak genirq: Fix chained interrupt data ordering uwb: fix device quirk on big-endian hosts metag/uaccess: Check access_ok in strncpy_from_user metag/uaccess: Fix access_ok() iommu/vt-d: Flush the IOTLB to get rid of the initial kdump mappings staging: rtl8192e: rtl92e_get_eeprom_size Fix read size of EPROM_CMD. staging: rtl8192e: fix 2 byte alignment of register BSSIDR. mm/huge_memory.c: respect FOLL_FORCE/FOLL_COW for thp xc2028: Fix use-after-free bug properly arm64: documentation: document tagged pointer stack constraints arm64: uaccess: ensure extension of access_ok() addr arm64: xchg: hazard against entire exchange variable ARM: dts: at91: sama5d3_xplained: not all ADC channels are available ARM: dts: at91: sama5d3_xplained: fix ADC vref powerpc/64e: Fix hang when debugging programs with relocated kernel powerpc/pseries: Fix of_node_put() underflow during DLPAR remove powerpc/book3s/mce: Move add_taint() later in virtual mode cx231xx-cards: fix NULL-deref at probe cx231xx-audio: fix NULL-deref at probe cx231xx-audio: fix init error path dvb-frontends/cxd2841er: define symbol_rate_min/max in T/C fe-ops zr364xx: enforce minimum size when reading header dib0700: fix NULL-deref at probe s5p-mfc: Fix unbalanced call to clock management gspca: konica: add missing endpoint sanity check ceph: fix recursion between ceph_set_acl() and __ceph_setattr() iio: proximity: as3935: fix as3935_write ipx: call ipxitf_put() in ioctl error path USB: hub: fix non-SS hub-descriptor handling USB: hub: fix SS hub-descriptor handling USB: serial: io_ti: fix div-by-zero in set_termios USB: serial: mct_u232: fix big-endian baud-rate handling USB: serial: qcserial: add more Lenovo EM74xx device IDs usb: serial: option: add Telit ME910 support USB: iowarrior: fix info ioctl on big-endian hosts usb: musb: tusb6010_omap: Do not reset the other direction's packet size ttusb2: limit messages to buffer size mceusb: fix NULL-deref at probe usbvision: fix NULL-deref at probe net: irda: irda-usb: fix firmware name on big-endian hosts usb: host: xhci-mem: allocate zeroed Scratchpad Buffer xhci: apply PME_STUCK_QUIRK and MISSING_CAS quirk for Denverton usb: host: xhci-plat: propagate return value of platform_get_irq() sched/fair: Initialize throttle_count for new task-groups lazily sched/fair: Do not announce throttled next buddy in dequeue_task_fair() fscrypt: avoid collisions when presenting long encrypted filenames f2fs: check entire encrypted bigname when finding a dentry fscrypt: fix context consistency check when key(s) unavailable net: qmi_wwan: Add SIMCom 7230E ext4 crypto: fix some error handling ext4 crypto: don't let data integrity writebacks fail with ENOMEM USB: serial: ftdi_sio: add Olimex ARM-USB-TINY(H) PIDs USB: serial: ftdi_sio: fix setting latency for unprivileged users pid_ns: Fix race between setns'ed fork() and zap_pid_ns_processes() pid_ns: Sleep in TASK_INTERRUPTIBLE in zap_pid_ns_processes iio: dac: ad7303: fix channel description of: fix sparse warning in of_pci_range_parser_one proc: Fix unbalanced hard link numbers cdc-acm: fix possible invalid access when processing notification drm/nouveau/tmr: handle races with hw when updating the next alarm time drm/nouveau/tmr: avoid processing completed alarms when adding a new one drm/nouveau/tmr: fix corruption of the pending list when rescheduling an alarm drm/nouveau/tmr: ack interrupt before processing alarms drm/nouveau/therm: remove ineffective workarounds for alarm bugs drm/amdgpu: Make display watermark calculations more accurate drm/amdgpu: Avoid overflows/divide-by-zero in latency_watermark calculations. ath9k_htc: fix NULL-deref at probe ath9k_htc: Add support of AirTies 1eda:2315 AR9271 device s390/cputime: fix incorrect system time s390/kdump: Add final note regulator: tps65023: Fix inverted core enable logic. KVM: X86: Fix read out-of-bounds vulnerability in kvm pio emulation KVM: x86: Fix load damaged SSEx MXCSR register ima: accept previously set IMA_NEW_FILE mwifiex: pcie: fix cmd_buf use-after-free in remove/reset rtlwifi: rtl8821ae: setup 8812ae RFE according to device type md: update slab_cache before releasing new stripes when stripes resizing dm space map disk: fix some book keeping in the disk space map dm thin metadata: call precommit before saving the roots dm bufio: make the parameter "retain_bytes" unsigned long dm cache metadata: fail operations if fail_io mode has been established dm bufio: check new buffer allocation watermark every 30 seconds dm bufio: avoid a possible ABBA deadlock dm raid: select the Kconfig option CONFIG_MD_RAID0 dm btree: fix for dm_btree_find_lowest_key() infiniband: call ipv6 route lookup via the stub interface tpm_crb: check for bad response size ARM: tegra: paz00: Mark panel regulator as enabled on boot USB: core: replace %p with %pK char: lp: fix possible integer overflow in lp_setup() watchdog: pcwd_usb: fix NULL-deref at probe USB: ene_usb6250: fix DMA to the stack usb: misc: legousbtower: Fix memory leak usb: misc: legousbtower: Fix buffers on stack ANDROID: uid_sys_stats: defer io stats calulation for dead tasks ANDROID: AVB: Fix linter errors. ANDROID: AVB: Fix invalidate_vbmeta_submit(). ANDROID: sdcardfs: Check for NULL in revalidate Linux 4.4.69 ipmi: Fix kernel panic at ipmi_ssif_thread() wlcore: Add RX_BA_WIN_SIZE_CHANGE_EVENT event wlcore: Pass win_size taken from ieee80211_sta to FW mac80211: RX BA support for sta max_rx_aggregation_subframes mac80211: pass block ack session timeout to to driver mac80211: pass RX aggregation window size to driver Bluetooth: hci_intel: add missing tty-device sanity check Bluetooth: hci_bcm: add missing tty-device sanity check Bluetooth: Fix user channel for 32bit userspace on 64bit kernel tty: pty: Fix ldisc flush after userspace become aware of the data already serial: omap: suspend device on probe errors serial: omap: fix runtime-pm handling on unbind serial: samsung: Use right device for DMA-mapping calls arm64: KVM: Fix decoding of Rt/Rt2 when trapping AArch32 CP accesses padata: free correct variable CIFS: add misssing SFM mapping for doublequote cifs: fix CIFS_IOC_GET_MNT_INFO oops CIFS: fix mapping of SFM_SPACE and SFM_PERIOD SMB3: Work around mount failure when using SMB3 dialect to Macs Set unicode flag on cifs echo request to avoid Mac error fs/block_dev: always invalidate cleancache in invalidate_bdev() ceph: fix memory leak in __ceph_setxattr() fs/xattr.c: zero out memory copied to userspace in getxattr ext4: evict inline data when writing to memory map IB/mlx4: Reduce SRIOV multicast cleanup warning message to debug level IB/mlx4: Fix ib device initialization error flow IB/IPoIB: ibX: failed to create mcg debug file IB/core: Fix sysfs registration error flow vfio/type1: Remove locked page accounting workqueue dm era: save spacemap metadata root after the pre-commit crypto: algif_aead - Require setkey before accept(2) block: fix blk_integrity_register to use template's interval_exp if not 0 KVM: arm/arm64: fix races in kvm_psci_vcpu_on KVM: x86: fix user triggerable warning in kvm_apic_accept_events() um: Fix PTRACE_POKEUSER on x86_64 x86, pmem: Fix cache flushing for iovec write < 8 bytes selftests/x86/ldt_gdt_32: Work around a glibc sigaction() bug x86/boot: Fix BSS corruption/overwrite bug in early x86 kernel startup usb: hub: Do not attempt to autosuspend disconnected devices usb: hub: Fix error loop seen after hub communication errors usb: Make sure usb/phy/of gets built-in usb: misc: add missing continue in switch staging: comedi: jr3_pci: cope with jiffies wraparound staging: comedi: jr3_pci: fix possible null pointer dereference staging: gdm724x: gdm_mux: fix use-after-free on module unload staging: vt6656: use off stack for out buffer USB transfers. staging: vt6656: use off stack for in buffer USB transfers. USB: Proper handling of Race Condition when two USB class drivers try to call init_usb_class simultaneously USB: serial: ftdi_sio: add device ID for Microsemi/Arrow SF2PLUS Dev Kit usb: host: xhci: print correct command ring address iscsi-target: Set session_fall_back_to_erl0 when forcing reinstatement target: Convert ACL change queue_depth se_session reference usage target/fileio: Fix zero-length READ and WRITE handling target: Fix compare_and_write_callback handling for non GOOD status xen: adjust early dom0 p2m handling to xen hypervisor behavior ANDROID: AVB: Only invalidate vbmeta when told to do so. ANDROID: sdcardfs: Move top to its own struct ANDROID: lowmemorykiller: account for unevictable pages ANDROID: usb: gadget: fix NULL pointer issue in mtp_read() ANDROID: usb: f_mtp: return error code if transfer error in receive_file_work function Signed-off-by: Blagovest Kolenichev <bkolenichev@codeaurora.org> Conflicts: drivers/usb/gadget/function/f_mtp.c fs/ext4/page-io.c net/mac80211/agg-rx.c Change-Id: Id65e75bf3bcee4114eb5d00730a9ef2444ad58eb Signed-off-by: Blagovest Kolenichev <bkolenichev@codeaurora.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/block_dev.c11
-rw-r--r--fs/ceph/acl.c4
-rw-r--r--fs/ceph/inode.c27
-rw-r--r--fs/ceph/super.h1
-rw-r--r--fs/ceph/xattr.c3
-rw-r--r--fs/cifs/cifs_unicode.c6
-rw-r--r--fs/cifs/cifs_unicode.h5
-rw-r--r--fs/cifs/cifssmb.c3
-rw-r--r--fs/cifs/ioctl.c2
-rw-r--r--fs/cifs/smb2pdu.c14
-rw-r--r--fs/ext4/crypto.c2
-rw-r--r--fs/ext4/crypto_fname.c2
-rw-r--r--fs/ext4/crypto_policy.c66
-rw-r--r--fs/ext4/inode.c5
-rw-r--r--fs/ext4/namei.c4
-rw-r--r--fs/ext4/page-io.c2
-rw-r--r--fs/f2fs/crypto_fname.c2
-rw-r--r--fs/f2fs/crypto_policy.c65
-rw-r--r--fs/f2fs/dir.c32
-rw-r--r--fs/f2fs/f2fs.h3
-rw-r--r--fs/f2fs/hash.c7
-rw-r--r--fs/f2fs/inline.c4
-rw-r--r--fs/nfsd/nfs4xdr.c8
-rw-r--r--fs/proc/generic.c1
-rw-r--r--fs/sdcardfs/dentry.c17
-rw-r--r--fs/sdcardfs/derived_perm.c130
-rw-r--r--fs/sdcardfs/inode.c53
-rw-r--r--fs/sdcardfs/lookup.c5
-rw-r--r--fs/sdcardfs/main.c8
-rw-r--r--fs/sdcardfs/packagelist.c2
-rw-r--r--fs/sdcardfs/sdcardfs.h114
-rw-r--r--fs/sdcardfs/super.c49
-rw-r--r--fs/xattr.c2
33 files changed, 435 insertions, 224 deletions
diff --git a/fs/block_dev.c b/fs/block_dev.c
index e5733bb537c9..26bbaaefdff4 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -88,12 +88,11 @@ void invalidate_bdev(struct block_device *bdev)
{
struct address_space *mapping = bdev->bd_inode->i_mapping;
- if (mapping->nrpages == 0)
- return;
-
- invalidate_bh_lrus();
- lru_add_drain_all(); /* make sure all lru add caches are flushed */
- invalidate_mapping_pages(mapping, 0, -1);
+ if (mapping->nrpages) {
+ invalidate_bh_lrus();
+ lru_add_drain_all(); /* make sure all lru add caches are flushed */
+ invalidate_mapping_pages(mapping, 0, -1);
+ }
/* 99% of the time, we don't need to flush the cleancache on the bdev.
* But, for the strange corners, lets be cautious
*/
diff --git a/fs/ceph/acl.c b/fs/ceph/acl.c
index 4d8caeb94a11..bdb9c94335f1 100644
--- a/fs/ceph/acl.c
+++ b/fs/ceph/acl.c
@@ -128,7 +128,7 @@ int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type)
if (new_mode != old_mode) {
newattrs.ia_mode = new_mode;
newattrs.ia_valid = ATTR_MODE;
- ret = ceph_setattr(dentry, &newattrs);
+ ret = __ceph_setattr(dentry, &newattrs);
if (ret)
goto out_dput;
}
@@ -138,7 +138,7 @@ int ceph_set_acl(struct inode *inode, struct posix_acl *acl, int type)
if (new_mode != old_mode) {
newattrs.ia_mode = old_mode;
newattrs.ia_valid = ATTR_MODE;
- ceph_setattr(dentry, &newattrs);
+ __ceph_setattr(dentry, &newattrs);
}
goto out_dput;
}
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index d98536c8abfc..9f0d99094cc1 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -1773,7 +1773,7 @@ static const struct inode_operations ceph_symlink_iops = {
/*
* setattr
*/
-int ceph_setattr(struct dentry *dentry, struct iattr *attr)
+int __ceph_setattr(struct dentry *dentry, struct iattr *attr)
{
struct inode *inode = d_inode(dentry);
struct ceph_inode_info *ci = ceph_inode(inode);
@@ -1975,11 +1975,6 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr)
if (inode_dirty_flags)
__mark_inode_dirty(inode, inode_dirty_flags);
- if (ia_valid & ATTR_MODE) {
- err = posix_acl_chmod(inode, attr->ia_mode);
- if (err)
- goto out_put;
- }
if (mask) {
req->r_inode = inode;
@@ -1993,13 +1988,23 @@ int ceph_setattr(struct dentry *dentry, struct iattr *attr)
ceph_cap_string(dirtied), mask);
ceph_mdsc_put_request(req);
- if (mask & CEPH_SETATTR_SIZE)
- __ceph_do_pending_vmtruncate(inode);
ceph_free_cap_flush(prealloc_cf);
+
+ if (err >= 0 && (mask & CEPH_SETATTR_SIZE))
+ __ceph_do_pending_vmtruncate(inode);
+
return err;
-out_put:
- ceph_mdsc_put_request(req);
- ceph_free_cap_flush(prealloc_cf);
+}
+
+int ceph_setattr(struct dentry *dentry, struct iattr *attr)
+{
+ int err;
+
+ err = __ceph_setattr(dentry, attr);
+
+ if (err >= 0 && (attr->ia_valid & ATTR_MODE))
+ err = posix_acl_chmod(d_inode(dentry), attr->ia_mode);
+
return err;
}
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index 75b7d125ce66..8c8cb8fe3d32 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -788,6 +788,7 @@ static inline int ceph_do_getattr(struct inode *inode, int mask, bool force)
return __ceph_do_getattr(inode, NULL, mask, force);
}
extern int ceph_permission(struct inode *inode, int mask);
+extern int __ceph_setattr(struct dentry *dentry, struct iattr *attr);
extern int ceph_setattr(struct dentry *dentry, struct iattr *attr);
extern int ceph_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat);
diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c
index 819163d8313b..b24275ef97f7 100644
--- a/fs/ceph/xattr.c
+++ b/fs/ceph/xattr.c
@@ -369,6 +369,7 @@ static int __set_xattr(struct ceph_inode_info *ci,
if (update_xattr) {
int err = 0;
+
if (xattr && (flags & XATTR_CREATE))
err = -EEXIST;
else if (!xattr && (flags & XATTR_REPLACE))
@@ -376,12 +377,14 @@ static int __set_xattr(struct ceph_inode_info *ci,
if (err) {
kfree(name);
kfree(val);
+ kfree(*newxattr);
return err;
}
if (update_xattr < 0) {
if (xattr)
__remove_xattr(ci, xattr);
kfree(name);
+ kfree(*newxattr);
return 0;
}
}
diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c
index 02b071bf3732..a0b3e7d1be48 100644
--- a/fs/cifs/cifs_unicode.c
+++ b/fs/cifs/cifs_unicode.c
@@ -83,6 +83,9 @@ convert_sfm_char(const __u16 src_char, char *target)
case SFM_COLON:
*target = ':';
break;
+ case SFM_DOUBLEQUOTE:
+ *target = '"';
+ break;
case SFM_ASTERISK:
*target = '*';
break;
@@ -418,6 +421,9 @@ static __le16 convert_to_sfm_char(char src_char, bool end_of_string)
case ':':
dest_char = cpu_to_le16(SFM_COLON);
break;
+ case '"':
+ dest_char = cpu_to_le16(SFM_DOUBLEQUOTE);
+ break;
case '*':
dest_char = cpu_to_le16(SFM_ASTERISK);
break;
diff --git a/fs/cifs/cifs_unicode.h b/fs/cifs/cifs_unicode.h
index 479bc0a941f3..07ade707fa60 100644
--- a/fs/cifs/cifs_unicode.h
+++ b/fs/cifs/cifs_unicode.h
@@ -57,6 +57,7 @@
* not conflict (although almost does) with the mapping above.
*/
+#define SFM_DOUBLEQUOTE ((__u16) 0xF020)
#define SFM_ASTERISK ((__u16) 0xF021)
#define SFM_QUESTION ((__u16) 0xF025)
#define SFM_COLON ((__u16) 0xF022)
@@ -64,8 +65,8 @@
#define SFM_LESSTHAN ((__u16) 0xF023)
#define SFM_PIPE ((__u16) 0xF027)
#define SFM_SLASH ((__u16) 0xF026)
-#define SFM_PERIOD ((__u16) 0xF028)
-#define SFM_SPACE ((__u16) 0xF029)
+#define SFM_SPACE ((__u16) 0xF028)
+#define SFM_PERIOD ((__u16) 0xF029)
/*
* Mapping mechanism to use when one of the seven reserved characters is
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 5e2f8b8ca08a..b60150e5b5ce 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -717,6 +717,9 @@ CIFSSMBEcho(struct TCP_Server_Info *server)
if (rc)
return rc;
+ if (server->capabilities & CAP_UNICODE)
+ smb->hdr.Flags2 |= SMBFLG2_UNICODE;
+
/* set up echo request */
smb->hdr.Tid = 0xffff;
smb->hdr.WordCount = 1;
diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c
index 35cf990f87d3..a8f5b31636dc 100644
--- a/fs/cifs/ioctl.c
+++ b/fs/cifs/ioctl.c
@@ -272,6 +272,8 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
rc = -EOPNOTSUPP;
break;
case CIFS_IOC_GET_MNT_INFO:
+ if (pSMBFile == NULL)
+ break;
tcon = tlink_tcon(pSMBFile->tlink);
rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg);
break;
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 6cb2603f8a5c..f4afa3b1cc56 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -564,8 +564,12 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
}
if (rsplen != sizeof(struct validate_negotiate_info_rsp)) {
- cifs_dbg(VFS, "invalid size of protocol negotiate response\n");
- return -EIO;
+ cifs_dbg(VFS, "invalid protocol negotiate response size: %d\n",
+ rsplen);
+
+ /* relax check since Mac returns max bufsize allowed on ioctl */
+ if (rsplen > CIFSMaxBufSize)
+ return -EIO;
}
/* check validate negotiate info response matches what we got earlier */
@@ -1518,8 +1522,12 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
* than one credit. Windows typically sets this smaller, but for some
* ioctls it may be useful to allow server to send more. No point
* limiting what the server can send as long as fits in one credit
+ * Unfortunately - we can not handle more than CIFS_MAX_MSG_SIZE
+ * (by default, note that it can be overridden to make max larger)
+ * in responses (except for read responses which can be bigger.
+ * We may want to bump this limit up
*/
- req->MaxOutputResponse = cpu_to_le32(0xFF00); /* < 64K uses 1 credit */
+ req->MaxOutputResponse = cpu_to_le32(CIFSMaxBufSize);
if (is_fsctl)
req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL);
diff --git a/fs/ext4/crypto.c b/fs/ext4/crypto.c
index e14b1b8fceb0..b9f838af5a72 100644
--- a/fs/ext4/crypto.c
+++ b/fs/ext4/crypto.c
@@ -271,7 +271,7 @@ static int ext4_page_crypto(struct inode *inode,
struct crypto_ablkcipher *tfm = ci->ci_ctfm;
int res = 0;
- req = ablkcipher_request_alloc(tfm, GFP_NOFS);
+ req = ablkcipher_request_alloc(tfm, gfp_flags);
if (!req) {
printk_ratelimited(KERN_ERR
"%s: crypto_request_alloc() failed\n",
diff --git a/fs/ext4/crypto_fname.c b/fs/ext4/crypto_fname.c
index e2645ca9b95e..026716bdbbfc 100644
--- a/fs/ext4/crypto_fname.c
+++ b/fs/ext4/crypto_fname.c
@@ -344,7 +344,7 @@ int _ext4_fname_disk_to_usr(struct inode *inode,
memcpy(buf+4, &hinfo->minor_hash, 4);
} else
memset(buf, 0, 8);
- memcpy(buf + 8, iname->name + iname->len - 16, 16);
+ memcpy(buf + 8, iname->name + ((iname->len - 17) & ~15), 16);
oname->name[0] = '_';
ret = digest_encode(buf, 24, oname->name+1);
oname->len = ret + 1;
diff --git a/fs/ext4/crypto_policy.c b/fs/ext4/crypto_policy.c
index dd561f916f0b..e4f4fc4e56ab 100644
--- a/fs/ext4/crypto_policy.c
+++ b/fs/ext4/crypto_policy.c
@@ -148,26 +148,38 @@ int ext4_get_policy(struct inode *inode, struct ext4_encryption_policy *policy)
int ext4_is_child_context_consistent_with_parent(struct inode *parent,
struct inode *child)
{
- struct ext4_crypt_info *parent_ci, *child_ci;
+ const struct ext4_crypt_info *parent_ci, *child_ci;
+ struct ext4_encryption_context parent_ctx, child_ctx;
int res;
- if ((parent == NULL) || (child == NULL)) {
- pr_err("parent %p child %p\n", parent, child);
- WARN_ON(1); /* Should never happen */
- return 0;
- }
-
/* No restrictions on file types which are never encrypted */
if (!S_ISREG(child->i_mode) && !S_ISDIR(child->i_mode) &&
!S_ISLNK(child->i_mode))
return 1;
- /* no restrictions if the parent directory is not encrypted */
+ /* No restrictions if the parent directory is unencrypted */
if (!ext4_encrypted_inode(parent))
return 1;
- /* if the child directory is not encrypted, this is always a problem */
+
+ /* Encrypted directories must not contain unencrypted files */
if (!ext4_encrypted_inode(child))
return 0;
+
+ /*
+ * Both parent and child are encrypted, so verify they use the same
+ * encryption policy. Compare the fscrypt_info structs if the keys are
+ * available, otherwise retrieve and compare the fscrypt_contexts.
+ *
+ * Note that the fscrypt_context retrieval will be required frequently
+ * when accessing an encrypted directory tree without the key.
+ * Performance-wise this is not a big deal because we already don't
+ * really optimize for file access without the key (to the extent that
+ * such access is even possible), given that any attempted access
+ * already causes a fscrypt_context retrieval and keyring search.
+ *
+ * In any case, if an unexpected error occurs, fall back to "forbidden".
+ */
+
res = ext4_get_encryption_info(parent);
if (res)
return 0;
@@ -176,17 +188,35 @@ int ext4_is_child_context_consistent_with_parent(struct inode *parent,
return 0;
parent_ci = EXT4_I(parent)->i_crypt_info;
child_ci = EXT4_I(child)->i_crypt_info;
- if (!parent_ci && !child_ci)
- return 1;
- if (!parent_ci || !child_ci)
+ if (parent_ci && child_ci) {
+ return memcmp(parent_ci->ci_master_key, child_ci->ci_master_key,
+ EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
+ (parent_ci->ci_data_mode == child_ci->ci_data_mode) &&
+ (parent_ci->ci_filename_mode ==
+ child_ci->ci_filename_mode) &&
+ (parent_ci->ci_flags == child_ci->ci_flags);
+ }
+
+ res = ext4_xattr_get(parent, EXT4_XATTR_INDEX_ENCRYPTION,
+ EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
+ &parent_ctx, sizeof(parent_ctx));
+ if (res != sizeof(parent_ctx))
+ return 0;
+
+ res = ext4_xattr_get(child, EXT4_XATTR_INDEX_ENCRYPTION,
+ EXT4_XATTR_NAME_ENCRYPTION_CONTEXT,
+ &child_ctx, sizeof(child_ctx));
+ if (res != sizeof(child_ctx))
return 0;
- return (memcmp(parent_ci->ci_master_key,
- child_ci->ci_master_key,
- EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
- (parent_ci->ci_data_mode == child_ci->ci_data_mode) &&
- (parent_ci->ci_filename_mode == child_ci->ci_filename_mode) &&
- (parent_ci->ci_flags == child_ci->ci_flags));
+ return memcmp(parent_ctx.master_key_descriptor,
+ child_ctx.master_key_descriptor,
+ EXT4_KEY_DESCRIPTOR_SIZE) == 0 &&
+ (parent_ctx.contents_encryption_mode ==
+ child_ctx.contents_encryption_mode) &&
+ (parent_ctx.filenames_encryption_mode ==
+ child_ctx.filenames_encryption_mode) &&
+ (parent_ctx.flags == child_ctx.flags);
}
/**
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 4caa0c1f77d8..2892a799f6f8 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -5461,6 +5461,11 @@ int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
file_update_time(vma->vm_file);
down_read(&EXT4_I(inode)->i_mmap_sem);
+
+ ret = ext4_convert_inline_data(inode);
+ if (ret)
+ goto out_ret;
+
/* Delalloc case is easy... */
if (test_opt(inode->i_sb, DELALLOC) &&
!ext4_should_journal_data(inode) &&
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index b936da9d3d0c..8a196e9b0bf3 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -1243,9 +1243,9 @@ static inline int ext4_match(struct ext4_filename *fname,
if (unlikely(!name)) {
if (fname->usr_fname->name[0] == '_') {
int ret;
- if (de->name_len < 16)
+ if (de->name_len <= 32)
return 0;
- ret = memcmp(de->name + de->name_len - 16,
+ ret = memcmp(de->name + ((de->name_len - 17) & ~15),
fname->crypto_buf.name + 8, 16);
return (ret == 0) ? 1 : 0;
}
diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c
index 1a6a8ca4de3a..978141e8b800 100644
--- a/fs/ext4/page-io.c
+++ b/fs/ext4/page-io.c
@@ -497,7 +497,7 @@ int ext4_bio_write_page(struct ext4_io_submit *io,
if (IS_ERR(data_page)) {
ret = PTR_ERR(data_page);
- if (ret == ENOMEM && wbc->sync_mode == WB_SYNC_ALL) {
+ if (ret == -ENOMEM && wbc->sync_mode == WB_SYNC_ALL) {
if (io->io_bio) {
ext4_io_submit(io);
congestion_wait(BLK_RW_ASYNC, HZ/50);
diff --git a/fs/f2fs/crypto_fname.c b/fs/f2fs/crypto_fname.c
index ab377d496a39..38349ed5ea51 100644
--- a/fs/f2fs/crypto_fname.c
+++ b/fs/f2fs/crypto_fname.c
@@ -333,7 +333,7 @@ int f2fs_fname_disk_to_usr(struct inode *inode,
memset(buf + 4, 0, 4);
} else
memset(buf, 0, 8);
- memcpy(buf + 8, iname->name + iname->len - 16, 16);
+ memcpy(buf + 8, iname->name + ((iname->len - 17) & ~15), 16);
oname->name[0] = '_';
ret = digest_encode(buf, 24, oname->name + 1);
oname->len = ret + 1;
diff --git a/fs/f2fs/crypto_policy.c b/fs/f2fs/crypto_policy.c
index 5bbd1989d5e6..884f3f0fe29d 100644
--- a/fs/f2fs/crypto_policy.c
+++ b/fs/f2fs/crypto_policy.c
@@ -141,25 +141,38 @@ int f2fs_get_policy(struct inode *inode, struct f2fs_encryption_policy *policy)
int f2fs_is_child_context_consistent_with_parent(struct inode *parent,
struct inode *child)
{
- struct f2fs_crypt_info *parent_ci, *child_ci;
+ const struct f2fs_crypt_info *parent_ci, *child_ci;
+ struct f2fs_encryption_context parent_ctx, child_ctx;
int res;
- if ((parent == NULL) || (child == NULL)) {
- pr_err("parent %p child %p\n", parent, child);
- BUG_ON(1);
- }
-
/* No restrictions on file types which are never encrypted */
if (!S_ISREG(child->i_mode) && !S_ISDIR(child->i_mode) &&
!S_ISLNK(child->i_mode))
return 1;
- /* no restrictions if the parent directory is not encrypted */
+ /* No restrictions if the parent directory is unencrypted */
if (!f2fs_encrypted_inode(parent))
return 1;
- /* if the child directory is not encrypted, this is always a problem */
+
+ /* Encrypted directories must not contain unencrypted files */
if (!f2fs_encrypted_inode(child))
return 0;
+
+ /*
+ * Both parent and child are encrypted, so verify they use the same
+ * encryption policy. Compare the fscrypt_info structs if the keys are
+ * available, otherwise retrieve and compare the fscrypt_contexts.
+ *
+ * Note that the fscrypt_context retrieval will be required frequently
+ * when accessing an encrypted directory tree without the key.
+ * Performance-wise this is not a big deal because we already don't
+ * really optimize for file access without the key (to the extent that
+ * such access is even possible), given that any attempted access
+ * already causes a fscrypt_context retrieval and keyring search.
+ *
+ * In any case, if an unexpected error occurs, fall back to "forbidden".
+ */
+
res = f2fs_get_encryption_info(parent);
if (res)
return 0;
@@ -168,17 +181,35 @@ int f2fs_is_child_context_consistent_with_parent(struct inode *parent,
return 0;
parent_ci = F2FS_I(parent)->i_crypt_info;
child_ci = F2FS_I(child)->i_crypt_info;
- if (!parent_ci && !child_ci)
- return 1;
- if (!parent_ci || !child_ci)
+ if (parent_ci && child_ci) {
+ return memcmp(parent_ci->ci_master_key, child_ci->ci_master_key,
+ F2FS_KEY_DESCRIPTOR_SIZE) == 0 &&
+ (parent_ci->ci_data_mode == child_ci->ci_data_mode) &&
+ (parent_ci->ci_filename_mode ==
+ child_ci->ci_filename_mode) &&
+ (parent_ci->ci_flags == child_ci->ci_flags);
+ }
+
+ res = f2fs_getxattr(parent, F2FS_XATTR_INDEX_ENCRYPTION,
+ F2FS_XATTR_NAME_ENCRYPTION_CONTEXT,
+ &parent_ctx, sizeof(parent_ctx), NULL);
+ if (res != sizeof(parent_ctx))
+ return 0;
+
+ res = f2fs_getxattr(child, F2FS_XATTR_INDEX_ENCRYPTION,
+ F2FS_XATTR_NAME_ENCRYPTION_CONTEXT,
+ &child_ctx, sizeof(child_ctx), NULL);
+ if (res != sizeof(child_ctx))
return 0;
- return (memcmp(parent_ci->ci_master_key,
- child_ci->ci_master_key,
- F2FS_KEY_DESCRIPTOR_SIZE) == 0 &&
- (parent_ci->ci_data_mode == child_ci->ci_data_mode) &&
- (parent_ci->ci_filename_mode == child_ci->ci_filename_mode) &&
- (parent_ci->ci_flags == child_ci->ci_flags));
+ return memcmp(parent_ctx.master_key_descriptor,
+ child_ctx.master_key_descriptor,
+ F2FS_KEY_DESCRIPTOR_SIZE) == 0 &&
+ (parent_ctx.contents_encryption_mode ==
+ child_ctx.contents_encryption_mode) &&
+ (parent_ctx.filenames_encryption_mode ==
+ child_ctx.filenames_encryption_mode) &&
+ (parent_ctx.flags == child_ctx.flags);
}
/**
diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
index 7c1678ba8f92..60972a559685 100644
--- a/fs/f2fs/dir.c
+++ b/fs/f2fs/dir.c
@@ -124,19 +124,29 @@ struct f2fs_dir_entry *find_target_dentry(struct f2fs_filename *fname,
de = &d->dentry[bit_pos];
- /* encrypted case */
+ if (de->hash_code != namehash)
+ goto not_match;
+
de_name.name = d->filename[bit_pos];
de_name.len = le16_to_cpu(de->name_len);
- /* show encrypted name */
- if (fname->hash) {
- if (de->hash_code == fname->hash)
- goto found;
- } else if (de_name.len == name->len &&
- de->hash_code == namehash &&
- !memcmp(de_name.name, name->name, name->len))
+#ifdef CONFIG_F2FS_FS_ENCRYPTION
+ if (unlikely(!name->name)) {
+ if (fname->usr_fname->name[0] == '_') {
+ if (de_name.len > 32 &&
+ !memcmp(de_name.name + ((de_name.len - 17) & ~15),
+ fname->crypto_buf.name + 8, 16))
+ goto found;
+ goto not_match;
+ }
+ name->name = fname->crypto_buf.name;
+ name->len = fname->crypto_buf.len;
+ }
+#endif
+ if (de_name.len == name->len &&
+ !memcmp(de_name.name, name->name, name->len))
goto found;
-
+not_match:
if (max_slots && max_len > *max_slots)
*max_slots = max_len;
max_len = 0;
@@ -170,7 +180,7 @@ static struct f2fs_dir_entry *find_in_level(struct inode *dir,
int max_slots;
f2fs_hash_t namehash;
- namehash = f2fs_dentry_hash(&name);
+ namehash = f2fs_dentry_hash(&name, fname);
f2fs_bug_on(F2FS_I_SB(dir), level > MAX_DIR_HASH_DEPTH);
@@ -547,7 +557,7 @@ int __f2fs_add_link(struct inode *dir, const struct qstr *name,
level = 0;
slots = GET_DENTRY_SLOTS(new_name.len);
- dentry_hash = f2fs_dentry_hash(&new_name);
+ dentry_hash = f2fs_dentry_hash(&new_name, NULL);
current_depth = F2FS_I(dir)->i_current_depth;
if (F2FS_I(dir)->chash == dentry_hash) {
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 3c7594b9d109..9dfbfe6dc775 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -1723,7 +1723,8 @@ void f2fs_msg(struct super_block *, const char *, const char *, ...);
/*
* hash.c
*/
-f2fs_hash_t f2fs_dentry_hash(const struct qstr *);
+f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info,
+ struct f2fs_filename *fname);
/*
* node.c
diff --git a/fs/f2fs/hash.c b/fs/f2fs/hash.c
index 71b7206c431e..b238d2fec3e5 100644
--- a/fs/f2fs/hash.c
+++ b/fs/f2fs/hash.c
@@ -70,7 +70,8 @@ static void str2hashbuf(const unsigned char *msg, size_t len,
*buf++ = pad;
}
-f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info)
+f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info,
+ struct f2fs_filename *fname)
{
__u32 hash;
f2fs_hash_t f2fs_hash;
@@ -79,6 +80,10 @@ f2fs_hash_t f2fs_dentry_hash(const struct qstr *name_info)
const unsigned char *name = name_info->name;
size_t len = name_info->len;
+ /* encrypted bigname case */
+ if (fname && !fname->disk_name.name)
+ return cpu_to_le32(fname->hash);
+
if (is_dot_dotdot(name_info))
return 0;
diff --git a/fs/f2fs/inline.c b/fs/f2fs/inline.c
index dbb2cc4df603..f35f3eb3541f 100644
--- a/fs/f2fs/inline.c
+++ b/fs/f2fs/inline.c
@@ -321,7 +321,7 @@ struct f2fs_dir_entry *find_in_inline_dir(struct inode *dir,
if (IS_ERR(ipage))
return NULL;
- namehash = f2fs_dentry_hash(&name);
+ namehash = f2fs_dentry_hash(&name, fname);
inline_dentry = inline_data_addr(ipage);
@@ -486,7 +486,7 @@ int f2fs_add_inline_entry(struct inode *dir, const struct qstr *name,
f2fs_wait_on_page_writeback(ipage, NODE);
- name_hash = f2fs_dentry_hash(name);
+ name_hash = f2fs_dentry_hash(name, NULL);
make_dentry_ptr(NULL, &d, (void *)dentry_blk, 2);
f2fs_update_dentry(ino, mode, &d, name, name_hash, bit_pos);
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 12935209deca..c3e1cb481fe0 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -4041,8 +4041,7 @@ nfsd4_encode_getdeviceinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
struct nfsd4_getdeviceinfo *gdev)
{
struct xdr_stream *xdr = &resp->xdr;
- const struct nfsd4_layout_ops *ops =
- nfsd4_layout_ops[gdev->gd_layout_type];
+ const struct nfsd4_layout_ops *ops;
u32 starting_len = xdr->buf->len, needed_len;
__be32 *p;
@@ -4059,6 +4058,7 @@ nfsd4_encode_getdeviceinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
/* If maxcount is 0 then just update notifications */
if (gdev->gd_maxcount != 0) {
+ ops = nfsd4_layout_ops[gdev->gd_layout_type];
nfserr = ops->encode_getdeviceinfo(xdr, gdev);
if (nfserr) {
/*
@@ -4111,8 +4111,7 @@ nfsd4_encode_layoutget(struct nfsd4_compoundres *resp, __be32 nfserr,
struct nfsd4_layoutget *lgp)
{
struct xdr_stream *xdr = &resp->xdr;
- const struct nfsd4_layout_ops *ops =
- nfsd4_layout_ops[lgp->lg_layout_type];
+ const struct nfsd4_layout_ops *ops;
__be32 *p;
dprintk("%s: err %d\n", __func__, nfserr);
@@ -4135,6 +4134,7 @@ nfsd4_encode_layoutget(struct nfsd4_compoundres *resp, __be32 nfserr,
*p++ = cpu_to_be32(lgp->lg_seg.iomode);
*p++ = cpu_to_be32(lgp->lg_layout_type);
+ ops = nfsd4_layout_ops[lgp->lg_layout_type];
nfserr = ops->encode_layoutget(xdr, lgp);
out:
kfree(lgp->lg_content);
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
index ff3ffc76a937..3773335791da 100644
--- a/fs/proc/generic.c
+++ b/fs/proc/generic.c
@@ -469,6 +469,7 @@ struct proc_dir_entry *proc_create_mount_point(const char *name)
ent->data = NULL;
ent->proc_fops = NULL;
ent->proc_iops = NULL;
+ parent->nlink++;
if (proc_register(parent, ent) < 0) {
kfree(ent);
parent->nlink--;
diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c
index 7a19e77fce99..13da7e5245bd 100644
--- a/fs/sdcardfs/dentry.c
+++ b/fs/sdcardfs/dentry.c
@@ -34,6 +34,8 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags)
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;
@@ -103,6 +105,21 @@ static int sdcardfs_d_revalidate(struct dentry *dentry, unsigned int flags)
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(d_inode(dentry));
+ 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);
diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c
index b4595aab5713..85a60fb5ff39 100644
--- a/fs/sdcardfs/derived_perm.c
+++ b/fs/sdcardfs/derived_perm.c
@@ -26,28 +26,28 @@ static void inherit_derived_state(struct inode *parent, struct inode *child)
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->under_cache = pi->under_cache;
- ci->under_obb = pi->under_obb;
- set_top(ci, pi->top);
+ 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,
- struct inode *top)
+ 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->under_cache = false;
- info->under_obb = false;
+ 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);
}
@@ -58,7 +58,8 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry,
const struct qstr *name)
{
struct sdcardfs_inode_info *info = SDCARDFS_I(d_inode(dentry));
- struct sdcardfs_inode_info *parent_info = SDCARDFS_I(d_inode(parent));
+ struct sdcardfs_inode_data *parent_data =
+ SDCARDFS_I(d_inode(parent))->data;
appid_t appid;
unsigned long user_num;
int err;
@@ -82,60 +83,61 @@ void get_derived_permission_new(struct dentry *parent, struct dentry *dentry,
if (!S_ISDIR(d_inode(dentry)->i_mode))
return;
/* Derive custom permissions based on parent and current node */
- switch (parent_info->perm) {
+ 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->perm = PERM_ROOT;
+ info->data->perm = PERM_ROOT;
err = kstrtoul(name->name, 10, &user_num);
if (err)
- info->userid = 0;
+ info->data->userid = 0;
else
- info->userid = user_num;
- set_top(info, &info->vfs_inode);
+ 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->perm = PERM_ANDROID;
- info->under_android = true;
- set_top(info, &info->vfs_inode);
+ 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->perm = PERM_ANDROID_DATA;
- set_top(info, &info->vfs_inode);
+ 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->perm = PERM_ANDROID_OBB;
- info->under_obb = true;
- set_top(info, &info->vfs_inode);
+ 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->perm = PERM_ANDROID_MEDIA;
- set_top(info, &info->vfs_inode);
+ 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->perm = PERM_ANDROID_PACKAGE;
+ info->data->perm = PERM_ANDROID_PACKAGE;
appid = get_appid(name->name);
- if (appid != 0 && !is_excluded(name->name, parent_info->userid))
- info->d_uid = multiuser_get_uid(parent_info->userid, appid);
- set_top(info, &info->vfs_inode);
+ 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->perm = PERM_ANDROID_PACKAGE_CACHE;
- info->under_cache = true;
+ info->data->perm = PERM_ANDROID_PACKAGE_CACHE;
+ info->data->under_cache = true;
}
break;
}
@@ -166,7 +168,8 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name)
struct inode *delegated_inode = NULL;
int error;
struct sdcardfs_inode_info *info;
- struct sdcardfs_inode_info *info_top;
+ 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;
@@ -174,15 +177,16 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name)
struct iattr newattrs;
info = SDCARDFS_I(d_inode(dentry));
- perm = info->perm;
- if (info->under_obb) {
+ info_d = info->data;
+ perm = info_d->perm;
+ if (info_d->under_obb) {
perm = PERM_ANDROID_OBB;
- } else if (info->under_cache) {
+ } else if (info_d->under_cache) {
perm = PERM_ANDROID_PACKAGE_CACHE;
} else if (perm == PERM_INHERIT) {
- info_top = SDCARDFS_I(grab_top(info));
+ info_top = top_data_get(info);
perm = info_top->perm;
- release_top(info);
+ data_put(info_top);
}
switch (perm) {
@@ -192,7 +196,7 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name)
case PERM_ANDROID_MEDIA:
case PERM_ANDROID_PACKAGE:
case PERM_ANDROID_PACKAGE_CACHE:
- uid = multiuser_get_uid(info->userid, uid);
+ uid = multiuser_get_uid(info_d->userid, uid);
break;
case PERM_ANDROID_OBB:
uid = AID_MEDIA_OBB;
@@ -207,24 +211,24 @@ void fixup_lower_ownership(struct dentry *dentry, const char *name)
case PERM_ANDROID_DATA:
case PERM_ANDROID_MEDIA:
if (S_ISDIR(d_inode(dentry)->i_mode))
- gid = multiuser_get_uid(info->userid, AID_MEDIA_RW);
+ gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW);
else
- gid = multiuser_get_uid(info->userid, get_type(name));
+ 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_uid))
- gid = multiuser_get_ext_gid(info->d_uid);
+ if (uid_is_app(info_d->d_uid))
+ gid = multiuser_get_ext_gid(info_d->d_uid);
else
- gid = multiuser_get_uid(info->userid, AID_MEDIA_RW);
+ gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW);
break;
case PERM_ANDROID_PACKAGE_CACHE:
- if (uid_is_app(info->d_uid))
- gid = multiuser_get_ext_cache_gid(info->d_uid);
+ if (uid_is_app(info_d->d_uid))
+ gid = multiuser_get_ext_cache_gid(info_d->d_uid);
else
- gid = multiuser_get_uid(info->userid, AID_MEDIA_RW);
+ gid = multiuser_get_uid(info_d->userid, AID_MEDIA_RW);
break;
case PERM_PRE_ROOT:
default:
@@ -257,11 +261,13 @@ retry_deleg:
sdcardfs_put_lower_path(dentry, &path);
}
-static int descendant_may_need_fixup(struct sdcardfs_inode_info *info, struct limit_search *limit)
+static int descendant_may_need_fixup(struct sdcardfs_inode_data *data,
+ struct limit_search *limit)
{
- if (info->perm == PERM_ROOT)
- return (limit->flags & BY_USERID)?info->userid == limit->userid:1;
- if (info->perm == PERM_PRE_ROOT || info->perm == PERM_ANDROID)
+ 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;
}
@@ -292,7 +298,7 @@ static void __fixup_perms_recursive(struct dentry *dentry, struct limit_search *
}
info = SDCARDFS_I(d_inode(dentry));
- if (needs_fixup(info->perm)) {
+ if (needs_fixup(info->data->perm)) {
list_for_each_entry(child, &dentry->d_subdirs, d_child) {
spin_lock_nested(&child->d_lock, depth + 1);
if (!(limit->flags & BY_NAME) || qstr_case_eq(&child->d_name, &limit->name)) {
@@ -305,7 +311,7 @@ static void __fixup_perms_recursive(struct dentry *dentry, struct limit_search *
}
spin_unlock(&child->d_lock);
}
- } else if (descendant_may_need_fixup(info, limit)) {
+ } else if (descendant_may_need_fixup(info->data, limit)) {
list_for_each_entry(child, &dentry->d_subdirs, d_child) {
__fixup_perms_recursive(child, limit, depth + 1);
}
@@ -349,12 +355,12 @@ int need_graft_path(struct dentry *dentry)
struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
struct qstr obb = QSTR_LITERAL("obb");
- if (parent_info->perm == PERM_ANDROID &&
+ 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)) {
+ && parent_info->data->userid == 0)) {
ret = 1;
}
}
@@ -415,11 +421,11 @@ int is_base_obbpath(struct dentry *dentry)
spin_lock(&SDCARDFS_D(dentry)->lock);
if (sbi->options.multiuser) {
- if (parent_info->perm == PERM_PRE_ROOT &&
+ 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 &&
+ } else if (parent_info->data->perm == PERM_ANDROID &&
qstr_case_eq(&dentry->d_name, &q_obb)) {
ret = 1;
}
diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c
index 4f09eebd7d95..60fea424835f 100644
--- a/fs/sdcardfs/inode.c
+++ b/fs/sdcardfs/inode.c
@@ -23,7 +23,8 @@
#include <linux/ratelimit.h>
/* Do not directly use this function. Use OVERRIDE_CRED() instead. */
-const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info)
+const struct cred *override_fsids(struct sdcardfs_sb_info *sbi,
+ struct sdcardfs_inode_data *data)
{
struct cred *cred;
const struct cred *old_cred;
@@ -33,10 +34,10 @@ const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_
if (!cred)
return NULL;
- if (info->under_obb)
+ if (data->under_obb)
uid = AID_MEDIA_OBB;
else
- uid = multiuser_get_uid(info->userid, sbi->options.fs_low_uid);
+ uid = multiuser_get_uid(data->userid, sbi->options.fs_low_uid);
cred->fsuid = make_kuid(&init_user_ns, uid);
cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid);
@@ -96,7 +97,8 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry,
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));
@@ -267,7 +269,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
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);
+ struct sdcardfs_inode_data *pd = SDCARDFS_I(dir)->data;
int touch_err = 0;
struct fs_struct *saved_fs;
struct fs_struct *copied_fs;
@@ -336,7 +338,7 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
make_nomedia_in_obb = 1;
}
- err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pi->userid);
+ err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pd->userid);
if (err) {
unlock_dir(lower_parent_dentry);
goto out;
@@ -349,12 +351,13 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode
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))
- && (pi->perm == PERM_ANDROID) && (pi->userid == 0))
+ && (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) && (qstr_case_eq(&dentry->d_name, &q_data)))) {
+ ((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(d_inode(dentry)));
set_fs_pwd(current->fs, &lower_path);
@@ -616,7 +619,7 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma
{
int err;
struct inode tmp;
- struct inode *top = grab_top(SDCARDFS_I(inode));
+ struct sdcardfs_inode_data *top = top_data_get(SDCARDFS_I(inode));
if (!top)
return -EINVAL;
@@ -633,10 +636,11 @@ static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int ma
* locks must be dealt with to avoid undefined behavior.
*/
copy_attrs(&tmp, inode);
- tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid);
- tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top)));
- tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top));
- release_top(SDCARDFS_I(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__);
@@ -687,11 +691,11 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct
struct iattr lower_ia;
struct dentry *parent;
struct inode tmp;
- struct inode *top;
+ struct sdcardfs_inode_data *top;
const struct cred *saved_cred = NULL;
inode = d_inode(dentry);
- top = grab_top(SDCARDFS_I(inode));
+ top = top_data_get(SDCARDFS_I(inode));
if (!top)
return -EINVAL;
@@ -709,11 +713,12 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct
*
*/
copy_attrs(&tmp, inode);
- tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid);
- tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top)));
- tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top));
+ 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);
- release_top(SDCARDFS_I(inode));
+ data_put(top);
tmp.i_sb = inode->i_sb;
/*
@@ -815,17 +820,17 @@ static int sdcardfs_fillattr(struct vfsmount *mnt,
struct inode *inode, struct kstat *stat)
{
struct sdcardfs_inode_info *info = SDCARDFS_I(inode);
- struct inode *top = grab_top(info);
+ 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, SDCARDFS_I(top));
+ stat->mode = (inode->i_mode & S_IFMT) | get_mode(mnt, info, top);
stat->nlink = inode->i_nlink;
- stat->uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid);
- stat->gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top)));
+ 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;
@@ -833,7 +838,7 @@ static int sdcardfs_fillattr(struct vfsmount *mnt,
stat->ctime = inode->i_ctime;
stat->blksize = (1 << inode->i_blkbits);
stat->blocks = inode->i_blocks;
- release_top(info);
+ data_put(top);
return 0;
}
diff --git a/fs/sdcardfs/lookup.c b/fs/sdcardfs/lookup.c
index 509d5fbcb472..00ae21151f52 100644
--- a/fs/sdcardfs/lookup.c
+++ b/fs/sdcardfs/lookup.c
@@ -71,7 +71,7 @@ struct inode_data {
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)
@@ -438,7 +438,8 @@ struct dentry *sdcardfs_lookup(struct inode *dir, struct dentry *dentry,
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)
diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c
index 953d2156d2e9..3c5b51d49d21 100644
--- a/fs/sdcardfs/main.c
+++ b/fs/sdcardfs/main.c
@@ -327,13 +327,13 @@ static int sdcardfs_read_super(struct vfsmount *mnt, struct super_block *sb,
mutex_lock(&sdcardfs_super_list_lock);
if (sb_info->options.multiuser) {
setup_derived_state(d_inode(sb->s_root), PERM_PRE_ROOT,
- sb_info->options.fs_user_id, AID_ROOT,
- false, d_inode(sb->s_root));
+ sb_info->options.fs_user_id, AID_ROOT,
+ false, SDCARDFS_I(d_inode(sb->s_root))->data);
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name);
} else {
setup_derived_state(d_inode(sb->s_root), PERM_ROOT,
- sb_info->options.fs_user_id, AID_ROOT,
- false, d_inode(sb->s_root));
+ sb_info->options.fs_user_id, AID_ROOT,
+ false, SDCARDFS_I(d_inode(sb->s_root))->data);
snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name);
}
fixup_tmp_permissions(d_inode(sb->s_root));
diff --git a/fs/sdcardfs/packagelist.c b/fs/sdcardfs/packagelist.c
index 89196e31073e..8495474258d5 100644
--- a/fs/sdcardfs/packagelist.c
+++ b/fs/sdcardfs/packagelist.c
@@ -156,7 +156,7 @@ int check_caller_access_to_name(struct inode *parent_node, const struct qstr *na
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 (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)) {
diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h
index 2b67b9a8ef9f..31d37d7da4f9 100644
--- a/fs/sdcardfs/sdcardfs.h
+++ b/fs/sdcardfs/sdcardfs.h
@@ -30,6 +30,7 @@
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/aio.h>
+#include <linux/kref.h>
#include <linux/mm.h>
#include <linux/mount.h>
#include <linux/namei.h>
@@ -81,7 +82,8 @@
*/
#define fixup_tmp_permissions(x) \
do { \
- (x)->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(x)->d_uid); \
+ (x)->i_uid = make_kuid(&init_user_ns, \
+ SDCARDFS_I(x)->data->d_uid); \
(x)->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW); \
(x)->i_mode = ((x)->i_mode & S_IFMT) | 0775;\
} while (0)
@@ -97,16 +99,16 @@
*/
#define OVERRIDE_CRED(sdcardfs_sbi, saved_cred, info) \
do { \
- saved_cred = override_fsids(sdcardfs_sbi, info); \
- if (!saved_cred) \
- return -ENOMEM; \
+ saved_cred = override_fsids(sdcardfs_sbi, info->data); \
+ if (!saved_cred) \
+ return -ENOMEM; \
} while (0)
#define OVERRIDE_CRED_PTR(sdcardfs_sbi, saved_cred, info) \
do { \
- saved_cred = override_fsids(sdcardfs_sbi, info); \
- if (!saved_cred) \
- return ERR_PTR(-ENOMEM); \
+ 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)
@@ -142,9 +144,11 @@ typedef enum {
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, struct sdcardfs_inode_info *info);
+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);
@@ -178,18 +182,26 @@ struct sdcardfs_file_info {
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 inode *top;
+ struct sdcardfs_inode_data *top_data;
struct inode vfs_inode;
};
@@ -351,39 +363,56 @@ SDCARDFS_DENT_FUNC(orig_path)
static inline bool sbinfo_has_sdcard_magic(struct sdcardfs_sb_info *sbinfo)
{
- return sbinfo && sbinfo->sb && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC;
+ return sbinfo && sbinfo->sb
+ && sbinfo->sb->s_magic == SDCARDFS_SUPER_MAGIC;
}
-/* grab a refererence if we aren't linking to ourself */
-static inline void set_top(struct sdcardfs_inode_info *info, struct inode *top)
+static inline struct sdcardfs_inode_data *data_get(
+ struct sdcardfs_inode_data *data)
{
- struct inode *old_top = NULL;
-
- BUG_ON(IS_ERR_OR_NULL(top));
- if (info->top && info->top != &info->vfs_inode)
- old_top = info->top;
- if (top != &info->vfs_inode)
- igrab(top);
- info->top = top;
- iput(old_top);
+ if (data)
+ kref_get(&data->refcount);
+ return data;
}
-static inline struct inode *grab_top(struct sdcardfs_inode_info *info)
+static inline struct sdcardfs_inode_data *top_data_get(
+ struct sdcardfs_inode_info *info)
{
- struct inode *top = info->top;
+ return data_get(info->top_data);
+}
- if (top)
- return igrab(top);
- else
- return NULL;
+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 release_top(struct sdcardfs_inode_info *info)
+static inline void set_top(struct sdcardfs_inode_info *info,
+ struct sdcardfs_inode_data *top)
{
- iput(info->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_info *info)
+static inline int get_gid(struct vfsmount *mnt,
+ struct sdcardfs_inode_data *data)
{
struct sdcardfs_vfsmount_options *opts = mnt->data;
@@ -396,10 +425,12 @@ static inline int get_gid(struct vfsmount *mnt, struct sdcardfs_inode_info *info
*/
return AID_SDCARD_RW;
else
- return multiuser_get_uid(info->userid, opts->gid);
+ return multiuser_get_uid(data->userid, opts->gid);
}
-static inline int get_mode(struct vfsmount *mnt, 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;
@@ -407,12 +438,12 @@ static inline int get_mode(struct vfsmount *mnt, struct sdcardfs_inode_info *inf
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.
*/
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.
@@ -481,8 +512,9 @@ struct limit_search {
userid_t userid;
};
-extern void setup_derived_state(struct inode *inode, perm_t perm, userid_t userid,
- uid_t uid, bool under_android, struct inode *top);
+extern void setup_derived_state(struct inode *inode, perm_t perm,
+ 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, const struct qstr *name);
extern void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit);
@@ -601,7 +633,7 @@ static inline void sdcardfs_copy_and_fix_attrs(struct inode *dest, const struct
{
dest->i_mode = (src->i_mode & S_IFMT) | S_IRWXU | S_IRWXG |
S_IROTH | S_IXOTH; /* 0775 */
- dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->d_uid);
+ dest->i_uid = make_kuid(&init_user_ns, SDCARDFS_I(dest)->data->d_uid);
dest->i_gid = make_kgid(&init_user_ns, AID_SDCARD_RW);
dest->i_rdev = src->i_rdev;
dest->i_atime = src->i_atime;
diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c
index 8a9c9c7adca2..7f4539b4b249 100644
--- 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)
{
@@ -166,6 +183,7 @@ static void sdcardfs_evict_inode(struct inode *inode)
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
@@ -173,13 +191,13 @@ static void sdcardfs_evict_inode(struct inode *inode)
*/
lower_inode = sdcardfs_lower_inode(inode);
sdcardfs_set_lower_inode(inode, NULL);
- set_top(SDCARDFS_I(inode), inode);
iput(lower_inode);
}
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)
@@ -188,6 +206,16 @@ static struct inode *sdcardfs_alloc_inode(struct super_block *sb)
/* 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;
}
@@ -196,6 +224,7 @@ 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));
}
@@ -214,20 +243,30 @@ static void init_once(void *obj)
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)
{
+ kmem_cache_destroy(sdcardfs_inode_data_cachep);
kmem_cache_destroy(sdcardfs_inode_cachep);
}
diff --git a/fs/xattr.c b/fs/xattr.c
index 9b932b95d74e..f0da9d24e9ca 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -442,7 +442,7 @@ getxattr(struct dentry *d, const char __user *name, void __user *value,
size = XATTR_SIZE_MAX;
kvalue = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
if (!kvalue) {
- vvalue = vmalloc(size);
+ vvalue = vzalloc(size);
if (!vvalue)
return -ENOMEM;
kvalue = vvalue;