@@ -326,31 +326,37 @@ void ovl_cleanup_fops_htable(void)
struct ovl_fops *ofop;
hash_for_each_safe(ovl_fops_htable, bkt, tmp, ofop, entry) {
- module_put(ofop->owner);
fops_put(ofop->orig_fops);
+ module_put(ofop->owner);
kfree(ofop);
}
}
-#define OVL_CALL_REAL_FOP(file, call) \
- ({ struct ovl_fops *__ofop = \
- container_of(file->f_op, struct ovl_fops, fops); \
- WARN_ON(__ofop->magic != OVL_FOPS_MAGIC) ? -EIO : \
- __ofop->orig_fops->call; \
- })
-
static bool ovl_file_is_lower(struct file *file)
{
return !OVL_TYPE_UPPER(ovl_path_type(file->f_path.dentry));
}
+static const struct file_operations *ovl_orig_fops(struct file *file)
+{
+ struct ovl_fops *ofop = container_of(file->f_op, struct ovl_fops, fops);
+
+ if (WARN_ON(ofop->magic != OVL_FOPS_MAGIC))
+ return NULL;
+
+ return ofop->orig_fops;
+}
+
static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *to)
{
struct file *file = iocb->ki_filp;
ssize_t ret;
- if (likely(ovl_file_is_lower(file)))
- return OVL_CALL_REAL_FOP(file, read_iter(iocb, to));
+ if (likely(ovl_file_is_lower(file))) {
+ const struct file_operations *f_op = ovl_orig_fops(file);
+
+ return f_op ? f_op->read_iter(iocb, to) : -EIO;
+ }
file = filp_clone_open(file);
if (IS_ERR(file))
@@ -364,8 +370,11 @@ static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *to)
static int ovl_mmap(struct file *file, struct vm_area_struct *vma)
{
- if (likely(ovl_file_is_lower(file)))
- return OVL_CALL_REAL_FOP(file, mmap(file, vma));
+ if (likely(ovl_file_is_lower(file))) {
+ const struct file_operations *f_op = ovl_orig_fops(file);
+
+ return f_op ? f_op->mmap(file, vma) : -EIO;
+ }
file = filp_clone_open(file);
if (IS_ERR(file))
@@ -386,8 +395,9 @@ static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
int ret;
if (likely(ovl_file_is_lower(file))) {
- return OVL_CALL_REAL_FOP(file,
- fsync(file, start, end, datasync));
+ const struct file_operations *f_op = ovl_orig_fops(file);
+
+ return f_op ? f_op->fsync(file, start, end, datasync) : -EIO;
}
file = filp_clone_open(file);
if (IS_ERR(file))
@@ -437,6 +447,9 @@ static struct ovl_fops *ovl_fops_get(struct file *file)
ofop->magic = OVL_FOPS_MAGIC;
ofop->orig_fops = fops_get(orig);
+ /* By default don't intercept: */
+ ofop->fops = *orig;
+
/* Intercept these: */
if (orig->read_iter)
ofop->fops.read_iter = ovl_read_iter;
@@ -447,24 +460,39 @@ static struct ovl_fops *ovl_fops_get(struct file *file)
/*
* These should be intercepted, but they are very unlikely to be
- * a problem in practice. Leave them alone for now.
+ * a problem in practice. Leave them alone for now:
+ *
+ * - copy_file_range
+ * - clone_file_range
+ * - dedupe_file_range
+ *
+ * Don't intercept these:
+ *
+ * - llseek
+ * - unlocked_ioctl
+ * - compat_ioctl
+ * - flush
+ * - release
+ * - get_unmapped_area
+ * - check_flags
+ *
+ * These will never be called on R/O file descriptors:
+ *
+ * - write
+ * - write_iter
+ * - splice_write
+ * - sendpage
+ * - fallocate
+ *
+ * Locking operations are already intercepted by vfs for ovl:
+ *
+ * - lock
+ * - flock
+ * - setlease
*/
- ofop->fops.copy_file_range = orig->copy_file_range;
- ofop->fops.clone_file_range = orig->clone_file_range;
- ofop->fops.dedupe_file_range = orig->dedupe_file_range;
-
- /* Don't intercept these: */
- ofop->fops.llseek = orig->llseek;
- ofop->fops.unlocked_ioctl = orig->unlocked_ioctl;
- ofop->fops.compat_ioctl = orig->compat_ioctl;
- ofop->fops.flush = orig->flush;
- ofop->fops.release = orig->release;
- ofop->fops.get_unmapped_area = orig->get_unmapped_area;
- ofop->fops.check_flags = orig->check_flags;
/* splice_read should be generic_file_splice_read */
WARN_ON(orig->splice_read != generic_file_splice_read);
- ofop->fops.splice_read = generic_file_splice_read;
/* These make no sense for "normal" files: */
WARN_ON(orig->read);
@@ -474,22 +502,6 @@ static struct ovl_fops *ovl_fops_get(struct file *file)
WARN_ON(orig->fasync);
WARN_ON(orig->show_fdinfo);
- /*
- * Don't add those which are unneeded for O_RDONLY:
- *
- * write
- * write_iter
- * splice_write
- * sendpage
- * fallocate
- *
- * Locking operations are already intercepted by vfs for ovl:
- *
- * lock
- * flock
- * setlease
- */
-
hash_add_rcu(ovl_fops_htable, &ofop->entry, (long) orig);
out_unlock: