VirtualBox

source: vbox/trunk/src/VBox/Additions/linux/sharedfolders/regops.c@ 77390

Last change on this file since 77390 was 77303, checked in by vboxsync, 6 years ago

linux/vboxsf: Cleaned out the VBOXSF_USE_DEPRECATED_VBGL_INTERFACE #ifdefs. bugref:9172

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.6 KB
Line 
1/* $Id: regops.c 77303 2019-02-13 14:49:11Z vboxsync $ */
2/** @file
3 * vboxsf - VBox Linux Shared Folders VFS, regular file inode and file operations.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without
12 * restriction, including without limitation the rights to use,
13 * copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following
16 * conditions:
17 *
18 * The above copyright notice and this permission notice shall be
19 * included in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
23 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28 * OTHER DEALINGS IN THE SOFTWARE.
29 */
30
31/*
32 * Limitations: only COW memory mapping is supported
33 */
34
35#include "vfsmod.h"
36
37#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
38
39/*
40 * inode compatibility glue.
41 */
42#include <iprt/asm.h>
43
44DECLINLINE(loff_t) i_size_read(struct inode *inode)
45{
46 AssertCompile(sizeof(loff_t) == sizeof(uint64_t));
47 return ASMAtomicReadU64((uint64_t volatile *)&inode->i_size);
48}
49
50DECLINLINE(void) i_size_write(struct inode *inode, loff_t i_size)
51{
52 AssertCompile(sizeof(inode->i_size) == sizeof(uint64_t));
53 ASMAtomicWriteU64((uint64_t volatile *)&inode->i_size, i_size);
54}
55
56#endif /* < 2.6.0 */
57
58/* fops */
59static 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 LogFunc(("VbglR0SfRead failed. caller=%s, rc=%Rrc\n", caller,
70 rc));
71 return -EPROTO;
72 }
73 return 0;
74}
75
76static 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,
85 false /* already locked? */ );
86 if (RT_FAILURE(rc)) {
87 LogFunc(("VbglR0SfWrite failed. caller=%s, rc=%Rrc\n",
88 caller, rc));
89 return -EPROTO;
90 }
91 return 0;
92}
93
94#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) \
95 && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
96
97void free_pipebuf(struct page *kpage)
98{
99 kunmap(kpage);
100 __free_pages(kpage, 0);
101}
102
103void *sf_pipe_buf_map(struct pipe_inode_info *pipe,
104 struct pipe_buffer *pipe_buf, int atomic)
105{
106 return 0;
107}
108
109void sf_pipe_buf_get(struct pipe_inode_info *pipe, struct pipe_buffer *pipe_buf)
110{
111}
112
113void sf_pipe_buf_unmap(struct pipe_inode_info *pipe,
114 struct pipe_buffer *pipe_buf, void *map_data)
115{
116}
117
118int sf_pipe_buf_steal(struct pipe_inode_info *pipe,
119 struct pipe_buffer *pipe_buf)
120{
121 return 0;
122}
123
124static void sf_pipe_buf_release(struct pipe_inode_info *pipe,
125 struct pipe_buffer *pipe_buf)
126{
127 free_pipebuf(pipe_buf->page);
128}
129
130int sf_pipe_buf_confirm(struct pipe_inode_info *info,
131 struct pipe_buffer *pipe_buf)
132{
133 return 0;
134}
135
136static struct pipe_buf_operations sf_pipe_buf_ops = {
137 .can_merge = 0,
138 .map = sf_pipe_buf_map,
139 .unmap = sf_pipe_buf_unmap,
140 .confirm = sf_pipe_buf_confirm,
141 .release = sf_pipe_buf_release,
142 .steal = sf_pipe_buf_steal,
143 .get = sf_pipe_buf_get,
144};
145
146#define LOCK_PIPE(pipe) \
147 if (pipe->inode) \
148 mutex_lock(&pipe->inode->i_mutex);
149
150#define UNLOCK_PIPE(pipe) \
151 if (pipe->inode) \
152 mutex_unlock(&pipe->inode->i_mutex);
153
154ssize_t
155sf_splice_read(struct file *in, loff_t * poffset,
156 struct pipe_inode_info *pipe, size_t len, unsigned int flags)
157{
158 size_t bytes_remaining = len;
159 loff_t orig_offset = *poffset;
160 loff_t offset = orig_offset;
161 struct inode *inode = GET_F_DENTRY(in)->d_inode;
162 struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
163 struct sf_reg_info *sf_r = in->private_data;
164 ssize_t retval;
165 struct page *kpage = 0;
166 size_t nsent = 0;
167
168/** @todo rig up a FsPerf test for this code */
169 TRACE();
170 if (!S_ISREG(inode->i_mode)) {
171 LogFunc(("read from non regular file %d\n", inode->i_mode));
172 return -EINVAL;
173 }
174 if (!len) {
175 return 0;
176 }
177
178 LOCK_PIPE(pipe);
179
180 uint32_t req_size = 0;
181 while (bytes_remaining > 0) {
182 kpage = alloc_page(GFP_KERNEL);
183 if (unlikely(kpage == NULL)) {
184 UNLOCK_PIPE(pipe);
185 return -ENOMEM;
186 }
187 req_size = 0;
188 uint32_t nread = req_size =
189 (uint32_t) min(bytes_remaining, (size_t) PAGE_SIZE);
190 uint32_t chunk = 0;
191 void *kbuf = kmap(kpage);
192 while (chunk < req_size) {
193 retval =
194 sf_reg_read_aux(__func__, sf_g, sf_r, kbuf + chunk,
195 &nread, offset);
196 if (retval < 0)
197 goto err;
198 if (nread == 0)
199 break;
200 chunk += nread;
201 offset += nread;
202 nread = req_size - chunk;
203 }
204 if (!pipe->readers) {
205 send_sig(SIGPIPE, current, 0);
206 retval = -EPIPE;
207 goto err;
208 }
209 if (pipe->nrbufs < PIPE_BUFFERS) {
210 struct pipe_buffer *pipebuf =
211 pipe->bufs +
212 ((pipe->curbuf + pipe->nrbufs) & (PIPE_BUFFERS -
213 1));
214 pipebuf->page = kpage;
215 pipebuf->ops = &sf_pipe_buf_ops;
216 pipebuf->len = req_size;
217 pipebuf->offset = 0;
218 pipebuf->private = 0;
219 pipebuf->flags = 0;
220 pipe->nrbufs++;
221 nsent += req_size;
222 bytes_remaining -= req_size;
223 if (signal_pending(current))
224 break;
225 } else { /* pipe full */
226
227 if (flags & SPLICE_F_NONBLOCK) {
228 retval = -EAGAIN;
229 goto err;
230 }
231 free_pipebuf(kpage);
232 break;
233 }
234 }
235 UNLOCK_PIPE(pipe);
236 if (!nsent && signal_pending(current))
237 return -ERESTARTSYS;
238 *poffset += nsent;
239 return offset - orig_offset;
240
241 err:
242 UNLOCK_PIPE(pipe);
243 free_pipebuf(kpage);
244 return retval;
245}
246
247#endif /* 2.6.23 <= LINUX_VERSION_CODE < 2.6.31 */
248
249
250/** Companion to sf_lock_user_pages(). */
251DECLINLINE(void) sf_unlock_user_pages(struct page **papPages, size_t cPages, bool fSetDirty)
252{
253 while (cPages-- > 0)
254 {
255 struct page *pPage = papPages[cPages];
256 if (fSetDirty && !PageReserved(pPage))
257 SetPageDirty(pPage);
258#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
259 put_page(pPage);
260#else
261 page_cache_release(pPage);
262#endif
263 }
264}
265
266
267/** Wrapper around get_user_pages. */
268DECLINLINE(int) sf_lock_user_pages(uintptr_t uPtrFrom, size_t cPages, bool fWrite, struct page **papPages)
269{
270# if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
271 ssize_t cPagesLocked = get_user_pages_unlocked(uPtrFrom, cPages, papPages,
272 fWrite ? FOLL_WRITE | FOLL_FORCE : FOLL_FORCE);
273# elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
274 ssize_t cPagesLocked = get_user_pages_unlocked(uPtrFrom, cPages, fWrite, 1 /*force*/, papPages);
275# elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)
276 ssize_t cPagesLocked = get_user_pages_unlocked(current, current->mm, uPtrFrom, cPages,
277 fWrite, 1 /*force*/, papPages);
278# else
279 struct task_struct *pTask = current;
280 size_t cPagesLocked;
281 down_read(&pTask->mm->mmap_sem);
282 cPagesLocked = get_user_pages(current, current->mm, uPtrFrom, cPages, fWrite, 1 /*force*/, papPages, NULL);
283 up_read(&pTask->mm->mmap_sem);
284# endif
285 if (cPagesLocked == cPages)
286 return 0;
287 if (cPagesLocked < 0)
288 return cPagesLocked;
289
290 sf_unlock_user_pages(papPages, cPagesLocked, false /*fSetDirty*/);
291
292 /* We could use uPtrFrom + cPagesLocked to get the correct status here... */
293 return -EFAULT;
294}
295
296
297/**
298 * Fallback case of sf_reg_read() that locks the user buffers and let the host
299 * write directly to them.
300 */
301static ssize_t sf_reg_read_fallback(struct file *file, char /*__user*/ *buf, size_t size, loff_t *off,
302 struct sf_glob_info *sf_g, struct sf_reg_info *sf_r)
303{
304 /*
305 * Lock pages and execute the read, taking care not to pass the host
306 * more than it can handle in one go or more than we care to allocate
307 * page arrays for. The latter limit is set at just short of 32KB due
308 * to how the physical heap works.
309 */
310 struct page *apPagesStack[16];
311 struct page **papPages = &apPagesStack[0];
312 struct page **papPagesFree = NULL;
313 VBOXSFREADPGLSTREQ *pReq;
314 loff_t offFile = *off;
315 ssize_t cbRet = -ENOMEM;
316 size_t cPages = (((uintptr_t)buf & PAGE_OFFSET_MASK) + size + PAGE_OFFSET_MASK) >> PAGE_SHIFT;
317 size_t cMaxPages = RT_MIN(RT_MAX(sf_g->cMaxIoPages, 1), cPages);
318
319 pReq = (VBOXSFREADPGLSTREQ *)VbglR0PhysHeapAlloc(RT_UOFFSETOF_DYN(VBOXSFREADPGLSTREQ, PgLst.aPages[cMaxPages]));
320 while (!pReq && cMaxPages > 4) {
321 cMaxPages /= 2;
322 pReq = (VBOXSFREADPGLSTREQ *)VbglR0PhysHeapAlloc(RT_UOFFSETOF_DYN(VBOXSFREADPGLSTREQ, PgLst.aPages[cMaxPages]));
323 }
324 if (pReq && cPages > RT_ELEMENTS(apPagesStack))
325 papPagesFree = papPages = kmalloc(cMaxPages * sizeof(sizeof(papPages[0])), GFP_KERNEL);
326 if (pReq && papPages) {
327 cbRet = 0;
328 for (;;) {
329 /*
330 * Figure out how much to process now and lock the user pages.
331 */
332 int rc;
333 size_t cbChunk = (uintptr_t)buf & PAGE_OFFSET_MASK;
334 pReq->PgLst.offFirstPage = (uint16_t)cbChunk;
335 cPages = RT_ALIGN_Z(cbChunk + size, PAGE_SIZE) >> PAGE_SHIFT;
336 if (cPages <= cMaxPages)
337 cbChunk = size;
338 else {
339 cPages = cMaxPages;
340 cbChunk = (cMaxPages << PAGE_SHIFT) - cbChunk;
341 }
342
343 rc = sf_lock_user_pages((uintptr_t)buf, cPages, true /*fWrite*/, papPages);
344 if (rc == 0) {
345 size_t iPage = cPages;
346 while (iPage-- > 0)
347 pReq->PgLst.aPages[iPage] = page_to_phys(papPages[iPage]);
348 } else {
349 cbRet = rc;
350 break;
351 }
352
353 /*
354 * Issue the request and unlock the pages.
355 */
356 rc = VbglR0SfHostReqReadPgLst(sf_g->map.root, pReq, sf_r->handle, offFile, cbChunk, cPages);
357
358 sf_unlock_user_pages(papPages, cPages, true /*fSetDirty*/);
359
360 if (RT_SUCCESS(rc)) {
361 /*
362 * Success, advance position and buffer.
363 */
364 uint32_t cbActual = pReq->Parms.cb32Read.u.value32;
365 AssertStmt(cbActual <= cbChunk, cbActual = cbChunk);
366 cbRet += cbActual;
367 offFile += cbActual;
368 buf = (uint8_t *)buf + cbActual;
369 size -= cbActual;
370
371 /*
372 * Are we done already? If so commit the new file offset.
373 */
374 if (!size || cbActual < cbChunk) {
375 *off = offFile;
376 break;
377 }
378 } else if (rc == VERR_NO_MEMORY && cMaxPages > 4) {
379 /*
380 * The host probably doesn't have enough heap to handle the
381 * request, reduce the page count and retry.
382 */
383 cMaxPages /= 4;
384 Assert(cMaxPages > 0);
385 } else {
386 /*
387 * If we've successfully read stuff, return it rather than
388 * the error. (Not sure if this is such a great idea...)
389 */
390 if (cbRet > 0)
391 *off = offFile;
392 else
393 cbRet = -EPROTO;
394 break;
395 }
396 }
397 }
398 if (papPagesFree)
399 kfree(papPages);
400 if (pReq)
401 VbglR0PhysHeapFree(pReq);
402 return cbRet;
403}
404
405
406/**
407 * Read from a regular file.
408 *
409 * @param file the file
410 * @param buf the buffer
411 * @param size length of the buffer
412 * @param off offset within the file (in/out).
413 * @returns the number of read bytes on success, Linux error code otherwise
414 */
415static ssize_t sf_reg_read(struct file *file, char /*__user*/ *buf, size_t size,
416 loff_t *off)
417{
418 struct inode *inode = GET_F_DENTRY(file)->d_inode;
419 struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
420 struct sf_reg_info *sf_r = file->private_data;
421
422 TRACE();
423 if (!S_ISREG(inode->i_mode)) {
424 LogFunc(("read from non regular file %d\n", inode->i_mode));
425 return -EINVAL;
426 }
427
428 /** @todo XXX Check read permission according to inode->i_mode! */
429
430 if (!size)
431 return 0;
432
433 /*
434 * For small requests, try use an embedded buffer provided we get a heap block
435 * that does not cross page boundraries (see host code).
436 */
437 if (size <= PAGE_SIZE / 4 * 3 - RT_UOFFSETOF(VBOXSFREADEMBEDDEDREQ, abData[0]) /* see allocator */) {
438 uint32_t const cbReq = RT_UOFFSETOF(VBOXSFREADEMBEDDEDREQ, abData[0]) + size;
439 VBOXSFREADEMBEDDEDREQ *pReq = (VBOXSFREADEMBEDDEDREQ *)VbglR0PhysHeapAlloc(cbReq);
440 if ( pReq
441 && (PAGE_SIZE - ((uintptr_t)pReq & PAGE_OFFSET_MASK)) >= cbReq) {
442 ssize_t cbRet;
443 int vrc = VbglR0SfHostReqReadEmbedded(sf_g->map.root, pReq, sf_r->handle, *off, (uint32_t)size);
444 if (RT_SUCCESS(vrc)) {
445 cbRet = pReq->Parms.cb32Read.u.value32;
446 AssertStmt(cbRet <= (ssize_t)size, cbRet = size);
447 if (copy_to_user(buf, pReq->abData, cbRet) == 0)
448 *off += cbRet;
449 else
450 cbRet = -EFAULT;
451 } else
452 cbRet = -EPROTO;
453 VbglR0PhysHeapFree(pReq);
454 return cbRet;
455 }
456 if (pReq)
457 VbglR0PhysHeapFree(pReq);
458 }
459
460# if 0 /* Turns out this is slightly slower than locking the pages even for 4KB reads (4.19/amd64). */
461 /*
462 * For medium sized requests try use a bounce buffer.
463 */
464 if (size <= _64K /** @todo make this configurable? */) {
465 void *pvBounce = kmalloc(size, GFP_KERNEL);
466 if (pvBounce) {
467 VBOXSFREADPGLSTREQ *pReq = (VBOXSFREADPGLSTREQ *)VbglR0PhysHeapAlloc(sizeof(*pReq));
468 if (pReq) {
469 ssize_t cbRet;
470 int vrc = VbglR0SfHostReqReadContig(sf_g->map.root, pReq, sf_r->handle, *off, (uint32_t)size,
471 pvBounce, virt_to_phys(pvBounce));
472 if (RT_SUCCESS(vrc)) {
473 cbRet = pReq->Parms.cb32Read.u.value32;
474 AssertStmt(cbRet <= (ssize_t)size, cbRet = size);
475 if (copy_to_user(buf, pvBounce, cbRet) == 0)
476 *off += cbRet;
477 else
478 cbRet = -EFAULT;
479 } else
480 cbRet = -EPROTO;
481 VbglR0PhysHeapFree(pReq);
482 kfree(pvBounce);
483 return cbRet;
484 }
485 kfree(pvBounce);
486 }
487 }
488# endif
489
490 return sf_reg_read_fallback(file, buf, size, off, sf_g, sf_r);
491}
492
493
494/**
495 * Fallback case of sf_reg_write() that locks the user buffers and let the host
496 * write directly to them.
497 */
498static ssize_t sf_reg_write_fallback(struct file *file, const char /*__user*/ *buf, size_t size, loff_t *off, loff_t offFile,
499 struct inode *inode, struct sf_inode_info *sf_i,
500 struct sf_glob_info *sf_g, struct sf_reg_info *sf_r)
501{
502 /*
503 * Lock pages and execute the write, taking care not to pass the host
504 * more than it can handle in one go or more than we care to allocate
505 * page arrays for. The latter limit is set at just short of 32KB due
506 * to how the physical heap works.
507 */
508 struct page *apPagesStack[16];
509 struct page **papPages = &apPagesStack[0];
510 struct page **papPagesFree = NULL;
511 VBOXSFWRITEPGLSTREQ *pReq;
512 ssize_t cbRet = -ENOMEM;
513 size_t cPages = (((uintptr_t)buf & PAGE_OFFSET_MASK) + size + PAGE_OFFSET_MASK) >> PAGE_SHIFT;
514 size_t cMaxPages = RT_MIN(RT_MAX(sf_g->cMaxIoPages, 1), cPages);
515
516 pReq = (VBOXSFWRITEPGLSTREQ *)VbglR0PhysHeapAlloc(RT_UOFFSETOF_DYN(VBOXSFWRITEPGLSTREQ, PgLst.aPages[cMaxPages]));
517 while (!pReq && cMaxPages > 4) {
518 cMaxPages /= 2;
519 pReq = (VBOXSFWRITEPGLSTREQ *)VbglR0PhysHeapAlloc(RT_UOFFSETOF_DYN(VBOXSFWRITEPGLSTREQ, PgLst.aPages[cMaxPages]));
520 }
521 if (pReq && cPages > RT_ELEMENTS(apPagesStack))
522 papPagesFree = papPages = kmalloc(cMaxPages * sizeof(sizeof(papPages[0])), GFP_KERNEL);
523 if (pReq && papPages) {
524 cbRet = 0;
525 for (;;) {
526 /*
527 * Figure out how much to process now and lock the user pages.
528 */
529 int rc;
530 size_t cbChunk = (uintptr_t)buf & PAGE_OFFSET_MASK;
531 pReq->PgLst.offFirstPage = (uint16_t)cbChunk;
532 cPages = RT_ALIGN_Z(cbChunk + size, PAGE_SIZE) >> PAGE_SHIFT;
533 if (cPages <= cMaxPages)
534 cbChunk = size;
535 else {
536 cPages = cMaxPages;
537 cbChunk = (cMaxPages << PAGE_SHIFT) - cbChunk;
538 }
539
540 rc = sf_lock_user_pages((uintptr_t)buf, cPages, false /*fWrite*/, papPages);
541 if (rc == 0) {
542 size_t iPage = cPages;
543 while (iPage-- > 0)
544 pReq->PgLst.aPages[iPage] = page_to_phys(papPages[iPage]);
545 } else {
546 cbRet = rc;
547 break;
548 }
549
550 /*
551 * Issue the request and unlock the pages.
552 */
553 rc = VbglR0SfHostReqWritePgLst(sf_g->map.root, pReq, sf_r->handle, offFile, cbChunk, cPages);
554
555 sf_unlock_user_pages(papPages, cPages, false /*fSetDirty*/);
556
557 if (RT_SUCCESS(rc)) {
558 /*
559 * Success, advance position and buffer.
560 */
561 uint32_t cbActual = pReq->Parms.cb32Write.u.value32;
562 AssertStmt(cbActual <= cbChunk, cbActual = cbChunk);
563 cbRet += cbActual;
564 offFile += cbActual;
565 buf = (uint8_t *)buf + cbActual;
566 size -= cbActual;
567 if (offFile > i_size_read(inode))
568 i_size_write(inode, offFile);
569
570 /*
571 * Are we done already? If so commit the new file offset.
572 */
573 if (!size || cbActual < cbChunk) {
574 *off = offFile;
575 break;
576 }
577 } else if (rc == VERR_NO_MEMORY && cMaxPages > 4) {
578 /*
579 * The host probably doesn't have enough heap to handle the
580 * request, reduce the page count and retry.
581 */
582 cMaxPages /= 4;
583 Assert(cMaxPages > 0);
584 } else {
585 /*
586 * If we've successfully written stuff, return it rather than
587 * the error. (Not sure if this is such a great idea...)
588 */
589 if (cbRet > 0)
590 *off = offFile;
591 else
592 cbRet = -EPROTO;
593 break;
594 }
595 sf_i->force_restat = 1; /* mtime (and size) may have changed */
596 }
597 }
598 if (papPagesFree)
599 kfree(papPages);
600 if (pReq)
601 VbglR0PhysHeapFree(pReq);
602 return cbRet;
603}
604
605
606/**
607 * Write to a regular file.
608 *
609 * @param file the file
610 * @param buf the buffer
611 * @param size length of the buffer
612 * @param off offset within the file
613 * @returns the number of written bytes on success, Linux error code otherwise
614 */
615static ssize_t sf_reg_write(struct file *file, const char *buf, size_t size,
616 loff_t * off)
617{
618 loff_t pos;
619 struct inode *inode = GET_F_DENTRY(file)->d_inode;
620 struct sf_inode_info *sf_i = GET_INODE_INFO(inode);
621 struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
622 struct sf_reg_info *sf_r = file->private_data;
623
624 TRACE();
625 BUG_ON(!sf_i);
626 BUG_ON(!sf_g);
627 BUG_ON(!sf_r);
628
629 if (!S_ISREG(inode->i_mode)) {
630 LogFunc(("write to non regular file %d\n", inode->i_mode));
631 return -EINVAL;
632 }
633
634 pos = *off;
635 /** @todo This should be handled by the host, it returning the new file
636 * offset when appending. We may have an outdated i_size value here! */
637 if (file->f_flags & O_APPEND)
638 pos = i_size_read(inode);
639
640 /** @todo XXX Check write permission according to inode->i_mode! */
641
642 if (!size) {
643 if (file->f_flags & O_APPEND) /** @todo check if this is the consensus behavior... */
644 *off = pos;
645 return 0;
646 }
647
648 /*
649 * For small requests, try use an embedded buffer provided we get a heap block
650 * that does not cross page boundraries (see host code).
651 */
652 if (size <= PAGE_SIZE / 4 * 3 - RT_UOFFSETOF(VBOXSFWRITEEMBEDDEDREQ, abData[0]) /* see allocator */) {
653 uint32_t const cbReq = RT_UOFFSETOF(VBOXSFWRITEEMBEDDEDREQ, abData[0]) + size;
654 VBOXSFWRITEEMBEDDEDREQ *pReq = (VBOXSFWRITEEMBEDDEDREQ *)VbglR0PhysHeapAlloc(cbReq);
655 if ( pReq
656 && (PAGE_SIZE - ((uintptr_t)pReq & PAGE_OFFSET_MASK)) >= cbReq) {
657 ssize_t cbRet;
658 if (copy_from_user(pReq->abData, buf, size) == 0) {
659 int vrc = VbglR0SfHostReqWriteEmbedded(sf_g->map.root, pReq, sf_r->handle, pos, (uint32_t)size);
660 if (RT_SUCCESS(vrc)) {
661 cbRet = pReq->Parms.cb32Write.u.value32;
662 AssertStmt(cbRet <= (ssize_t)size, cbRet = size);
663 pos += cbRet;
664 *off = pos;
665 if (pos > i_size_read(inode))
666 i_size_write(inode, pos);
667 } else
668 cbRet = -EPROTO;
669 sf_i->force_restat = 1; /* mtime (and size) may have changed */
670 } else
671 cbRet = -EFAULT;
672
673 VbglR0PhysHeapFree(pReq);
674 return cbRet;
675 }
676 if (pReq)
677 VbglR0PhysHeapFree(pReq);
678 }
679
680# if 0 /* Turns out this is slightly slower than locking the pages even for 4KB reads (4.19/amd64). */
681 /*
682 * For medium sized requests try use a bounce buffer.
683 */
684 if (size <= _64K /** @todo make this configurable? */) {
685 void *pvBounce = kmalloc(size, GFP_KERNEL);
686 if (pvBounce) {
687 if (copy_from_user(pvBounce, buf, size) == 0) {
688 VBOXSFWRITEPGLSTREQ *pReq = (VBOXSFWRITEPGLSTREQ *)VbglR0PhysHeapAlloc(sizeof(*pReq));
689 if (pReq) {
690 ssize_t cbRet;
691 int vrc = VbglR0SfHostReqWriteContig(sf_g->map.root, pReq, sf_r->handle, pos,
692 (uint32_t)size, pvBounce, virt_to_phys(pvBounce));
693 if (RT_SUCCESS(vrc)) {
694 cbRet = pReq->Parms.cb32Write.u.value32;
695 AssertStmt(cbRet <= (ssize_t)size, cbRet = size);
696 pos += cbRet;
697 *off = pos;
698 if (pos > i_size_read(inode))
699 i_size_write(inode, pos);
700 } else
701 cbRet = -EPROTO;
702 sf_i->force_restat = 1; /* mtime (and size) may have changed */
703 VbglR0PhysHeapFree(pReq);
704 kfree(pvBounce);
705 return cbRet;
706 }
707 kfree(pvBounce);
708 } else {
709 kfree(pvBounce);
710 return -EFAULT;
711 }
712 }
713 }
714# endif
715
716 return sf_reg_write_fallback(file, buf, size, off, pos, inode, sf_i, sf_g, sf_r);
717}
718
719
720/**
721 * Open a regular file.
722 *
723 * @param inode the inode
724 * @param file the file
725 * @returns 0 on success, Linux error code otherwise
726 */
727static int sf_reg_open(struct inode *inode, struct file *file)
728{
729 int rc, rc_linux = 0;
730 struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
731 struct sf_inode_info *sf_i = GET_INODE_INFO(inode);
732 struct sf_reg_info *sf_r;
733 VBOXSFCREATEREQ *pReq;
734 SHFLCREATEPARMS *pCreateParms; /* temp glue */
735
736 TRACE();
737 BUG_ON(!sf_g);
738 BUG_ON(!sf_i);
739
740 LogFunc(("open %s\n", sf_i->path->String.utf8));
741
742 sf_r = kmalloc(sizeof(*sf_r), GFP_KERNEL);
743 if (!sf_r) {
744 LogRelFunc(("could not allocate reg info\n"));
745 return -ENOMEM;
746 }
747
748 /* Already open? */
749 if (sf_i->handle != SHFL_HANDLE_NIL) {
750 /*
751 * This inode was created with sf_create_aux(). Check the CreateFlags:
752 * O_CREAT, O_TRUNC: inherent true (file was just created). Not sure
753 * about the access flags (SHFL_CF_ACCESS_*).
754 */
755 sf_i->force_restat = 1;
756 sf_r->handle = sf_i->handle;
757 sf_i->handle = SHFL_HANDLE_NIL;
758 sf_i->file = file;
759 file->private_data = sf_r;
760 return 0;
761 }
762
763 pReq = (VBOXSFCREATEREQ *)VbglR0PhysHeapAlloc(sizeof(*pReq) + sf_i->path->u16Size);
764 if (!pReq) {
765 kfree(sf_r);
766 LogRelFunc(("Failed to allocate a VBOXSFCREATEREQ buffer!\n"));
767 return -ENOMEM;
768 }
769 memcpy(&pReq->StrPath, sf_i->path, SHFLSTRING_HEADER_SIZE + sf_i->path->u16Size);
770 RT_ZERO(pReq->CreateParms);
771 pCreateParms = &pReq->CreateParms;
772 pCreateParms->Handle = SHFL_HANDLE_NIL;
773
774 /* We check the value of pCreateParms->Handle afterwards to find out if
775 * the call succeeded or failed, as the API does not seem to cleanly
776 * distinguish error and informational messages.
777 *
778 * Furthermore, we must set pCreateParms->Handle to SHFL_HANDLE_NIL to
779 * make the shared folders host service use our fMode parameter */
780
781 if (file->f_flags & O_CREAT) {
782 LogFunc(("O_CREAT set\n"));
783 pCreateParms->CreateFlags |= SHFL_CF_ACT_CREATE_IF_NEW;
784 /* We ignore O_EXCL, as the Linux kernel seems to call create
785 beforehand itself, so O_EXCL should always fail. */
786 if (file->f_flags & O_TRUNC) {
787 LogFunc(("O_TRUNC set\n"));
788 pCreateParms->CreateFlags |= SHFL_CF_ACT_OVERWRITE_IF_EXISTS;
789 } else
790 pCreateParms->CreateFlags |= SHFL_CF_ACT_OPEN_IF_EXISTS;
791 } else {
792 pCreateParms->CreateFlags |= SHFL_CF_ACT_FAIL_IF_NEW;
793 if (file->f_flags & O_TRUNC) {
794 LogFunc(("O_TRUNC set\n"));
795 pCreateParms->CreateFlags |= SHFL_CF_ACT_OVERWRITE_IF_EXISTS;
796 }
797 }
798
799 switch (file->f_flags & O_ACCMODE) {
800 case O_RDONLY:
801 pCreateParms->CreateFlags |= SHFL_CF_ACCESS_READ;
802 break;
803
804 case O_WRONLY:
805 pCreateParms->CreateFlags |= SHFL_CF_ACCESS_WRITE;
806 break;
807
808 case O_RDWR:
809 pCreateParms->CreateFlags |= SHFL_CF_ACCESS_READWRITE;
810 break;
811
812 default:
813 BUG();
814 }
815
816 if (file->f_flags & O_APPEND) {
817 LogFunc(("O_APPEND set\n"));
818 pCreateParms->CreateFlags |= SHFL_CF_ACCESS_APPEND;
819 }
820
821 pCreateParms->Info.Attr.fMode = inode->i_mode;
822 LogFunc(("sf_reg_open: calling VbglR0SfHostReqCreate, file %s, flags=%#x, %#x\n", sf_i->path->String.utf8, file->f_flags, pCreateParms->CreateFlags));
823 rc = VbglR0SfHostReqCreate(sf_g->map.root, pReq);
824 if (RT_FAILURE(rc)) {
825 LogFunc(("VbglR0SfHostReqCreate failed flags=%d,%#x rc=%Rrc\n", file->f_flags, pCreateParms->CreateFlags, rc));
826 kfree(sf_r);
827 VbglR0PhysHeapFree(pReq);
828 return -RTErrConvertToErrno(rc);
829 }
830
831 if (pCreateParms->Handle == SHFL_HANDLE_NIL) {
832 switch (pCreateParms->Result) {
833 case SHFL_PATH_NOT_FOUND:
834 case SHFL_FILE_NOT_FOUND:
835 rc_linux = -ENOENT;
836 break;
837 case SHFL_FILE_EXISTS:
838 rc_linux = -EEXIST;
839 break;
840 default:
841 break;
842 }
843 }
844
845 sf_i->force_restat = 1;
846 sf_r->handle = pCreateParms->Handle;
847 sf_i->file = file;
848 file->private_data = sf_r;
849 VbglR0PhysHeapFree(pReq);
850 return rc_linux;
851}
852
853/**
854 * Close a regular file.
855 *
856 * @param inode the inode
857 * @param file the file
858 * @returns 0 on success, Linux error code otherwise
859 */
860static int sf_reg_release(struct inode *inode, struct file *file)
861{
862 int rc;
863 struct sf_reg_info *sf_r;
864 struct sf_glob_info *sf_g;
865 struct sf_inode_info *sf_i = GET_INODE_INFO(inode);
866
867 TRACE();
868 sf_g = GET_GLOB_INFO(inode->i_sb);
869 sf_r = file->private_data;
870
871 BUG_ON(!sf_g);
872 BUG_ON(!sf_r);
873
874#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 25)
875 /* See the smbfs source (file.c). mmap in particular can cause data to be
876 * written to the file after it is closed, which we can't cope with. We
877 * copy and paste the body of filemap_write_and_wait() here as it was not
878 * defined before 2.6.6 and not exported until quite a bit later. */
879 /* filemap_write_and_wait(inode->i_mapping); */
880 if (inode->i_mapping->nrpages
881 && filemap_fdatawrite(inode->i_mapping) != -EIO)
882 filemap_fdatawait(inode->i_mapping);
883#endif
884 rc = VbglR0SfHostReqCloseSimple(sf_g->map.root, sf_r->handle);
885 if (RT_FAILURE(rc))
886 LogFunc(("VbglR0SfHostReqCloseSimple failed rc=%Rrc\n", rc));
887 sf_r->handle = SHFL_HANDLE_NIL;
888
889 kfree(sf_r);
890 sf_i->file = NULL;
891 sf_i->handle = SHFL_HANDLE_NIL;
892 file->private_data = NULL;
893 return 0;
894}
895
896#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
897static int sf_reg_fault(struct vm_fault *vmf)
898#elif LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
899static int sf_reg_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
900#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
901static struct page *sf_reg_nopage(struct vm_area_struct *vma,
902 unsigned long vaddr, int *type)
903# define SET_TYPE(t) *type = (t)
904#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) */
905static struct page *sf_reg_nopage(struct vm_area_struct *vma,
906 unsigned long vaddr, int unused)
907# define SET_TYPE(t)
908#endif
909{
910 struct page *page;
911 char *buf;
912 loff_t off;
913 uint32_t nread = PAGE_SIZE;
914 int err;
915#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
916 struct vm_area_struct *vma = vmf->vma;
917#endif
918 struct file *file = vma->vm_file;
919 struct inode *inode = GET_F_DENTRY(file)->d_inode;
920 struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
921 struct sf_reg_info *sf_r = file->private_data;
922
923 TRACE();
924#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
925 if (vmf->pgoff > vma->vm_end)
926 return VM_FAULT_SIGBUS;
927#else
928 if (vaddr > vma->vm_end) {
929 SET_TYPE(VM_FAULT_SIGBUS);
930 return NOPAGE_SIGBUS;
931 }
932#endif
933
934 /* Don't use GFP_HIGHUSER as long as sf_reg_read_aux() calls VbglR0SfRead()
935 * which works on virtual addresses. On Linux cannot reliably determine the
936 * physical address for high memory, see rtR0MemObjNativeLockKernel(). */
937 page = alloc_page(GFP_USER);
938 if (!page) {
939 LogRelFunc(("failed to allocate page\n"));
940#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
941 return VM_FAULT_OOM;
942#else
943 SET_TYPE(VM_FAULT_OOM);
944 return NOPAGE_OOM;
945#endif
946 }
947
948 buf = kmap(page);
949#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
950 off = (vmf->pgoff << PAGE_SHIFT);
951#else
952 off = (vaddr - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT);
953#endif
954 err = sf_reg_read_aux(__func__, sf_g, sf_r, buf, &nread, off);
955 if (err) {
956 kunmap(page);
957 put_page(page);
958#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
959 return VM_FAULT_SIGBUS;
960#else
961 SET_TYPE(VM_FAULT_SIGBUS);
962 return NOPAGE_SIGBUS;
963#endif
964 }
965
966 BUG_ON(nread > PAGE_SIZE);
967 if (!nread) {
968#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
969 clear_user_page(page_address(page), vmf->pgoff, page);
970#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
971 clear_user_page(page_address(page), vaddr, page);
972#else
973 clear_user_page(page_address(page), vaddr);
974#endif
975 } else
976 memset(buf + nread, 0, PAGE_SIZE - nread);
977
978 flush_dcache_page(page);
979 kunmap(page);
980#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
981 vmf->page = page;
982 return 0;
983#else
984 SET_TYPE(VM_FAULT_MAJOR);
985 return page;
986#endif
987}
988
989static struct vm_operations_struct sf_vma_ops = {
990#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25)
991 .fault = sf_reg_fault
992#else
993 .nopage = sf_reg_nopage
994#endif
995};
996
997static int sf_reg_mmap(struct file *file, struct vm_area_struct *vma)
998{
999 TRACE();
1000 if (vma->vm_flags & VM_SHARED) {
1001 LogFunc(("shared mmapping not available\n"));
1002 return -EINVAL;
1003 }
1004
1005 vma->vm_ops = &sf_vma_ops;
1006 return 0;
1007}
1008
1009struct file_operations sf_reg_fops = {
1010 .read = sf_reg_read,
1011 .open = sf_reg_open,
1012 .write = sf_reg_write,
1013 .release = sf_reg_release,
1014 .mmap = sf_reg_mmap,
1015#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
1016# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
1017/** @todo This code is known to cause caching of data which should not be
1018 * cached. Investigate. */
1019# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
1020 .splice_read = sf_splice_read,
1021# else
1022 .sendfile = generic_file_sendfile,
1023# endif
1024 .aio_read = generic_file_aio_read,
1025 .aio_write = generic_file_aio_write,
1026# endif
1027# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
1028 .fsync = noop_fsync,
1029# else
1030 .fsync = simple_sync_file,
1031# endif
1032 .llseek = generic_file_llseek,
1033#endif
1034};
1035
1036struct inode_operations sf_reg_iops = {
1037#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
1038 .revalidate = sf_inode_revalidate
1039#else
1040 .getattr = sf_getattr,
1041 .setattr = sf_setattr
1042#endif
1043};
1044
1045#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
1046
1047static int sf_readpage(struct file *file, struct page *page)
1048{
1049 struct inode *inode = GET_F_DENTRY(file)->d_inode;
1050 struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
1051 struct sf_reg_info *sf_r = file->private_data;
1052 uint32_t nread = PAGE_SIZE;
1053 char *buf;
1054 loff_t off = (loff_t)page->index << PAGE_SHIFT;
1055 int ret;
1056
1057 TRACE();
1058
1059 buf = kmap(page);
1060 ret = sf_reg_read_aux(__func__, sf_g, sf_r, buf, &nread, off);
1061 if (ret) {
1062 kunmap(page);
1063 if (PageLocked(page))
1064 unlock_page(page);
1065 return ret;
1066 }
1067 BUG_ON(nread > PAGE_SIZE);
1068 memset(&buf[nread], 0, PAGE_SIZE - nread);
1069 flush_dcache_page(page);
1070 kunmap(page);
1071 SetPageUptodate(page);
1072 unlock_page(page);
1073 return 0;
1074}
1075
1076static int sf_writepage(struct page *page, struct writeback_control *wbc)
1077{
1078 struct address_space *mapping = page->mapping;
1079 struct inode *inode = mapping->host;
1080 struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
1081 struct sf_inode_info *sf_i = GET_INODE_INFO(inode);
1082 struct file *file = sf_i->file;
1083 struct sf_reg_info *sf_r = file->private_data;
1084 char *buf;
1085 uint32_t nwritten = PAGE_SIZE;
1086 int end_index = inode->i_size >> PAGE_SHIFT;
1087 loff_t off = ((loff_t) page->index) << PAGE_SHIFT;
1088 int err;
1089
1090 TRACE();
1091
1092/** @todo rig up a FsPerf testcase for this code! */
1093
1094 if (page->index >= end_index)
1095 nwritten = inode->i_size & (PAGE_SIZE - 1);
1096
1097 buf = kmap(page);
1098
1099 err = sf_reg_write_aux(__func__, sf_g, sf_r, buf, &nwritten, off);
1100 if (err < 0) {
1101 ClearPageUptodate(page);
1102 goto out;
1103 }
1104
1105 if (off > inode->i_size)
1106 inode->i_size = off;
1107
1108 if (PageError(page))
1109 ClearPageError(page);
1110 err = 0;
1111
1112 out:
1113 kunmap(page);
1114
1115 unlock_page(page);
1116 return err;
1117}
1118
1119# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
1120
1121int sf_write_begin(struct file *file, struct address_space *mapping, loff_t pos,
1122 unsigned len, unsigned flags, struct page **pagep,
1123 void **fsdata)
1124{
1125 TRACE();
1126
1127 return simple_write_begin(file, mapping, pos, len, flags, pagep,
1128 fsdata);
1129}
1130
1131int sf_write_end(struct file *file, struct address_space *mapping, loff_t pos,
1132 unsigned len, unsigned copied, struct page *page, void *fsdata)
1133{
1134 struct inode *inode = mapping->host;
1135 struct sf_glob_info *sf_g = GET_GLOB_INFO(inode->i_sb);
1136 struct sf_reg_info *sf_r = file->private_data;
1137 void *buf;
1138 unsigned from = pos & (PAGE_SIZE - 1);
1139 uint32_t nwritten = len;
1140 int err;
1141
1142 TRACE();
1143
1144/** @todo rig up a FsPerf testcase for this code! */
1145
1146 buf = kmap(page);
1147 err = sf_reg_write_aux(__func__, sf_g, sf_r, buf + from, &nwritten, pos);
1148 kunmap(page);
1149
1150 if (err >= 0) {
1151 if (!PageUptodate(page) && nwritten == PAGE_SIZE)
1152 SetPageUptodate(page);
1153
1154 pos += nwritten;
1155 if (pos > inode->i_size)
1156 inode->i_size = pos;
1157 }
1158
1159 unlock_page(page);
1160# if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
1161 put_page(page);
1162# else
1163 page_cache_release(page);
1164# endif
1165
1166 return nwritten;
1167}
1168
1169# endif /* KERNEL_VERSION >= 2.6.24 */
1170
1171# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 10)
1172/**
1173 * This is needed to make open accept O_DIRECT as well as dealing with direct
1174 * I/O requests if we don't intercept them earlier.
1175 */
1176# if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
1177static ssize_t sf_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
1178# elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
1179static ssize_t sf_direct_IO(struct kiocb *iocb, struct iov_iter *iter, loff_t offset)
1180# elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
1181static ssize_t sf_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, loff_t offset)
1182# elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 6)
1183static ssize_t sf_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_t offset, unsigned long nr_segs)
1184# elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 55)
1185static int sf_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, loff_t offset, unsigned long nr_segs)
1186# elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 41)
1187static int sf_direct_IO(int rw, struct file *file, const struct iovec *iov, loff_t offset, unsigned long nr_segs)
1188# elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 35)
1189static int sf_direct_IO(int rw, struct inode *inode, const struct iovec *iov, loff_t offset, unsigned long nr_segs)
1190# elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 26)
1191static int sf_direct_IO(int rw, struct inode *inode, char *buf, loff_t offset, size_t count)
1192# else
1193static int sf_direct_IO(int rw, struct inode *inode, struct kiobuf *, unsigned long, int)
1194# endif
1195{
1196 TRACE();
1197 return -EINVAL;
1198}
1199# endif
1200
1201struct address_space_operations sf_reg_aops = {
1202 .readpage = sf_readpage,
1203 .writepage = sf_writepage,
1204# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
1205 .write_begin = sf_write_begin,
1206 .write_end = sf_write_end,
1207# else
1208 .prepare_write = simple_prepare_write,
1209 .commit_write = simple_commit_write,
1210# endif
1211# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 10)
1212 .direct_IO = sf_direct_IO,
1213# endif
1214};
1215
1216#endif /* LINUX_VERSION_CODE >= 2.6.0 */
1217
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette