-
Notifications
You must be signed in to change notification settings - Fork 0
/
file.c
646 lines (577 loc) · 18.3 KB
/
file.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
/*
* Copyright (c) 1998-2020 Erez Zadok
* Copyright (c) 2009 Shrikar Archak
* Copyright (c) 2003-2020 Stony Brook University
* Copyright (c) 2003-2020 The Research Foundation of SUNY
* Copyright (c) 2020-2021 Barnim Dzwillo @ Strato AG
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "wrapfs.h"
/* if f_op->read/write are not avail, vfs_read/write will call
* f_op->read/write_iter instead.
*
* Note:
* - v3.16 converted all in-kernel .read/.write calls to
* .read_iter/.write_iter to stop calling ->read and ->write with
* kernel pointers under set_fs.
* - v4.1 removed also the new_sync_read/write mapper calls.
* - v4.14 unexported the vfs_read/write calls. (kernel_read/write are no
* substitute, since they take kernel pointers instead of user-pointers)
*/
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
static ssize_t wrapfs_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
int err;
struct file *lower_file;
struct dentry *dentry = file->f_path.dentry;
lower_file = wrapfs_lower_file(file);
err = vfs_read(lower_file, buf, count, ppos);
/* update our inode atime upon a successful lower read */
if (err >= 0)
fsstack_copy_attr_atime(d_inode(dentry),
file_inode(lower_file));
return err;
}
static ssize_t wrapfs_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
int err;
struct file *lower_file;
struct dentry *dentry = file->f_path.dentry;
lower_file = wrapfs_lower_file(file);
err = vfs_write(lower_file, buf, count, ppos);
/* update our inode times+sizes upon a successful lower write */
if (err >= 0) {
fsstack_copy_inode_size(d_inode(dentry),
file_inode(lower_file));
fsstack_copy_attr_times(d_inode(dentry),
file_inode(lower_file));
}
return err;
}
#endif
/* For ->iterate() the caller holds the file->f_inode lock.
* (see: Documentation/filesystems/locking)
*/
static int wrapfs_readdir(struct file *file, struct dir_context *ctx)
{
int err;
struct file *lower_file = NULL;
struct dentry *dentry = file->f_path.dentry;
pr_debug("wrapfs: readdir(%pD4)\n", file);
lower_file = wrapfs_lower_file(file);
err = iterate_dir(lower_file, ctx);
file->f_pos = lower_file->f_pos;
if (err >= 0) /* copy the atime */
fsstack_copy_attr_atime(d_inode(dentry),
file_inode(lower_file));
return err;
}
static long wrapfs_unlocked_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
long err = -ENOTTY;
struct file *lower_file;
lower_file = wrapfs_lower_file(file);
/* XXX: use vfs_ioctl if/when VFS exports it */
if (!lower_file || !lower_file->f_op)
goto out;
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)
fsstack_copy_attr_all(file_inode(file),
file_inode(lower_file));
out:
return err;
}
#ifdef CONFIG_COMPAT
static long wrapfs_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
long err = -ENOTTY;
struct file *lower_file;
lower_file = wrapfs_lower_file(file);
/* XXX: use vfs_ioctl if/when VFS exports it */
if (!lower_file || !lower_file->f_op)
goto out;
if (lower_file->f_op->compat_ioctl)
err = lower_file->f_op->compat_ioctl(lower_file, cmd, arg);
out:
return err;
}
#endif
static int wrapfs_mmap(struct file *file, struct vm_area_struct *vma)
{
int err = 0;
struct file *lower_file;
pr_debug("wrapfs: mmap(%pD4, 0x%lx)\n", file, vma->vm_flags);
lower_file = wrapfs_lower_file(file);
if (!lower_file->f_op->mmap)
return -ENODEV;
if (WARN_ON(file != vma->vm_file))
return -EIO;
vma->vm_file = get_file(lower_file);
err = lower_file->f_op->mmap(lower_file, vma);
if (err) {
/* Drop reference count from new vm_file value */
fput(lower_file);
} else {
/* Drop reference count from previous vm_file value */
fput(file);
}
file_accessed(file);
return err;
}
/* The caller of ->open() holds no inode lock.
* The caller also holds a reference on f->f_path.dentry & f->f_path.mnt
* (see: Documentation/filesystems/Locking)
*/
static int wrapfs_open(struct inode *inode, struct file *file)
{
int err = 0;
struct file *lower_file = NULL;
struct path lower_path;
pr_debug("wrapfs: open(%pD4, %s:%lu, 0%o)\n", file, inode ? inode->i_sb->s_id : "NULL", inode ? inode->i_ino : 0, file->f_flags);
/* don't open unhashed/deleted files */
if (d_unhashed(file->f_path.dentry)) {
err = -ENOENT;
goto out_err;
}
file->private_data =
kzalloc(sizeof(struct wrapfs_file_info), GFP_KERNEL);
if (!WRAPFS_F(file)) {
err = -ENOMEM;
goto out_err;
}
/* open lower object and link wrapfs's file struct to lower's
* (dentry_open()->do_dentry_open() will hold a reference on lower_path)
*/
pathcpy(&lower_path, &WRAPFS_D(file->f_path.dentry)->lower_path);
lower_file = dentry_open(&lower_path, file->f_flags, current_cred());
if (IS_ERR(lower_file)) {
err = PTR_ERR(lower_file);
lower_file = wrapfs_lower_file(file);
if (lower_file) {
wrapfs_set_lower_file(file, NULL);
fput(lower_file); /* fput calls dput for lower_dentry */
}
} else {
wrapfs_set_lower_file(file, lower_file);
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0)
file->f_mode |= FMODE_KABI_ITERATE;
#endif
}
if (err)
kfree(WRAPFS_F(file));
else
fsstack_copy_attr_all(inode, wrapfs_lower_inode(inode));
out_err:
return err;
}
static int wrapfs_flush(struct file *file, fl_owner_t id)
{
int err = 0;
struct file *lower_file = NULL;
lower_file = wrapfs_lower_file(file);
if (lower_file && lower_file->f_op && lower_file->f_op->flush) {
filemap_write_and_wait(file->f_mapping);
err = lower_file->f_op->flush(lower_file, id);
}
return err;
}
/* release all lower object references & free the file info structure */
static int wrapfs_file_release(struct inode *inode, struct file *file)
{
struct file *lower_file;
pr_debug("wrapfs: file_release(%pD4, %s:%lu)\n", file, inode ? inode->i_sb->s_id : "NULL", inode ? inode->i_ino : 0);
lower_file = wrapfs_lower_file(file);
if (lower_file) {
#if defined(WRAP_REMOTE_FILE_LOCKS)
struct inode *inode_lower = locks_inode(lower_file);
/* Avoid 'leftover lock' warnings from locks_remove_file() when
* a process does not unlock a posix lock.
*
* The file_lock list is located at the lower inode and fl_file
* points to lower_file (the lock request is copied in
* posix_lock_file() when it is inserted to the i_flock list).
*
* Unlock of posix locks is not triggered when an overlayfs is
* mounted on top, because filp_close() does not see the i_flock
* of the lower_file.
*
* So the unlock for the lower posix locks is triggerd here with
* the matching owner, when the last reference to the file is
* removed.
*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
if (inode_lower->i_flctx) {
#else
if (inode_lower->i_flock) {
#endif
int n = 0;
int o = 0;
struct file_lock *fl;
fl_owner_t owner = current->files; // default owner of non-OFD posix locks
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
struct file_lock_context *ctx = smp_load_acquire(&inode_lower->i_flctx);
if (ctx && !list_empty_careful(&ctx->flc_flock)) {
spin_lock(&ctx->flc_lock);
list_for_each_entry(fl, &ctx->flc_flock, fl_list) {
if (fl->fl_file == lower_file) {
owner = fl->fl_owner;
o++;
}
n++;
}
spin_unlock(&ctx->flc_lock);
}
if (o) {
/* see fs/locks.c:locks_remove_flock()
*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
struct file_lock fl;
locks_init_lock(&fl);
fl.fl_owner = owner;
fl.fl_pid = current->tgid;
fl.fl_file = lower_file;
fl.fl_flags = FL_FLOCK | FL_CLOSE;
fl.fl_type = F_UNLCK;
fl.fl_end = OFFSET_MAX;
#else
struct file_lock fl = {
.fl_owner = owner,
.fl_pid = current->tgid,
.fl_file = lower_file,
.fl_flags = FL_FLOCK | FL_CLOSE,
.fl_type = F_UNLCK,
.fl_end = OFFSET_MAX,
};
#endif
if (lower_file->f_op->flock) {
lower_file->f_op->flock(lower_file, F_SETLKW, &fl);
} else {
locks_lock_inode_wait(inode_lower, &fl);
}
if (fl.fl_ops && fl.fl_ops->fl_release_private) {
fl.fl_ops->fl_release_private(&fl);
}
pr_debug("wrapfs: file_release(%pD4) remove flock (%s:%lu) nlocks %d owned %d\n", file, inode_lower->i_sb->s_id, inode_lower->i_ino, n, o);
o = 0;
n = 0;
}
if (ctx && !list_empty_careful(&ctx->flc_posix)) {
spin_lock(&ctx->flc_lock);
list_for_each_entry(fl, &ctx->flc_posix, fl_list) {
if (fl->fl_file == lower_file) {
owner = fl->fl_owner;
o++;
}
n++;
}
spin_unlock(&ctx->flc_lock);
}
#else
spin_lock(&inode_lower->i_lock);
for (fl = inode_lower->i_flock; fl != NULL; fl = fl->fl_next) {
if (fl->fl_flags & FL_POSIX) {
if (fl->fl_file == lower_file) {
owner = fl->fl_owner;
o++;
}
}
n++;
}
spin_unlock(&inode_lower->i_lock);
#endif
if (o) {
locks_remove_posix(lower_file, owner);
pr_debug("wrapfs: file_release(%pD4) remove posix lock (%s:%lu) nlocks %d owned %d\n", file, inode_lower->i_sb->s_id, inode_lower->i_ino, n, o);
}
}
#endif
wrapfs_set_lower_file(file, NULL);
fput(lower_file);
}
kfree(WRAPFS_F(file));
return 0;
}
static int wrapfs_fsync(struct file *file, loff_t start, loff_t end,
int datasync)
{
int err;
struct file *lower_file;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
err = __generic_file_fsync(file, start, end, datasync);
#else
err = generic_file_fsync(file, start, end, datasync);
#endif
if (err)
goto out;
lower_file = wrapfs_lower_file(file);
err = vfs_fsync_range(lower_file, start, end, datasync);
out:
return err;
}
static int wrapfs_fasync(int fd, struct file *file, int flag)
{
int err = 0;
struct file *lower_file = NULL;
lower_file = wrapfs_lower_file(file);
if (lower_file->f_op && lower_file->f_op->fasync)
err = lower_file->f_op->fasync(fd, lower_file, flag);
return err;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
static ssize_t wrapfs_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{
int err;
struct file *file = iocb->ki_filp, *lower_file;
lower_file = wrapfs_lower_file(file);
if (!lower_file->f_op->read_iter) {
err = -EINVAL;
goto out;
}
get_file(lower_file); /* prevent lower_file from being released */
iocb->ki_filp = lower_file;
err = lower_file->f_op->read_iter(iocb, iter);
iocb->ki_filp = file;
fput(lower_file);
/* update upper inode atime as needed */
if (err >= 0 || err == -EIOCBQUEUED)
fsstack_copy_attr_atime(d_inode(file->f_path.dentry),
file_inode(lower_file));
out:
return err;
}
static ssize_t wrapfs_write_iter(struct kiocb *iocb, struct iov_iter *iter)
{
int err;
struct file *file = iocb->ki_filp, *lower_file;
lower_file = wrapfs_lower_file(file);
if (!lower_file->f_op->write_iter) {
err = -EINVAL;
goto out;
}
get_file(lower_file); /* prevent lower_file from being released */
iocb->ki_filp = lower_file;
err = lower_file->f_op->write_iter(iocb, iter);
iocb->ki_filp = file;
fput(lower_file);
/* update upper inode times/sizes as needed */
if (err >= 0 || err == -EIOCBQUEUED) {
fsstack_copy_inode_size(d_inode(file->f_path.dentry),
file_inode(lower_file));
fsstack_copy_attr_times(d_inode(file->f_path.dentry),
file_inode(lower_file));
}
out:
return err;
}
#else
static ssize_t wrapfs_aio_read(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
int err = -EINVAL;
struct file *file, *lower_file;
file = iocb->ki_filp;
lower_file = wrapfs_lower_file(file);
if (!lower_file->f_op->aio_read)
goto out;
/*
* It appears safe to rewrite this iocb, because in
* do_io_submit@fs/aio.c, iocb is a just copy from user.
*/
get_file(lower_file); /* prevent lower_file from being released */
iocb->ki_filp = lower_file;
err = lower_file->f_op->aio_read(iocb, iov, nr_segs, pos);
iocb->ki_filp = file;
fput(lower_file);
/* update upper inode atime as needed */
if (err >= 0 || err == -EIOCBQUEUED)
fsstack_copy_attr_atime(d_inode(file->f_path.dentry),
file_inode(lower_file));
out:
return err;
}
static ssize_t wrapfs_aio_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
int err = -EINVAL;
struct file *file, *lower_file;
file = iocb->ki_filp;
lower_file = wrapfs_lower_file(file);
if (!lower_file->f_op->aio_write)
goto out;
/*
* It appears safe to rewrite this iocb, because in
* do_io_submit@fs/aio.c, iocb is a just copy from user.
*/
get_file(lower_file); /* prevent lower_file from being released */
iocb->ki_filp = lower_file;
err = lower_file->f_op->aio_write(iocb, iov, nr_segs, pos);
iocb->ki_filp = file;
fput(lower_file);
/* update upper inode times/sizes as needed */
if (err >= 0 || err == -EIOCBQUEUED) {
fsstack_copy_inode_size(d_inode(file->f_path.dentry),
file_inode(lower_file));
fsstack_copy_attr_times(d_inode(file->f_path.dentry),
file_inode(lower_file));
}
out:
return err;
}
#endif
#if defined(WRAP_REMOTE_FILE_LOCKS)
/* wrapfs/overlayfs files will automatically get the correct locks
* since linux-4.19 (backported to rh8-4.18)
*/
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0)
static inline bool is_remote_lock(struct file *filp)
{
return likely(!(filp->f_path.dentry->d_sb->s_flags & MS_NOREMOTELOCK));
}
#endif
static int wrapfs_lock(struct file *filp, int cmd, struct file_lock *fl)
{
int err;
struct file *lower_file;
/* F_*LK cmds are defined in fcntl.h (F_GETLK=5, F_SETLK=6, F_SETLKW=7)
* F_*LCK types are defined in fcntl.h (F_RDLCK=0, F_WRLCK=1, F_UNLCK=2)
* FL_* flags are defined in linux/fs.h (FL_POSIX=1, FL_FLOCK=2, FL_SLEEP=128, ..)
*
* posix locks use 'fl->fl_owner == current->files' here.
*/
pr_debug("wrapfs: lock(%pD4, %d, t=0x%x, fl=0x%x, r=%lld:%lld)\n", filp, cmd, fl->fl_type, fl->fl_flags, (long long)fl->fl_start, (long long)fl->fl_end);
lower_file = wrapfs_lower_file(filp);
get_file(lower_file); /* prevent lower_file from being released */
fl->fl_file = lower_file;
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0)
if (lower_file->f_op && lower_file->f_op->lock && is_remote_lock(lower_file)) {
#else
if (lower_file->f_op && lower_file->f_op->lock) {
#endif
err = lower_file->f_op->lock(lower_file, cmd, fl);
} else {
err = posix_lock_file(lower_file, fl, NULL);
}
fl->fl_file = filp;
fput(lower_file);
return err;
}
static int wrapfs_flock(struct file *filp, int cmd, struct file_lock *fl)
{
int err;
struct file *lower_file;
/* F_*LK cmds are defined in fctnl.h ((flock_cmd & LOCK_NB) ? F_SETLK=6 : F_SETLKW=7)
* F_*LCK types are defined in fcntl.h (F_RDLCK=0 (flock_cmd:LOCK_SH), F_WRLCK=1 (flock_cmd:LOCK_EX), F_UNLCK=2 (flock_cmd:LOCK_UN))
* FL_* flags are defined in linux/fs.h (FL_POSIX=1, FL_FLOCK=2, FL_SLEEP=128, ..)
*
* flocks use 'fl->fl_owner == filp' here.
*/
pr_debug("wrapfs: flock(%pD4, %d, t=0x%x, fl=0x%x)\n", filp, cmd, fl->fl_type, fl->fl_flags);
lower_file = wrapfs_lower_file(filp);
get_file(lower_file); /* prevent lower_file from being released */
fl->fl_file = lower_file;
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0)
if (lower_file->f_op && lower_file->f_op->flock && is_remote_lock(lower_file)) {
#else
if (lower_file->f_op && lower_file->f_op->flock) {
#endif
err = lower_file->f_op->flock(lower_file, cmd, fl);
} else {
err = locks_lock_file_wait(lower_file, fl);
}
fl->fl_file = filp;
fput(lower_file);
return err;
}
#endif
/*
* For directories wrapfs cannot use generic_file_llseek as ->llseek,
* because it would only set the offset of the upper file. It is also
* necessary to call the llseek operation of the lower filesystem,
* because filesystems like nfs implement a differing logic from
* generic_file_llseek.
*
* For regular files generic_file_llseek is sufficient, because all
* the read()/write() calls are called with a file->f_pos parameter
* from the vfs-layer.
*
* note: It should be safe to acquire the inode mutex or just to use
* i_size_read() here. This does not protect the file->f_pos
* against concurrent modifications since this is something the
* userspace has to take care about.
* (see: Documentation/filesystems/Locking)
*/
static loff_t wrapfs_file_llseek(struct file *file, loff_t offset, int whence)
{
int err;
struct file *lower_file;
lower_file = wrapfs_lower_file(file);
inode_lock(d_inode(file->f_path.dentry));
err = vfs_llseek(lower_file, offset, whence);
file->f_pos = lower_file->f_pos;
inode_unlock(d_inode(file->f_path.dentry));
return err;
}
const struct file_operations wrapfs_main_fops = {
.llseek = generic_file_llseek,
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
.read = new_sync_read,
.write = new_sync_write,
#else
.read = wrapfs_read,
.write = wrapfs_write,
#endif
#endif
.unlocked_ioctl = wrapfs_unlocked_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = wrapfs_compat_ioctl,
#endif
.mmap = wrapfs_mmap,
.open = wrapfs_open,
.flush = wrapfs_flush,
.release = wrapfs_file_release,
.fsync = wrapfs_fsync,
.fasync = wrapfs_fasync,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
.read_iter = wrapfs_read_iter,
.write_iter = wrapfs_write_iter,
#else
.aio_read = wrapfs_aio_read,
.aio_write = wrapfs_aio_write,
#endif
#if defined(WRAP_REMOTE_FILE_LOCKS)
.lock = wrapfs_lock,
.flock = wrapfs_flock,
#endif
};
/* trimmed directory options
*
* note: for an underlying nfs it is required to map the directory file ops,
* because nfs_opendir(inode, file) uses the inode spinlock to protect
* a list_add() operation, and nfs_closedir(inode, file) uses the
* d_inode(file->f_path.dentry) spinlock to protect the list_del().
* (when the directory ops are not mapped, these are not the same objects)
*/
const struct file_operations wrapfs_dir_fops = {
.llseek = wrapfs_file_llseek,
.read = generic_read_dir,
.iterate = wrapfs_readdir,
.unlocked_ioctl = wrapfs_unlocked_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = wrapfs_compat_ioctl,
#endif
.open = wrapfs_open,
.release = wrapfs_file_release,
.flush = wrapfs_flush,
.fsync = wrapfs_fsync,
.fasync = wrapfs_fasync,
};