diff --git a/fs/fcntl.c b/fs/fcntl.c
index ef68665..eea0b65 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -21,6 +21,7 @@
 #include <linux/rcupdate.h>
 #include <linux/pid_namespace.h>
 #include <linux/user_namespace.h>
+#include <linux/shmem_fs.h>
 
 #include <asm/poll.h>
 #include <asm/siginfo.h>
@@ -248,9 +249,10 @@ static int f_getowner_uids(struct file *filp, unsigned long arg)
 #endif
 
 static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
-		struct file *filp)
+		     struct fd f)
 {
 	long err = -EINVAL;
+	struct file *filp = f.file;
 
 	switch (cmd) {
 	case F_DUPFD:
@@ -326,6 +328,10 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
 	case F_GETPIPE_SZ:
 		err = pipe_fcntl(filp, cmd, arg);
 		break;
+	case SHMEM_SET_SEALS:
+	case SHMEM_GET_SEALS:
+		err = shmem_fcntl(f, cmd, arg);
+		break;
 	default:
 		break;
 	}
@@ -360,7 +366,7 @@ SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
 
 	err = security_file_fcntl(f.file, cmd, arg);
 	if (!err)
-		err = do_fcntl(fd, cmd, arg, f.file);
+		err = do_fcntl(fd, cmd, arg, f);
 
 out1:
  	fdput(f);
@@ -397,7 +403,7 @@ SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd,
 					(struct flock64 __user *) arg);
 			break;
 		default:
-			err = do_fcntl(fd, cmd, arg, f.file);
+			err = do_fcntl(fd, cmd, arg, f);
 			break;
 	}
 out1:
diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h
index 9d55438..6a3f685 100644
--- a/include/linux/shmem_fs.h
+++ b/include/linux/shmem_fs.h
@@ -1,6 +1,7 @@
 #ifndef __SHMEM_FS_H
 #define __SHMEM_FS_H
 
+#include <linux/file.h>
 #include <linux/swap.h>
 #include <linux/mempolicy.h>
 #include <linux/pagemap.h>
@@ -20,6 +21,7 @@ struct shmem_inode_info {
 	struct shared_policy	policy;		/* NUMA memory alloc policy */
 	struct list_head	swaplist;	/* chain of maybes on swap */
 	struct simple_xattrs	xattrs;		/* list of xattrs */
+	u32			seals;		/* shmem seals */
 	struct inode		vfs_inode;
 };
 
@@ -57,6 +59,21 @@ extern struct page *shmem_read_mapping_page_gfp(struct address_space *mapping,
 extern void shmem_truncate_range(struct inode *inode, loff_t start, loff_t end);
 extern int shmem_unuse(swp_entry_t entry, struct page *page);
 
+#ifdef CONFIG_SHMEM
+
+extern int shmem_set_seals(struct file *file, u32 seals);
+extern int shmem_get_seals(struct file *file);
+extern long shmem_fcntl(struct fd f, unsigned int cmd, unsigned long arg);
+
+#else
+
+static inline long shmem_fcntl(struct fd f, unsigned int cmd, unsigned long arg)
+{
+	return -EINVAL;
+}
+
+#endif
+
 static inline struct page *shmem_read_mapping_page(
 				struct address_space *mapping, pgoff_t index)
 {
diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h
index 074b886..8f31bef 100644
--- a/include/uapi/linux/fcntl.h
+++ b/include/uapi/linux/fcntl.h
@@ -28,6 +28,19 @@
 #define F_GETPIPE_SZ	(F_LINUX_SPECIFIC_BASE + 8)
 
 /*
+ * Set/Get seals
+ */
+#define SHMEM_SET_SEALS	(F_LINUX_SPECIFIC_BASE + 9)
+#define SHMEM_GET_SEALS	(F_LINUX_SPECIFIC_BASE + 10)
+
+/*
+ * Types of seals
+ */
+#define SHMEM_SEAL_SHRINK	0x0001	/* prevent file from shrinking */
+#define SHMEM_SEAL_GROW		0x0002	/* prevent file from growing */
+#define SHMEM_SEAL_WRITE	0x0004	/* prevent writes */
+
+/*
  * Types of directory notifications that may be requested.
  */
 #define DN_ACCESS	0x00000001	/* File accessed */
diff --git a/mm/shmem.c b/mm/shmem.c
index 1f18c9d..44d7f3b 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -66,6 +66,7 @@ static struct vfsmount *shm_mnt;
 #include <linux/highmem.h>
 #include <linux/seq_file.h>
 #include <linux/magic.h>
+#include <linux/fcntl.h>
 
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
@@ -596,16 +597,23 @@ EXPORT_SYMBOL_GPL(shmem_truncate_range);
 static int shmem_setattr(struct dentry *dentry, struct iattr *attr)
 {
 	struct inode *inode = dentry->d_inode;
+	struct shmem_inode_info *info = SHMEM_I(inode);
+	loff_t oldsize = inode->i_size;
+	loff_t newsize = attr->ia_size;
 	int error;
 
 	error = inode_change_ok(inode, attr);
 	if (error)
 		return error;
 
-	if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) {
-		loff_t oldsize = inode->i_size;
-		loff_t newsize = attr->ia_size;
+	/* protected by i_mutex */
+	if (attr->ia_valid & ATTR_SIZE) {
+		if ((newsize < oldsize && (info->seals & SHMEM_SEAL_SHRINK)) ||
+		    (newsize > oldsize && (info->seals & SHMEM_SEAL_GROW)))
+			return -EPERM;
+	}
 
+	if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) {
 		if (newsize != oldsize) {
 			i_size_write(inode, newsize);
 			inode->i_ctime = inode->i_mtime = CURRENT_TIME;
@@ -1354,6 +1362,13 @@ out_nomem:
 
 static int shmem_mmap(struct file *file, struct vm_area_struct *vma)
 {
+	struct inode *inode = file_inode(file);
+	struct shmem_inode_info *info = SHMEM_I(inode);
+
+	/* protected by mmap_sem and owns additional file-reference */
+	if ((info->seals & SHMEM_SEAL_WRITE) && (vma->vm_flags & VM_SHARED))
+		return -EPERM;
+
 	file_accessed(file);
 	vma->vm_ops = &shmem_vm_ops;
 	return 0;
@@ -1433,7 +1448,15 @@ shmem_write_begin(struct file *file, struct address_space *mapping,
 			struct page **pagep, void **fsdata)
 {
 	struct inode *inode = mapping->host;
+	struct shmem_inode_info *info = SHMEM_I(inode);
 	pgoff_t index = pos >> PAGE_CACHE_SHIFT;
+
+	/* i_mutex is held by caller */
+	if (info->seals & SHMEM_SEAL_WRITE)
+		return -EPERM;
+	if ((info->seals & SHMEM_SEAL_GROW) && pos + len > inode->i_size)
+		return -EPERM;
+
 	return shmem_getpage(inode, index, pagep, SGP_WRITE, NULL);
 }
 
@@ -1802,11 +1825,171 @@ static loff_t shmem_file_llseek(struct file *file, loff_t offset, int whence)
 	return offset;
 }
 
+#define SHMEM_ALL_SEALS (SHMEM_SEAL_SHRINK | \
+			 SHMEM_SEAL_GROW | \
+			 SHMEM_SEAL_WRITE)
+
+int shmem_set_seals(struct file *file, u32 seals)
+{
+	struct dentry *dentry = file->f_path.dentry;
+	struct inode *inode = dentry->d_inode;
+	struct shmem_inode_info *info = SHMEM_I(inode);
+	bool has_writers, has_readers;
+	int r;
+
+	/*
+	 * SHMEM SEALING
+	 * Sealing allows multiple parties to share a shmem-file but restrict
+	 * access to a specific subset of file operations as long as more than
+	 * one party has access to the inode. This way, mutually untrusted
+	 * parties can share common memory regions with a well-defined policy.
+	 *
+	 * Seals can be set on any shmem-file, but always affect the whole
+	 * underlying inode. Once a seal is set, it may prevent some kinds of
+	 * access to the file. Currently, the following seals are defined:
+	 *   SHRINK: Prevent the file from shrinking
+	 *   GROW: Prevent the file from growing
+	 *   WRITE: Prevent write access to the file
+	 *
+	 * As we don't require any trust relationship between two parties, we
+	 * cannot allow asynchronous sealing. Instead, sealing is only allowed
+	 * if you own an exclusive reference to the shmem-file. Each FD, each
+	 * mmap and any link increase the ref-count. So as long as you have any
+	 * access to the file, you can be sure no-one (besides perhaps you) can
+	 * modify the seals.
+	 * There is one exception: Setting initial seals is allowed even if
+	 * there are multiple references to the file (but no writable mappings
+	 * may exist). Once *any* seal is set, removing or changing it requires
+	 * an exclusive reference, though.
+	 *
+	 * The combination of SHRINK and WRITE also guarantees that any mapped
+	 * region will not get destructed asynchronously. Even if at some point
+	 * revoke() is supported, the region will stay mapped (maybe only
+	 * privately) and accessible.
+	 */
+
+	if (file->f_op != &shmem_file_operations)
+		return -EBADF;
+
+	/* require write-access to modify seals */
+	if (!(file->f_mode & FMODE_WRITE))
+		return -EPERM;
+
+	if (seals & ~(u32)SHMEM_ALL_SEALS)
+		return -EINVAL;
+
+	/*
+	 * - i_mutex prevents racing write/ftruncate/fallocate/..
+	 * - mmap_sem prevents racing mmap() calls
+	 * - i_lock prevents racing open() calls and new inode-refs
+	 */
+
+	mutex_lock(&inode->i_mutex);
+	down_read(&current->mm->mmap_sem);
+	spin_lock(&inode->i_lock);
+
+	/*
+	 * Changing seals is only allowed on exclusive references. Exception is
+	 * initial sealing, which allows other readers. We need to test for
+	 * i_mmap_writable to prevent VM_SHARED vmas on our exclusive writer.
+	 * i_writecount is not checked, as we explicitly allow writable FDs
+	 * even if sealed. It's the write-operation that is blocked, not the
+	 * writable FD itself.
+	 * Readers are tested the same way F_SETLEASE does it. One dentry,
+	 * inode and file ref combination is allowed.
+	 * Note that we actually allow 2 file-refs: One is the ref in the
+	 * file-table, the other is from the current context.
+	 * Note: for racing dup() calls see GET_SEALS
+	 */
+	has_writers = file->f_mapping->i_mmap_writable > 0;
+
+	has_readers = d_count(dentry) > 1 || atomic_read(&inode->i_count) > 1;
+	has_readers = has_readers || file_count(file) > 2;
+
+	if (has_writers || (has_readers && info->seals != 0)) {
+		r = -EPERM;
+	} else {
+		info->seals = seals;
+		r = 0;
+	}
+
+	spin_unlock(&inode->i_lock);
+	up_read(&current->mm->mmap_sem);
+	mutex_unlock(&inode->i_mutex);
+
+	return r;
+}
+EXPORT_SYMBOL(shmem_set_seals);
+
+int shmem_get_seals(struct file *file)
+{
+	struct inode *inode = file_inode(file);
+	struct shmem_inode_info *info = SHMEM_I(inode);
+	unsigned long flags;
+	int r;
+
+	if (file->f_op != &shmem_file_operations)
+		return -EBADF;
+
+	/*
+	 * Lock i_lock so we don't read seals between file_count() and setting
+	 * the seals in SET_SEALS. Racing get_file()s could end up with an
+	 * inconsistent view.
+	 */
+
+	spin_lock_irqsave(&inode->i_lock, flags);
+	r = info->seals;
+	spin_unlock_irqrestore(&inode->i_lock, flags);
+
+	return r;
+}
+EXPORT_SYMBOL(shmem_get_seals);
+
+long shmem_fcntl(struct fd f, unsigned int cmd, unsigned long arg)
+{
+	long r;
+
+	if (f.file->f_op != &shmem_file_operations)
+		return -EBADF;
+
+	switch (cmd) {
+	case SHMEM_SET_SEALS:
+		/* disallow upper 32bit */
+		if (arg >> 32)
+			return -EINVAL;
+
+		/*
+		 * shmem_set_seals() allows 2 file-refs, one of the owner and
+		 * one of the current context. Make sure we have a real
+		 * owner-ref here, otherwise the fast-path of __fdget_light
+		 * breaks the assumptions in shmem_set_seals().
+		 */
+
+		if (!(f.flags & FDPUT_FPUT))
+			get_file(f.file);
+
+		r = shmem_set_seals(f.file, arg);
+
+		if (!(f.flags & FDPUT_FPUT))
+			fput(f.file);
+		break;
+	case SHMEM_GET_SEALS:
+		r = shmem_get_seals(f.file);
+		break;
+	default:
+		r = -EINVAL;
+		break;
+	}
+
+	return r;
+}
+
 static long shmem_fallocate(struct file *file, int mode, loff_t offset,
 							 loff_t len)
 {
 	struct inode *inode = file_inode(file);
 	struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
+	struct shmem_inode_info *info = SHMEM_I(inode);
 	struct shmem_falloc shmem_falloc;
 	pgoff_t start, index, end;
 	int error;
@@ -1818,6 +2001,12 @@ static long shmem_fallocate(struct file *file, int mode, loff_t offset,
 		loff_t unmap_start = round_up(offset, PAGE_SIZE);
 		loff_t unmap_end = round_down(offset + len, PAGE_SIZE) - 1;
 
+		/* protected by i_mutex */
+		if (info->seals & SHMEM_SEAL_WRITE) {
+			error = -EPERM;
+			goto out;
+		}
+
 		if ((u64)unmap_end > (u64)unmap_start)
 			unmap_mapping_range(mapping, unmap_start,
 					    1 + unmap_end - unmap_start, 0);
@@ -1832,6 +2021,11 @@ static long shmem_fallocate(struct file *file, int mode, loff_t offset,
 	if (error)
 		goto out;
 
+	if ((info->seals & SHMEM_SEAL_GROW) && offset + len > inode->i_size) {
+		error = -EPERM;
+		goto out;
+	}
+
 	start = offset >> PAGE_CACHE_SHIFT;
 	end = (offset + len + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
 	/* Try to avoid a swapstorm if len is impossible to satisfy */
