]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blob - ubuntu/vbox/vboxsf/regops.c
UBUNTU: ubuntu: vbox -- Update to 5.1.14-dfsg-1
[mirror_ubuntu-zesty-kernel.git] / ubuntu / vbox / vboxsf / regops.c
1 /* $Id: regops.c $ */
2 /** @file
3 * vboxsf - VBox Linux Shared Folders, Regular file inode and file operations.
4 */
5
6 /*
7 * Copyright (C) 2006-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18 /*
19 * Limitations: only COW memory mapping is supported
20 */
21
22 #include "vfsmod.h"
23
24 static void *alloc_bounce_buffer(size_t *tmp_sizep, PRTCCPHYS physp, size_t
25 xfer_size, const char *caller)
26 {
27 size_t tmp_size;
28 void *tmp;
29
30 /* try for big first. */
31 tmp_size = RT_ALIGN_Z(xfer_size, PAGE_SIZE);
32 if (tmp_size > 16U*_1K)
33 tmp_size = 16U*_1K;
34 tmp = kmalloc(tmp_size, GFP_KERNEL);
35 if (!tmp)
36 {
37 /* fall back on a page sized buffer. */
38 tmp = kmalloc(PAGE_SIZE, GFP_KERNEL);
39 if (!tmp)
40 {
41 LogRel(("%s: could not allocate bounce buffer for xfer_size=%zu %s\n", caller, xfer_size));
42 return NULL;
43 }
44 tmp_size = PAGE_SIZE;
45 }
46
47 *tmp_sizep = tmp_size;
48 *physp = virt_to_phys(tmp);
49 return tmp;
50 }
51
52 static void free_bounce_buffer(void *tmp)
53 {
54 kfree (tmp);
55 }
56
57
58 /* fops */
59 static int sf_reg_read_aux(const char *caller, struct sf_glob_info *sf_g,
60 struct sf_reg_info *sf_r, void *buf,
61 uint32_t *nread, uint64_t pos)
62 {
63 /** @todo bird: yes, kmap() and kmalloc() input only. Since the buffer is
64 * contiguous in physical memory (kmalloc or single page), we should
65 * use a physical address here to speed things up. */
66 int rc = VbglR0SfRead(&client_handle, &sf_g->map, sf_r->handle,
67 pos, nread, buf, false /* already locked? */);
68 if (RT_FAILURE(rc))
69 {
70 LogFunc(("VbglR0SfRead failed. caller=%s, rc=%Rrc\n", caller, rc));
71 return -EPROTO;
72 }
73 return 0;
74 }
75
76 static int sf_reg_write_aux(const char *caller, struct sf_glob_info *sf_g,
77 struct sf_reg_info *sf_r, void *buf,
78 uint32_t *nwritten, uint64_t pos)
79 {
80 /** @todo bird: yes, kmap() and kmalloc() input only. Since the buffer is
81 * contiguous in physical memory (kmalloc or single page), we should
82 * use a physical address here to speed things up. */
83 int rc = VbglR0SfWrite(&client_handle, &sf_g->map, sf_r->handle,
84 pos, nwritten, buf, false /* already locked? */);
85 if (RT_FAILURE(rc))
86 {
87 LogFunc(("VbglR0SfWrite failed. caller=%s, rc=%Rrc\n",
88 caller, rc));
89 return -EPROTO;
90 }
91 return 0;
92 }
93
94 /**
95 * Read from a regular file.
96 *
97 * @param file the file
98 * @param buf the buffer
99 * @param size length of the buffer
100 * @param off offset within the file
101 * @returns the number of read bytes on success, Linux error code otherwise
102 */
103 static ssize_t sf_reg_read(struct file *file, char *buf, size_t size, loff_t *off)
104 {
105 int err;
106 void *tmp;
107 RTCCPHYS tmp_phys;
108 size_t tmp_size;
109 size_t left = size;
110 ssize_t total_bytes_read = 0;
111 struct inode *inode = GET_F_DENTRY(file)->d_inode;
112 struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
113 struct sf_reg_info *sf_r = file->private_data;
114 loff_t pos = *off;
115
116 TRACE();
117 if (!S_ISREG(inode->i_mode))
118 {
119 LogFunc(("read from non regular file %d\n", inode->i_mode));
120 return -EINVAL;
121 }
122
123 /** XXX Check read permission according to inode->i_mode! */
124
125 if (!size)
126 return 0;
127
128 tmp = alloc_bounce_buffer(&tmp_size, &tmp_phys, size, __PRETTY_FUNCTION__);
129 if (!tmp)
130 return -ENOMEM;
131
132 while (left)
133 {
134 uint32_t to_read, nread;
135
136 to_read = tmp_size;
137 if (to_read > left)
138 to_read = (uint32_t) left;
139
140 nread = to_read;
141
142 err = sf_reg_read_aux(__func__, sf_g, sf_r, tmp, &nread, pos);
143 if (err)
144 goto fail;
145
146 if (copy_to_user(buf, tmp, nread))
147 {
148 err = -EFAULT;
149 goto fail;
150 }
151
152 pos += nread;
153 left -= nread;
154 buf += nread;
155 total_bytes_read += nread;
156 if (nread != to_read)
157 break;
158 }
159
160 *off += total_bytes_read;
161 free_bounce_buffer(tmp);
162 return total_bytes_read;
163
164 fail:
165 free_bounce_buffer(tmp);
166 return err;
167 }
168
169 /**
170 * Write to a regular file.
171 *
172 * @param file the file
173 * @param buf the buffer
174 * @param size length of the buffer
175 * @param off offset within the file
176 * @returns the number of written bytes on success, Linux error code otherwise
177 */
178 static ssize_t sf_reg_write(struct file *file, const char *buf, size_t size, loff_t *off)
179 {
180 int err;
181 void *tmp;
182 RTCCPHYS tmp_phys;
183 size_t tmp_size;
184 size_t left = size;
185 ssize_t total_bytes_written = 0;
186 struct inode *inode = GET_F_DENTRY(file)->d_inode;
187 struct sf_inode_info *sf_i = GET_INODE_INFO(inode);
188 struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
189 struct sf_reg_info *sf_r = file->private_data;
190 loff_t pos;
191
192 TRACE();
193 BUG_ON(!sf_i);
194 BUG_ON(!sf_g);
195 BUG_ON(!sf_r);
196
197 if (!S_ISREG(inode->i_mode))
198 {
199 LogFunc(("write to non regular file %d\n", inode->i_mode));
200 return -EINVAL;
201 }
202
203 pos = *off;
204 if (file->f_flags & O_APPEND)
205 {
206 pos = inode->i_size;
207 *off = pos;
208 }
209
210 /** XXX Check write permission according to inode->i_mode! */
211
212 if (!size)
213 return 0;
214
215 tmp = alloc_bounce_buffer(&tmp_size, &tmp_phys, size, __PRETTY_FUNCTION__);
216 if (!tmp)
217 return -ENOMEM;
218
219 while (left)
220 {
221 uint32_t to_write, nwritten;
222
223 to_write = tmp_size;
224 if (to_write > left)
225 to_write = (uint32_t) left;
226
227 nwritten = to_write;
228
229 if (copy_from_user(tmp, buf, to_write))
230 {
231 err = -EFAULT;
232 goto fail;
233 }
234
235 #if 1
236 if (VbglR0CanUsePhysPageList())
237 {
238 err = VbglR0SfWritePhysCont(&client_handle, &sf_g->map, sf_r->handle,
239 pos, &nwritten, tmp_phys);
240 err = RT_FAILURE(err) ? -EPROTO : 0;
241 }
242 else
243 #endif
244 err = sf_reg_write_aux(__func__, sf_g, sf_r, tmp, &nwritten, pos);
245 if (err)
246 goto fail;
247
248 pos += nwritten;
249 left -= nwritten;
250 buf += nwritten;
251 total_bytes_written += nwritten;
252 if (nwritten != to_write)
253 break;
254 }
255
256 *off += total_bytes_written;
257 if (*off > inode->i_size)
258 inode->i_size = *off;
259
260 sf_i->force_restat = 1;
261 free_bounce_buffer(tmp);
262 return total_bytes_written;
263
264 fail:
265 free_bounce_buffer(tmp);
266 return err;
267 }
268
269 /**
270 * Open a regular file.
271 *
272 * @param inode the inode
273 * @param file the file
274 * @returns 0 on success, Linux error code otherwise
275 */
276 static int sf_reg_open(struct inode *inode, struct file *file)
277 {
278 int rc, rc_linux = 0;
279 struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
280 struct sf_inode_info *sf_i = GET_INODE_INFO(inode);
281 struct sf_reg_info *sf_r;
282 SHFLCREATEPARMS params;
283
284 TRACE();
285 BUG_ON(!sf_g);
286 BUG_ON(!sf_i);
287
288 LogFunc(("open %s\n", sf_i->path->String.utf8));
289
290 sf_r = kmalloc(sizeof(*sf_r), GFP_KERNEL);
291 if (!sf_r)
292 {
293 LogRelFunc(("could not allocate reg info\n"));
294 return -ENOMEM;
295 }
296
297 /* Already open? */
298 if (sf_i->handle != SHFL_HANDLE_NIL)
299 {
300 /*
301 * This inode was created with sf_create_aux(). Check the CreateFlags:
302 * O_CREAT, O_TRUNC: inherent true (file was just created). Not sure
303 * about the access flags (SHFL_CF_ACCESS_*).
304 */
305 sf_i->force_restat = 1;
306 sf_r->handle = sf_i->handle;
307 sf_i->handle = SHFL_HANDLE_NIL;
308 sf_i->file = file;
309 file->private_data = sf_r;
310 return 0;
311 }
312
313 RT_ZERO(params);
314 params.Handle = SHFL_HANDLE_NIL;
315 /* We check the value of params.Handle afterwards to find out if
316 * the call succeeded or failed, as the API does not seem to cleanly
317 * distinguish error and informational messages.
318 *
319 * Furthermore, we must set params.Handle to SHFL_HANDLE_NIL to
320 * make the shared folders host service use our fMode parameter */
321
322 if (file->f_flags & O_CREAT)
323 {
324 LogFunc(("O_CREAT set\n"));
325 params.CreateFlags |= SHFL_CF_ACT_CREATE_IF_NEW;
326 /* We ignore O_EXCL, as the Linux kernel seems to call create
327 beforehand itself, so O_EXCL should always fail. */
328 if (file->f_flags & O_TRUNC)
329 {
330 LogFunc(("O_TRUNC set\n"));
331 params.CreateFlags |= SHFL_CF_ACT_OVERWRITE_IF_EXISTS;
332 }
333 else
334 params.CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS;
335 }
336 else
337 {
338 params.CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW;
339 if (file->f_flags & O_TRUNC)
340 {
341 LogFunc(("O_TRUNC set\n"));
342 params.CreateFlags |= SHFL_CF_ACT_OVERWRITE_IF_EXISTS;
343 }
344 }
345
346 switch (file->f_flags & O_ACCMODE)
347 {
348 case O_RDONLY:
349 params.CreateFlags |= SHFL_CF_ACCESS_READ;
350 break;
351
352 case O_WRONLY:
353 params.CreateFlags |= SHFL_CF_ACCESS_WRITE;
354 break;
355
356 case O_RDWR:
357 params.CreateFlags |= SHFL_CF_ACCESS_READWRITE;
358 break;
359
360 default:
361 BUG ();
362 }
363
364 if (file->f_flags & O_APPEND)
365 {
366 LogFunc(("O_APPEND set\n"));
367 params.CreateFlags |= SHFL_CF_ACCESS_APPEND;
368 }
369
370 params.Info.Attr.fMode = inode->i_mode;
371 LogFunc(("sf_reg_open: calling VbglR0SfCreate, file %s, flags=%#x, %#x\n",
372 sf_i->path->String.utf8 , file->f_flags, params.CreateFlags));
373 rc = VbglR0SfCreate(&client_handle, &sf_g->map, sf_i->path, &params);
374 if (RT_FAILURE(rc))
375 {
376 LogFunc(("VbglR0SfCreate failed flags=%d,%#x rc=%Rrc\n",
377 file->f_flags, params.CreateFlags, rc));
378 kfree(sf_r);
379 return -RTErrConvertToErrno(rc);
380 }
381
382 if (SHFL_HANDLE_NIL == params.Handle)
383 {
384 switch (params.Result)
385 {
386 case SHFL_PATH_NOT_FOUND:
387 case SHFL_FILE_NOT_FOUND:
388 rc_linux = -ENOENT;
389 break;
390 case SHFL_FILE_EXISTS:
391 rc_linux = -EEXIST;
392 break;
393 default:
394 break;
395 }
396 }
397
398 sf_i->force_restat = 1;
399 sf_r->handle = params.Handle;
400 sf_i->file = file;
401 file->private_data = sf_r;
402 return rc_linux;
403 }
404
405 /**
406 * Close a regular file.
407 *
408 * @param inode the inode
409 * @param file the file
410 * @returns 0 on success, Linux error code otherwise
411 */
412 static int sf_reg_release(struct inode *inode, struct file *file)
413 {
414 int rc;
415 struct sf_reg_info *sf_r;
416 struct sf_glob_info *sf_g;
417 struct sf_inode_info *sf_i = GET_INODE_INFO(inode);
418
419 TRACE();
420 sf_g = GET_GLOB_INFO(inode->i_sb);
421 sf_r = file->private_data;
422
423 BUG_ON(!sf_g);
424 BUG_ON(!sf_r);
425
426 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25)
427 /* See the smbfs source (file.c). mmap in particular can cause data to be
428 * written to the file after it is closed, which we can't cope with. We
429 * copy and paste the body of filemap_write_and_wait() here as it was not
430 * defined before 2.6.6 and not exported until quite a bit later. */
431 /* filemap_write_and_wait(inode->i_mapping); */
432 if ( inode->i_mapping->nrpages
433 && filemap_fdatawrite(inode->i_mapping) != -EIO)
434 filemap_fdatawait(inode->i_mapping);
435 #endif
436 rc = VbglR0SfClose(&client_handle, &sf_g->map, sf_r->handle);
437 if (RT_FAILURE(rc))
438 LogFunc(("VbglR0SfClose failed rc=%Rrc\n", rc));
439
440 kfree(sf_r);
441 sf_i->file = NULL;
442 sf_i->handle = SHFL_HANDLE_NIL;
443 file->private_data = NULL;
444 return 0;
445 }
446
447 #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
448 static int sf_reg_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
449 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
450 static struct page *sf_reg_nopage(struct vm_area_struct *vma, unsigned long vaddr, int *type)
451 # define SET_TYPE(t) *type = (t)
452 #else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) */
453 static struct page *sf_reg_nopage(struct vm_area_struct *vma, unsigned long vaddr, int unused)
454 # define SET_TYPE(t)
455 #endif
456 {
457 struct page *page;
458 char *buf;
459 loff_t off;
460 uint32_t nread = PAGE_SIZE;
461 int err;
462 struct file *file = vma->vm_file;
463 struct inode *inode = GET_F_DENTRY(file)->d_inode;
464 struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
465 struct sf_reg_info *sf_r = file->private_data;
466
467 TRACE();
468 #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
469 if (vmf->pgoff > vma->vm_end)
470 return VM_FAULT_SIGBUS;
471 #else
472 if (vaddr > vma->vm_end)
473 {
474 SET_TYPE(VM_FAULT_SIGBUS);
475 return NOPAGE_SIGBUS;
476 }
477 #endif
478
479 /* Don't use GFP_HIGHUSER as long as sf_reg_read_aux() calls VbglR0SfRead()
480 * which works on virtual addresses. On Linux cannot reliably determine the
481 * physical address for high memory, see rtR0MemObjNativeLockKernel(). */
482 page = alloc_page(GFP_USER);
483 if (!page) {
484 LogRelFunc(("failed to allocate page\n"));
485 #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
486 return VM_FAULT_OOM;
487 #else
488 SET_TYPE(VM_FAULT_OOM);
489 return NOPAGE_OOM;
490 #endif
491 }
492
493 buf = kmap(page);
494 #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
495 off = (vmf->pgoff << PAGE_SHIFT);
496 #else
497 off = (vaddr - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT);
498 #endif
499 err = sf_reg_read_aux(__func__, sf_g, sf_r, buf, &nread, off);
500 if (err)
501 {
502 kunmap(page);
503 put_page(page);
504 #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
505 return VM_FAULT_SIGBUS;
506 #else
507 SET_TYPE(VM_FAULT_SIGBUS);
508 return NOPAGE_SIGBUS;
509 #endif
510 }
511
512 BUG_ON (nread > PAGE_SIZE);
513 if (!nread)
514 {
515 #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
516 clear_user_page(page_address(page), vmf->pgoff, page);
517 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
518 clear_user_page(page_address(page), vaddr, page);
519 #else
520 clear_user_page(page_address(page), vaddr);
521 #endif
522 }
523 else
524 memset(buf + nread, 0, PAGE_SIZE - nread);
525
526 flush_dcache_page(page);
527 kunmap(page);
528 #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
529 vmf->page = page;
530 return 0;
531 #else
532 SET_TYPE(VM_FAULT_MAJOR);
533 return page;
534 #endif
535 }
536
537 static struct vm_operations_struct sf_vma_ops =
538 {
539 #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
540 .fault = sf_reg_fault
541 #else
542 .nopage = sf_reg_nopage
543 #endif
544 };
545
546 static int sf_reg_mmap(struct file *file, struct vm_area_struct *vma)
547 {
548 TRACE();
549 if (vma->vm_flags & VM_SHARED)
550 {
551 LogFunc(("shared mmapping not available\n"));
552 return -EINVAL;
553 }
554
555 vma->vm_ops = &sf_vma_ops;
556 return 0;
557 }
558
559 struct file_operations sf_reg_fops =
560 {
561 .read = sf_reg_read,
562 .open = sf_reg_open,
563 .write = sf_reg_write,
564 .release = sf_reg_release,
565 .mmap = sf_reg_mmap,
566 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
567 # if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
568 .splice_read = generic_file_splice_read,
569 # else
570 .sendfile = generic_file_sendfile,
571 # endif
572 # if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
573 .read_iter = generic_file_read_iter,
574 .write_iter = generic_file_write_iter,
575 # else
576 .aio_read = generic_file_aio_read,
577 .aio_write = generic_file_aio_write,
578 # endif
579 # if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
580 .fsync = noop_fsync,
581 # else
582 .fsync = simple_sync_file,
583 # endif
584 .llseek = generic_file_llseek,
585 #endif
586 };
587
588
589 struct inode_operations sf_reg_iops =
590 {
591 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
592 .revalidate = sf_inode_revalidate
593 #else
594 .getattr = sf_getattr,
595 .setattr = sf_setattr
596 #endif
597 };
598
599
600 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
601 static int sf_readpage(struct file *file, struct page *page)
602 {
603 struct inode *inode = GET_F_DENTRY(file)->d_inode;
604 struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
605 struct sf_reg_info *sf_r = file->private_data;
606 uint32_t nread = PAGE_SIZE;
607 char *buf;
608 loff_t off = ((loff_t)page->index) << PAGE_SHIFT;
609 int ret;
610
611 TRACE();
612
613 buf = kmap(page);
614 ret = sf_reg_read_aux(__func__, sf_g, sf_r, buf, &nread, off);
615 if (ret)
616 {
617 kunmap(page);
618 if (PageLocked(page))
619 unlock_page(page);
620 return ret;
621 }
622 BUG_ON(nread > PAGE_SIZE);
623 memset(&buf[nread], 0, PAGE_SIZE - nread);
624 flush_dcache_page(page);
625 kunmap(page);
626 SetPageUptodate(page);
627 unlock_page(page);
628 return 0;
629 }
630
631 static int
632 sf_writepage(struct page *page, struct writeback_control *wbc)
633 {
634 struct address_space *mapping = page->mapping;
635 struct inode *inode = mapping->host;
636 struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
637 struct sf_inode_info *sf_i = GET_INODE_INFO(inode);
638 struct file *file = sf_i->file;
639 struct sf_reg_info *sf_r = file->private_data;
640 char *buf;
641 uint32_t nwritten = PAGE_SIZE;
642 int end_index = inode->i_size >> PAGE_SHIFT;
643 loff_t off = ((loff_t) page->index) << PAGE_SHIFT;
644 int err;
645
646 TRACE();
647
648 if (page->index >= end_index)
649 nwritten = inode->i_size & (PAGE_SIZE-1);
650
651 buf = kmap(page);
652
653 err = sf_reg_write_aux(__func__, sf_g, sf_r, buf, &nwritten, off);
654 if (err < 0)
655 {
656 ClearPageUptodate(page);
657 goto out;
658 }
659
660 if (off > inode->i_size)
661 inode->i_size = off;
662
663 if (PageError(page))
664 ClearPageError(page);
665 err = 0;
666
667 out:
668 kunmap(page);
669
670 unlock_page(page);
671 return err;
672 }
673
674 # if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
675 int sf_write_begin(struct file *file, struct address_space *mapping, loff_t pos,
676 unsigned len, unsigned flags, struct page **pagep, void **fsdata)
677 {
678 TRACE();
679
680 return simple_write_begin(file, mapping, pos, len, flags, pagep, fsdata);
681 }
682
683 int sf_write_end(struct file *file, struct address_space *mapping, loff_t pos,
684 unsigned len, unsigned copied, struct page *page, void *fsdata)
685 {
686 struct inode *inode = mapping->host;
687 struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
688 struct sf_reg_info *sf_r = file->private_data;
689 void *buf;
690 unsigned from = pos & (PAGE_SIZE - 1);
691 uint32_t nwritten = len;
692 int err;
693
694 TRACE();
695
696 buf = kmap(page);
697 err = sf_reg_write_aux(__func__, sf_g, sf_r, buf+from, &nwritten, pos);
698 kunmap(page);
699
700 if (!PageUptodate(page) && err == PAGE_SIZE)
701 SetPageUptodate(page);
702
703 if (err >= 0) {
704 pos += nwritten;
705 if (pos > inode->i_size)
706 inode->i_size = pos;
707 }
708
709 unlock_page(page);
710 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
711 put_page(page);
712 #else
713 page_cache_release(page);
714 #endif
715
716 return nwritten;
717 }
718
719 # endif /* KERNEL_VERSION >= 2.6.24 */
720
721 struct address_space_operations sf_reg_aops =
722 {
723 .readpage = sf_readpage,
724 .writepage = sf_writepage,
725 # if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
726 .write_begin = sf_write_begin,
727 .write_end = sf_write_end,
728 # else
729 .prepare_write = simple_prepare_write,
730 .commit_write = simple_commit_write,
731 # endif
732 };
733 #endif