VirtualBox

source: vbox/trunk/src/VBox/Additions/linux/sharedfolders/utils.c@ 77524

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

linux/vboxsf: Code cleanups in the super block info area. bugref:9172

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.3 KB
Line 
1/* $Id: utils.c 77524 2019-03-01 11:58:24Z vboxsync $ */
2/** @file
3 * vboxsf - VBox Linux Shared Folders VFS, utility functions.
4 *
5 * Utility functions (mainly conversion from/to VirtualBox/Linux data structures).
6 */
7
8/*
9 * Copyright (C) 2006-2019 Oracle Corporation
10 *
11 * Permission is hereby granted, free of charge, to any person
12 * obtaining a copy of this software and associated documentation
13 * files (the "Software"), to deal in the Software without
14 * restriction, including without limitation the rights to use,
15 * copy, modify, merge, publish, distribute, sublicense, and/or sell
16 * copies of the Software, and to permit persons to whom the
17 * Software is furnished to do so, subject to the following
18 * conditions:
19 *
20 * The above copyright notice and this permission notice shall be
21 * included in all copies or substantial portions of the Software.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
25 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
27 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
28 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
30 * OTHER DEALINGS IN THE SOFTWARE.
31 */
32
33#include "vfsmod.h"
34#include <iprt/asm.h>
35#include <iprt/err.h>
36#include <linux/vfs.h>
37
38
39/**
40 * Convert from VBox to linux time.
41 */
42#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
43DECLINLINE(void) sf_ftime_from_timespec(time_t *pLinuxDst, PCRTTIMESPEC pVBoxSrc)
44{
45 int64_t t = RTTimeSpecGetNano(pVBoxSrc);
46 do_div(t, RT_NS_1SEC);
47 *pLinuxDst = t;
48}
49#else /* >= 2.6.0 */
50# if LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0)
51DECLINLINE(void) sf_ftime_from_timespec(struct timespec *pLinuxDst, PCRTTIMESPEC pVBoxSrc)
52# else
53DECLINLINE(void) sf_ftime_from_timespec(struct timespec64 *pLinuxDst, PCRTTIMESPEC pVBoxSrc)
54# endif
55{
56 int64_t t = RTTimeSpecGetNano(pVBoxSrc);
57 pLinuxDst->tv_nsec = do_div(t, RT_NS_1SEC);
58 pLinuxDst->tv_sec = t;
59}
60#endif /* >= 2.6.0 */
61
62
63/**
64 * Convert from linux to VBox time.
65 */
66#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0)
67DECLINLINE(void) sf_timespec_from_ftime(PRTTIMESPEC pVBoxDst, time_t *pLinuxSrc)
68{
69 RTTimeSpecSetNano(pVBoxDst, RT_NS_1SEC_64 * *pLinuxSrc);
70}
71#else /* >= 2.6.0 */
72# if LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0)
73DECLINLINE(void) sf_timespec_from_ftime(PRTTIMESPEC pVBoxDst, struct timespec const *pLinuxSrc)
74# else
75DECLINLINE(void) sf_timespec_from_ftime(PRTTIMESPEC pVBoxDst, struct timespec64 const *pLinuxSrc)
76# endif
77{
78 RTTimeSpecSetNano(pVBoxDst, pLinuxSrc->tv_nsec + pLinuxSrc->tv_sec * (int64_t)RT_NS_1SEC);
79}
80#endif /* >= 2.6.0 */
81
82
83/**
84 * Converts VBox access permissions to Linux ones (mode & 0777).
85 *
86 * @note Currently identical.
87 */
88DECLINLINE(int) sf_convert_access_perms(uint32_t fAttr)
89{
90 /* Access bits should be the same: */
91 AssertCompile(RTFS_UNIX_IRUSR == S_IRUSR);
92 AssertCompile(RTFS_UNIX_IWUSR == S_IWUSR);
93 AssertCompile(RTFS_UNIX_IXUSR == S_IXUSR);
94 AssertCompile(RTFS_UNIX_IRGRP == S_IRGRP);
95 AssertCompile(RTFS_UNIX_IWGRP == S_IWGRP);
96 AssertCompile(RTFS_UNIX_IXGRP == S_IXGRP);
97 AssertCompile(RTFS_UNIX_IROTH == S_IROTH);
98 AssertCompile(RTFS_UNIX_IWOTH == S_IWOTH);
99 AssertCompile(RTFS_UNIX_IXOTH == S_IXOTH);
100
101 return fAttr & RTFS_UNIX_ALL_ACCESS_PERMS;
102}
103
104
105/**
106 * Produce the Linux mode mask, given VBox, mount options and file type.
107 */
108DECLINLINE(int) sf_convert_file_mode(uint32_t fVBoxMode, int fFixedMode, int fClearMask, int fType)
109{
110 int fLnxMode = sf_convert_access_perms(fVBoxMode);
111 if (fFixedMode != ~0)
112 fLnxMode = fFixedMode & 0777;
113 fLnxMode &= ~fClearMask;
114 fLnxMode |= fType;
115 return fLnxMode;
116}
117
118
119/**
120 * Initializes the @a inode attributes based on @a pObjInfo and @a sf_g options.
121 */
122void sf_init_inode(struct inode *inode, struct sf_inode_info *sf_i, PSHFLFSOBJINFO pObjInfo, struct vbsf_super_info *sf_g)
123{
124 PCSHFLFSOBJATTR pAttr = &pObjInfo->Attr;
125
126 TRACE();
127
128 sf_i->ts_up_to_date = jiffies;
129 sf_i->force_restat = 0;
130
131#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
132 inode->i_mapping->a_ops = &sf_reg_aops;
133# if LINUX_VERSION_CODE <= KERNEL_VERSION(3, 19, 0)
134 inode->i_mapping->backing_dev_info = &sf_g->bdi; /* This is needed for mmap. */
135# endif
136#endif
137 if (RTFS_IS_DIRECTORY(pAttr->fMode)) {
138 inode->i_mode = sf_convert_file_mode(pAttr->fMode, sf_g->dmode, sf_g->dmask, S_IFDIR);
139 inode->i_op = &sf_dir_iops;
140 inode->i_fop = &sf_dir_fops;
141
142 /* XXX: this probably should be set to the number of entries
143 in the directory plus two (. ..) */
144 set_nlink(inode, 1);
145 }
146#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 8)
147 else if (RTFS_IS_SYMLINK(pAttr->fMode)) {
148 /** @todo r=bird: Aren't System V symlinks w/o any mode mask? IIRC there is
149 * no lchmod on Linux. */
150 inode->i_mode = sf_convert_file_mode(pAttr->fMode, sf_g->fmode, sf_g->fmask, S_IFLNK);
151 inode->i_op = &sf_lnk_iops;
152 set_nlink(inode, 1);
153 }
154#endif
155 else {
156 inode->i_mode = sf_convert_file_mode(pAttr->fMode, sf_g->fmode, sf_g->fmask, S_IFREG);
157 inode->i_op = &sf_reg_iops;
158 inode->i_fop = &sf_reg_fops;
159 set_nlink(inode, 1);
160 }
161
162#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
163 inode->i_uid = make_kuid(current_user_ns(), sf_g->uid);
164 inode->i_gid = make_kgid(current_user_ns(), sf_g->gid);
165#else
166 inode->i_uid = sf_g->uid;
167 inode->i_gid = sf_g->gid;
168#endif
169
170 inode->i_size = pObjInfo->cbObject;
171#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) && !defined(KERNEL_FC6)
172 inode->i_blksize = 4096;
173#endif
174#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 11)
175 inode->i_blkbits = 12;
176#endif
177 /* i_blocks always in units of 512 bytes! */
178 inode->i_blocks = (pObjInfo->cbAllocated + 511) / 512;
179
180 sf_ftime_from_timespec(&inode->i_atime, &pObjInfo->AccessTime);
181 sf_ftime_from_timespec(&inode->i_ctime, &pObjInfo->ChangeTime);
182 sf_ftime_from_timespec(&inode->i_mtime, &pObjInfo->ModificationTime);
183 sf_i->BirthTime = pObjInfo->BirthTime;
184}
185
186/**
187 * Update the inode with new object info from the host.
188 *
189 * Called by sf_inode_revalidate() and sf_inode_revalidate_with_handle(), the
190 * inode is probably locked...
191 *
192 * @todo sort out the inode locking situation.
193 */
194static void sf_update_inode(struct inode *pInode, struct sf_inode_info *pInodeInfo, PSHFLFSOBJINFO pObjInfo,
195 struct vbsf_super_info *sf_g, bool fInodeLocked)
196{
197 PCSHFLFSOBJATTR pAttr = &pObjInfo->Attr;
198 int fMode;
199
200 TRACE();
201
202#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
203 if (!fInodeLocked)
204 inode_lock(pInode);
205#endif
206
207 /*
208 * Calc new mode mask and update it if it changed.
209 */
210 if (RTFS_IS_DIRECTORY(pAttr->fMode))
211 fMode = sf_convert_file_mode(pAttr->fMode, sf_g->dmode, sf_g->dmask, S_IFDIR);
212#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 8)
213 else if (RTFS_IS_SYMLINK(pAttr->fMode))
214 /** @todo r=bird: Aren't System V symlinks w/o any mode mask? IIRC there is
215 * no lchmod on Linux. */
216 fMode = sf_convert_file_mode(pAttr->fMode, sf_g->fmode, sf_g->fmask, S_IFLNK);
217#endif
218 else
219 fMode = sf_convert_file_mode(pAttr->fMode, sf_g->fmode, sf_g->fmask, S_IFREG);
220
221 if (fMode == pInode->i_mode) {
222 /* likely */
223 } else {
224 if ((fMode & S_IFMT) == (pInode->i_mode & S_IFMT))
225 pInode->i_mode = fMode;
226 else {
227 SFLOGFLOW(("sf_update_inode: Changed from %o to %o (%s)\n",
228 pInode->i_mode & S_IFMT, fMode & S_IFMT, pInodeInfo->path->String.ach));
229 /** @todo we probably need to be more drastic... */
230 sf_init_inode(pInode, pInodeInfo, pObjInfo, sf_g);
231
232#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
233 if (!fInodeLocked)
234 inode_unlock(pInode);
235#endif
236 return;
237 }
238 }
239
240 /*
241 * Update the sizes.
242 * Note! i_blocks is always in units of 512 bytes!
243 */
244 pInode->i_blocks = (pObjInfo->cbAllocated + 511) / 512;
245 i_size_write(pInode, pObjInfo->cbObject);
246
247 /*
248 * Update the timestamps.
249 */
250 sf_ftime_from_timespec(&pInode->i_atime, &pObjInfo->AccessTime);
251 sf_ftime_from_timespec(&pInode->i_ctime, &pObjInfo->ChangeTime);
252 sf_ftime_from_timespec(&pInode->i_mtime, &pObjInfo->ModificationTime);
253 pInodeInfo->BirthTime = pObjInfo->BirthTime;
254
255 /*
256 * Mark it as up to date.
257 */
258 pInodeInfo->ts_up_to_date = jiffies;
259 pInodeInfo->force_restat = 0;
260
261#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0)
262 if (!fInodeLocked)
263 inode_unlock(pInode);
264#endif
265}
266
267
268/** @note Currently only used for the root directory during (re-)mount. */
269int sf_stat(const char *caller, struct vbsf_super_info *sf_g,
270 SHFLSTRING *path, PSHFLFSOBJINFO result, int ok_to_fail)
271{
272 int rc;
273 VBOXSFCREATEREQ *pReq;
274 NOREF(caller);
275
276 TRACE();
277
278 pReq = (VBOXSFCREATEREQ *)VbglR0PhysHeapAlloc(sizeof(*pReq) + path->u16Size);
279 if (pReq) {
280 RT_ZERO(*pReq);
281 memcpy(&pReq->StrPath, path, SHFLSTRING_HEADER_SIZE + path->u16Size);
282 pReq->CreateParms.Handle = SHFL_HANDLE_NIL;
283 pReq->CreateParms.CreateFlags = SHFL_CF_LOOKUP | SHFL_CF_ACT_FAIL_IF_NEW;
284
285 LogFunc(("Calling VbglR0SfHostReqCreate on %s\n", path->String.utf8));
286 rc = VbglR0SfHostReqCreate(sf_g->map.root, pReq);
287 if (RT_SUCCESS(rc)) {
288 if (pReq->CreateParms.Result == SHFL_FILE_EXISTS) {
289 *result = pReq->CreateParms.Info;
290 rc = 0;
291 } else {
292 if (!ok_to_fail)
293 LogFunc(("VbglR0SfHostReqCreate on %s: file does not exist: %d (caller=%s)\n",
294 path->String.utf8, pReq->CreateParms.Result, caller));
295 rc = -ENOENT;
296 }
297 } else if (rc == VERR_INVALID_NAME) {
298 rc = -ENOENT; /* this can happen for names like 'foo*' on a Windows host */
299 } else {
300 LogFunc(("VbglR0SfHostReqCreate failed on %s: %Rrc (caller=%s)\n", path->String.utf8, rc, caller));
301 rc = -EPROTO;
302 }
303 VbglR0PhysHeapFree(pReq);
304 }
305 else
306 rc = -ENOMEM;
307 return rc;
308}
309
310/**
311 * Revalidate an inode, inner worker.
312 *
313 * @sa sf_inode_revalidate()
314 */
315int sf_inode_revalidate_worker(struct dentry *dentry, bool fForced)
316{
317 int rc;
318 struct inode *pInode = dentry ? dentry->d_inode : NULL;
319 if (pInode) {
320 struct sf_inode_info *sf_i = GET_INODE_INFO(pInode);
321 struct vbsf_super_info *sf_g = VBSF_GET_SUPER_INFO(pInode->i_sb);
322 AssertReturn(sf_i, -EINVAL);
323 AssertReturn(sf_g, -EINVAL);
324
325 /*
326 * Can we get away without any action here?
327 */
328 if ( !fForced
329 && !sf_i->force_restat
330 && jiffies - sf_i->ts_up_to_date < sf_g->ttl)
331 rc = 0;
332 else {
333 /*
334 * No, we have to query the file info from the host.
335 * Try get a handle we can query, any kind of handle will do here.
336 */
337 struct sf_handle *pHandle = sf_handle_find(sf_i, 0, 0);
338 if (pHandle) {
339 /* Query thru pHandle. */
340 VBOXSFOBJINFOREQ *pReq = (VBOXSFOBJINFOREQ *)VbglR0PhysHeapAlloc(sizeof(*pReq));
341 if (pReq) {
342 RT_ZERO(*pReq);
343 rc = VbglR0SfHostReqQueryObjInfo(sf_g->map.root, pReq, pHandle->hHost);
344 if (RT_SUCCESS(rc)) {
345 /*
346 * Reset the TTL and copy the info over into the inode structure.
347 */
348 sf_update_inode(pInode, sf_i, &pReq->ObjInfo, sf_g, true /*fInodeLocked??*/);
349 } else if (rc == VERR_INVALID_HANDLE) {
350 rc = -ENOENT; /* Restore.*/
351 } else {
352 LogFunc(("VbglR0SfHostReqQueryObjInfo failed on %#RX64: %Rrc\n", pHandle->hHost, rc));
353 rc = -RTErrConvertToErrno(rc);
354 }
355 VbglR0PhysHeapFree(pReq);
356 } else
357 rc = -ENOMEM;
358 sf_handle_release(pHandle, sf_g, "sf_inode_revalidate_worker");
359
360 } else {
361 /* Query via path. */
362 SHFLSTRING *pPath = sf_i->path;
363 VBOXSFCREATEREQ *pReq = (VBOXSFCREATEREQ *)VbglR0PhysHeapAlloc(sizeof(*pReq) + pPath->u16Size);
364 if (pReq) {
365 RT_ZERO(*pReq);
366 memcpy(&pReq->StrPath, pPath, SHFLSTRING_HEADER_SIZE + pPath->u16Size);
367 pReq->CreateParms.Handle = SHFL_HANDLE_NIL;
368 pReq->CreateParms.CreateFlags = SHFL_CF_LOOKUP | SHFL_CF_ACT_FAIL_IF_NEW;
369
370 rc = VbglR0SfHostReqCreate(sf_g->map.root, pReq);
371 if (RT_SUCCESS(rc)) {
372 if (pReq->CreateParms.Result == SHFL_FILE_EXISTS) {
373 /*
374 * Reset the TTL and copy the info over into the inode structure.
375 */
376 sf_update_inode(pInode, sf_i, &pReq->CreateParms.Info,
377 sf_g, true /*fInodeLocked??*/);
378 rc = 0;
379 } else {
380 rc = -ENOENT;
381 }
382 } else if (rc == VERR_INVALID_NAME) {
383 rc = -ENOENT; /* this can happen for names like 'foo*' on a Windows host */
384 } else {
385 LogFunc(("VbglR0SfHostReqCreate failed on %s: %Rrc\n", pPath->String.ach, rc));
386 rc = -EPROTO;
387 }
388 VbglR0PhysHeapFree(pReq);
389 }
390 else
391 rc = -ENOMEM;
392 }
393 }
394 } else {
395 LogFunc(("no dentry(%p) or inode(%p)\n", dentry, pInode));
396 rc = -EINVAL;
397 }
398 return rc;
399}
400
401
402/**
403 * Revalidate an inode.
404 *
405 * This is called directly as inode-op on 2.4, indirectly as dir-op
406 * sf_dentry_revalidate() on 2.4/2.6. The job is to find out whether
407 * dentry/inode is still valid. The test fails if @a dentry does not have an
408 * inode or sf_stat() is unsuccessful, otherwise we return success and update
409 * inode attributes.
410 */
411int sf_inode_revalidate(struct dentry *dentry)
412{
413 return sf_inode_revalidate_worker(dentry, false /*fForced*/);
414}
415
416
417/**
418 * Similar to sf_inode_revalidate, but uses associated host file handle as that
419 * is quite a bit faster.
420 */
421int sf_inode_revalidate_with_handle(struct dentry *dentry, SHFLHANDLE hHostFile, bool fForced, bool fInodeLocked)
422{
423 int err;
424 struct inode *pInode = dentry ? dentry->d_inode : NULL;
425 if (!pInode) {
426 LogFunc(("no dentry(%p) or inode(%p)\n", dentry, pInode));
427 err = -EINVAL;
428 } else {
429 struct sf_inode_info *sf_i = GET_INODE_INFO(pInode);
430 struct vbsf_super_info *sf_g = VBSF_GET_SUPER_INFO(pInode->i_sb);
431 AssertReturn(sf_i, -EINVAL);
432 AssertReturn(sf_g, -EINVAL);
433
434 /*
435 * Can we get away without any action here?
436 */
437 if ( !fForced
438 && !sf_i->force_restat
439 && jiffies - sf_i->ts_up_to_date < sf_g->ttl)
440 err = 0;
441 else {
442 /*
443 * No, we have to query the file info from the host.
444 */
445 VBOXSFOBJINFOREQ *pReq = (VBOXSFOBJINFOREQ *)VbglR0PhysHeapAlloc(sizeof(*pReq));
446 if (pReq) {
447 RT_ZERO(*pReq);
448 err = VbglR0SfHostReqQueryObjInfo(sf_g->map.root, pReq, hHostFile);
449 if (RT_SUCCESS(err)) {
450 /*
451 * Reset the TTL and copy the info over into the inode structure.
452 */
453 sf_update_inode(pInode, sf_i, &pReq->ObjInfo, sf_g, fInodeLocked);
454 } else {
455 LogFunc(("VbglR0SfHostReqQueryObjInfo failed on %#RX64: %Rrc\n", hHostFile, err));
456 err = -RTErrConvertToErrno(err);
457 }
458 VbglR0PhysHeapFree(pReq);
459 } else
460 err = -ENOMEM;
461 }
462 }
463 return err;
464}
465
466/* on 2.6 this is a proxy for [sf_inode_revalidate] which (as a side
467 effect) updates inode attributes for [dentry] (given that [dentry]
468 has inode at all) from these new attributes we derive [kstat] via
469 [generic_fillattr] */
470#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
471
472# if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
473int sf_getattr(const struct path *path, struct kstat *kstat, u32 request_mask,
474 unsigned int flags)
475# else
476int sf_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *kstat)
477# endif
478{
479 int rc;
480# if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
481 struct dentry *dentry = path->dentry;
482# endif
483
484# if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
485 SFLOGFLOW(("sf_getattr: dentry=%p request_mask=%#x flags=%#x\n", dentry, request_mask, flags));
486# else
487 SFLOGFLOW(("sf_getattr: dentry=%p\n", dentry));
488# endif
489
490# if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
491 /*
492 * With the introduction of statx() userland can control whether we
493 * update the inode information or not.
494 */
495 switch (flags & AT_STATX_SYNC_TYPE) {
496 default:
497 rc = sf_inode_revalidate_worker(dentry, false /*fForced*/);
498 break;
499
500 case AT_STATX_FORCE_SYNC:
501 rc = sf_inode_revalidate_worker(dentry, true /*fForced*/);
502 break;
503
504 case AT_STATX_DONT_SYNC:
505 rc = 0;
506 break;
507 }
508# else
509 rc = sf_inode_revalidate_worker(dentry, false /*fForced*/);
510# endif
511 if (rc == 0) {
512 /* Do generic filling in of info. */
513 generic_fillattr(dentry->d_inode, kstat);
514
515 /* Add birth time. */
516#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
517 if (dentry->d_inode) {
518 struct sf_inode_info *pInodeInfo = GET_INODE_INFO(dentry->d_inode);
519 if (pInodeInfo) {
520 sf_ftime_from_timespec(&kstat->btime, &pInodeInfo->BirthTime);
521 kstat->result_mask |= STATX_BTIME;
522 }
523 }
524#endif
525
526 /*
527 * FsPerf shows the following numbers for sequential file access against
528 * a tmpfs folder on an AMD 1950X host running debian buster/sid:
529 *
530 * block size = r128600 ----- r128755 -----
531 * reads reads writes
532 * 4096 KB = 2254 MB/s 4953 MB/s 3668 MB/s
533 * 2048 KB = 2368 MB/s 4908 MB/s 3541 MB/s
534 * 1024 KB = 2208 MB/s 4011 MB/s 3291 MB/s
535 * 512 KB = 1908 MB/s 3399 MB/s 2721 MB/s
536 * 256 KB = 1625 MB/s 2679 MB/s 2251 MB/s
537 * 128 KB = 1413 MB/s 1967 MB/s 1684 MB/s
538 * 64 KB = 1152 MB/s 1409 MB/s 1265 MB/s
539 * 32 KB = 726 MB/s 815 MB/s 783 MB/s
540 * 16 KB = 683 MB/s 475 MB/s
541 * 8 KB = 294 MB/s 286 MB/s
542 * 4 KB = 145 MB/s 156 MB/s 149 MB/s
543 *
544 */
545 if (S_ISREG(kstat->mode))
546 kstat->blksize = _1M;
547 else if (S_ISDIR(kstat->mode))
548 /** @todo this may need more tuning after we rewrite the directory handling. */
549 kstat->blksize = _16K;
550 }
551 return rc;
552}
553
554int sf_setattr(struct dentry *dentry, struct iattr *iattr)
555{
556 struct vbsf_super_info *sf_g;
557 struct sf_inode_info *sf_i;
558 union SetAttrReqs
559 {
560 VBOXSFCREATEREQ Create;
561 VBOXSFOBJINFOREQ Info;
562 VBOXSFSETFILESIZEREQ SetSize;
563 VBOXSFCLOSEREQ Close;
564 } *pReq;
565 size_t cbReq;
566 SHFLCREATEPARMS *pCreateParms;
567 SHFLFSOBJINFO *pInfo;
568 SHFLHANDLE hHostFile;
569 int vrc;
570 int err = 0;
571
572 TRACE();
573
574 sf_g = VBSF_GET_SUPER_INFO(dentry->d_inode->i_sb);
575 sf_i = GET_INODE_INFO(dentry->d_inode);
576 cbReq = RT_MAX(sizeof(pReq->Info), sizeof(pReq->Create) + SHFLSTRING_HEADER_SIZE + sf_i->path->u16Size);
577 pReq = (union SetAttrReqs *)VbglR0PhysHeapAlloc(cbReq);
578 if (!pReq) {
579 LogFunc(("Failed to allocate %#x byte request buffer!\n", cbReq));
580 return -ENOMEM;
581 }
582 pCreateParms = &pReq->Create.CreateParms;
583 pInfo = &pReq->Info.ObjInfo;
584
585 RT_ZERO(*pCreateParms);
586 pCreateParms->Handle = SHFL_HANDLE_NIL;
587 pCreateParms->CreateFlags = SHFL_CF_ACT_OPEN_IF_EXISTS
588 | SHFL_CF_ACT_FAIL_IF_NEW
589 | SHFL_CF_ACCESS_ATTR_WRITE;
590
591 /* this is at least required for Posix hosts */
592 if (iattr->ia_valid & ATTR_SIZE)
593 pCreateParms->CreateFlags |= SHFL_CF_ACCESS_WRITE;
594
595 memcpy(&pReq->Create.StrPath, sf_i->path, SHFLSTRING_HEADER_SIZE + sf_i->path->u16Size);
596 vrc = VbglR0SfHostReqCreate(sf_g->map.root, &pReq->Create);
597 if (RT_SUCCESS(vrc)) {
598 hHostFile = pCreateParms->Handle;
599 } else {
600 err = -RTErrConvertToErrno(vrc);
601 LogFunc(("VbglR0SfCreate(%s) failed vrc=%Rrc err=%d\n", sf_i->path->String.ach, vrc, err));
602 goto fail2;
603 }
604 if (pCreateParms->Result != SHFL_FILE_EXISTS) {
605 LogFunc(("file %s does not exist\n", sf_i->path->String.utf8));
606 err = -ENOENT;
607 goto fail1;
608 }
609
610 /* Setting the file size and setting the other attributes has to be
611 * handled separately, see implementation of vbsfSetFSInfo() in
612 * vbsf.cpp */
613 if (iattr->ia_valid & (ATTR_MODE | ATTR_ATIME | ATTR_MTIME)) {
614# define mode_set(r) ((iattr->ia_mode & (S_##r)) ? RTFS_UNIX_##r : 0)
615
616 RT_ZERO(*pInfo);
617 if (iattr->ia_valid & ATTR_MODE) {
618 pInfo->Attr.fMode = mode_set(IRUSR);
619 pInfo->Attr.fMode |= mode_set(IWUSR);
620 pInfo->Attr.fMode |= mode_set(IXUSR);
621 pInfo->Attr.fMode |= mode_set(IRGRP);
622 pInfo->Attr.fMode |= mode_set(IWGRP);
623 pInfo->Attr.fMode |= mode_set(IXGRP);
624 pInfo->Attr.fMode |= mode_set(IROTH);
625 pInfo->Attr.fMode |= mode_set(IWOTH);
626 pInfo->Attr.fMode |= mode_set(IXOTH);
627
628 if (iattr->ia_mode & S_IFDIR)
629 pInfo->Attr.fMode |= RTFS_TYPE_DIRECTORY;
630 else
631 pInfo->Attr.fMode |= RTFS_TYPE_FILE;
632 }
633
634 if (iattr->ia_valid & ATTR_ATIME)
635 sf_timespec_from_ftime(&pInfo->AccessTime,
636 &iattr->ia_atime);
637 if (iattr->ia_valid & ATTR_MTIME)
638 sf_timespec_from_ftime(&pInfo->ModificationTime,
639 &iattr->ia_mtime);
640 /* ignore ctime (inode change time) as it can't be set from userland anyway */
641
642 vrc = VbglR0SfHostReqSetObjInfo(sf_g->map.root, &pReq->Info, hHostFile);
643 if (RT_FAILURE(vrc)) {
644 err = -RTErrConvertToErrno(vrc);
645 LogFunc(("VbglR0SfHostReqSetObjInfo(%s) failed vrc=%Rrc err=%d\n", sf_i->path->String.ach, vrc, err));
646 goto fail1;
647 }
648 }
649
650 if (iattr->ia_valid & ATTR_SIZE) {
651 vrc = VbglR0SfHostReqSetFileSize(sf_g->map.root, &pReq->SetSize, hHostFile, iattr->ia_size);
652 /** @todo Implement fallback if host is < 6.0? */
653 if (RT_FAILURE(vrc)) {
654 err = -RTErrConvertToErrno(vrc);
655 LogFunc(("VbglR0SfHostReqSetFileSize(%s, %#llx) failed vrc=%Rrc err=%d\n",
656 sf_i->path->String.ach, (unsigned long long)iattr->ia_size, vrc, err));
657 goto fail1;
658 }
659 }
660
661 vrc = VbglR0SfHostReqClose(sf_g->map.root, &pReq->Close, hHostFile);
662 if (RT_FAILURE(vrc))
663 LogFunc(("VbglR0SfHostReqClose(%s [%#llx]) failed vrc=%Rrc\n", sf_i->path->String.utf8, hHostFile, vrc));
664 VbglR0PhysHeapFree(pReq);
665
666 /** @todo r=bird: I guess we're calling revalidate here to update the inode
667 * info. However, due to the TTL optimization this is not guarenteed to happen.
668 *
669 * Also, we already have accurate stat information on the file, either from the
670 * SHFL_FN_CREATE call or from SHFL_FN_INFORMATION, so there is no need to do a
671 * slow stat()-like operation to retrieve the information again.
672 *
673 * What's more, given that the SHFL_FN_CREATE call succeeded, we know that the
674 * dentry and all its parent entries are valid and could touch their timestamps
675 * extending their TTL (CIFS does that). */
676 return sf_inode_revalidate_worker(dentry, true /*fForced*/);
677
678 fail1:
679 vrc = VbglR0SfHostReqClose(sf_g->map.root, &pReq->Close, hHostFile);
680 if (RT_FAILURE(vrc))
681 LogFunc(("VbglR0SfHostReqClose(%s [%#llx]) failed vrc=%Rrc; err=%d\n", sf_i->path->String.utf8, hHostFile, vrc, err));
682
683 fail2:
684 VbglR0PhysHeapFree(pReq);
685 return err;
686}
687
688#endif /* >= 2.6.0 */
689
690static int sf_make_path(const char *caller, struct sf_inode_info *sf_i,
691 const char *d_name, size_t d_len, SHFLSTRING ** result)
692{
693 size_t path_len, shflstring_len;
694 SHFLSTRING *tmp;
695 uint16_t p_len;
696 uint8_t *p_name;
697 int fRoot = 0;
698
699 TRACE();
700 p_len = sf_i->path->u16Length;
701 p_name = sf_i->path->String.utf8;
702
703 if (p_len == 1 && *p_name == '/') {
704 path_len = d_len + 1;
705 fRoot = 1;
706 } else {
707 /* lengths of constituents plus terminating zero plus slash */
708 path_len = p_len + d_len + 2;
709 if (path_len > 0xffff) {
710 LogFunc(("path too long. caller=%s, path_len=%zu\n",
711 caller, path_len));
712 return -ENAMETOOLONG;
713 }
714 }
715
716 shflstring_len = offsetof(SHFLSTRING, String.utf8) + path_len;
717 tmp = kmalloc(shflstring_len, GFP_KERNEL);
718 if (!tmp) {
719 LogRelFunc(("kmalloc failed, caller=%s\n", caller));
720 return -ENOMEM;
721 }
722 tmp->u16Length = path_len - 1;
723 tmp->u16Size = path_len;
724
725 if (fRoot)
726 memcpy(&tmp->String.utf8[0], d_name, d_len + 1);
727 else {
728 memcpy(&tmp->String.utf8[0], p_name, p_len);
729 tmp->String.utf8[p_len] = '/';
730 memcpy(&tmp->String.utf8[p_len + 1], d_name, d_len);
731 tmp->String.utf8[p_len + 1 + d_len] = '\0';
732 }
733
734 *result = tmp;
735 return 0;
736}
737
738/**
739 * [dentry] contains string encoded in coding system that corresponds
740 * to [sf_g]->nls, we must convert it to UTF8 here and pass down to
741 * [sf_make_path] which will allocate SHFLSTRING and fill it in
742 */
743int sf_path_from_dentry(const char *caller, struct vbsf_super_info *sf_g,
744 struct sf_inode_info *sf_i, struct dentry *dentry,
745 SHFLSTRING ** result)
746{
747 int err;
748 const char *d_name;
749 size_t d_len;
750 const char *name;
751 size_t len = 0;
752
753 TRACE();
754 d_name = dentry->d_name.name;
755 d_len = dentry->d_name.len;
756
757 if (sf_g->nls) {
758 size_t in_len, i, out_bound_len;
759 const char *in;
760 char *out;
761
762 in = d_name;
763 in_len = d_len;
764
765 out_bound_len = PATH_MAX;
766 out = kmalloc(out_bound_len, GFP_KERNEL);
767 name = out;
768
769 for (i = 0; i < d_len; ++i) {
770 /* We renamed the linux kernel wchar_t type to linux_wchar_t in
771 the-linux-kernel.h, as it conflicts with the C++ type of that name. */
772 linux_wchar_t uni;
773 int nb;
774
775 nb = sf_g->nls->char2uni(in, in_len, &uni);
776 if (nb < 0) {
777 LogFunc(("nls->char2uni failed %x %d\n",
778 *in, in_len));
779 err = -EINVAL;
780 goto fail1;
781 }
782 in_len -= nb;
783 in += nb;
784
785#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)
786 nb = utf32_to_utf8(uni, out, out_bound_len);
787#else
788 nb = utf8_wctomb(out, uni, out_bound_len);
789#endif
790 if (nb < 0) {
791 LogFunc(("nls->uni2char failed %x %d\n",
792 uni, out_bound_len));
793 err = -EINVAL;
794 goto fail1;
795 }
796 out_bound_len -= nb;
797 out += nb;
798 len += nb;
799 }
800 if (len >= PATH_MAX - 1) {
801 err = -ENAMETOOLONG;
802 goto fail1;
803 }
804
805 LogFunc(("result(%d) = %.*s\n", len, len, name));
806 *out = 0;
807 } else {
808 name = d_name;
809 len = d_len;
810 }
811
812 err = sf_make_path(caller, sf_i, name, len, result);
813 if (name != d_name)
814 kfree(name);
815
816 return err;
817
818 fail1:
819 kfree(name);
820 return err;
821}
822
823int sf_nlscpy(struct vbsf_super_info *sf_g,
824 char *name, size_t name_bound_len,
825 const unsigned char *utf8_name, size_t utf8_len)
826{
827 if (sf_g->nls) {
828 const char *in;
829 char *out;
830 size_t out_len;
831 size_t out_bound_len;
832 size_t in_bound_len;
833
834 in = utf8_name;
835 in_bound_len = utf8_len;
836
837 out = name;
838 out_len = 0;
839 out_bound_len = name_bound_len;
840
841 while (in_bound_len) {
842 int nb;
843#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)
844 unicode_t uni;
845
846 nb = utf8_to_utf32(in, in_bound_len, &uni);
847#else
848 linux_wchar_t uni;
849
850 nb = utf8_mbtowc(&uni, in, in_bound_len);
851#endif
852 if (nb < 0) {
853 LogFunc(("utf8_mbtowc failed(%s) %x:%d\n",
854 (const char *)utf8_name, *in,
855 in_bound_len));
856 return -EINVAL;
857 }
858 in += nb;
859 in_bound_len -= nb;
860
861 nb = sf_g->nls->uni2char(uni, out, out_bound_len);
862 if (nb < 0) {
863 LogFunc(("nls->uni2char failed(%s) %x:%d\n",
864 utf8_name, uni, out_bound_len));
865 return nb;
866 }
867 out += nb;
868 out_bound_len -= nb;
869 out_len += nb;
870 }
871
872 *out = 0;
873 } else {
874 if (utf8_len + 1 > name_bound_len)
875 return -ENAMETOOLONG;
876
877 memcpy(name, utf8_name, utf8_len + 1);
878 }
879 return 0;
880}
881
882static struct sf_dir_buf *sf_dir_buf_alloc(void)
883{
884 struct sf_dir_buf *b;
885
886 TRACE();
887 b = kmalloc(sizeof(*b), GFP_KERNEL);
888 if (!b) {
889 LogRelFunc(("could not alloc directory buffer\n"));
890 return NULL;
891 }
892 b->buf = kmalloc(DIR_BUFFER_SIZE, GFP_KERNEL);
893 if (!b->buf) {
894 kfree(b);
895 LogRelFunc(("could not alloc directory buffer storage\n"));
896 return NULL;
897 }
898
899 INIT_LIST_HEAD(&b->head);
900 b->cEntries = 0;
901 b->cbUsed = 0;
902 b->cbFree = DIR_BUFFER_SIZE;
903 return b;
904}
905
906static void sf_dir_buf_free(struct sf_dir_buf *b)
907{
908 BUG_ON(!b || !b->buf);
909
910 TRACE();
911 list_del(&b->head);
912 kfree(b->buf);
913 kfree(b);
914}
915
916/**
917 * Free the directory buffer.
918 */
919void sf_dir_info_free(struct sf_dir_info *p)
920{
921 struct list_head *list, *pos, *tmp;
922
923 TRACE();
924 list = &p->info_list;
925 list_for_each_safe(pos, tmp, list) {
926 struct sf_dir_buf *b;
927
928 b = list_entry(pos, struct sf_dir_buf, head);
929 sf_dir_buf_free(b);
930 }
931 kfree(p);
932}
933
934/**
935 * Empty (but not free) the directory buffer.
936 */
937void sf_dir_info_empty(struct sf_dir_info *p)
938{
939 struct list_head *list, *pos, *tmp;
940 TRACE();
941 list = &p->info_list;
942 list_for_each_safe(pos, tmp, list) {
943 struct sf_dir_buf *b;
944 b = list_entry(pos, struct sf_dir_buf, head);
945 b->cEntries = 0;
946 b->cbUsed = 0;
947 b->cbFree = DIR_BUFFER_SIZE;
948 }
949}
950
951/**
952 * Create a new directory buffer descriptor.
953 */
954struct sf_dir_info *sf_dir_info_alloc(void)
955{
956 struct sf_dir_info *p;
957
958 TRACE();
959 p = kmalloc(sizeof(*p), GFP_KERNEL);
960 if (!p) {
961 LogRelFunc(("could not alloc directory info\n"));
962 return NULL;
963 }
964
965 INIT_LIST_HEAD(&p->info_list);
966 return p;
967}
968
969/**
970 * Search for an empty directory content buffer.
971 */
972static struct sf_dir_buf *sf_get_empty_dir_buf(struct sf_dir_info *sf_d)
973{
974 struct list_head *list, *pos;
975
976 list = &sf_d->info_list;
977 list_for_each(pos, list) {
978 struct sf_dir_buf *b;
979
980 b = list_entry(pos, struct sf_dir_buf, head);
981 if (!b)
982 return NULL;
983 else {
984 if (b->cbUsed == 0)
985 return b;
986 }
987 }
988
989 return NULL;
990}
991
992/** @todo r=bird: Why on earth do we read in the entire directory??? This
993 * cannot be healthy for like big directory and such... */
994int sf_dir_read_all(struct vbsf_super_info *sf_g, struct sf_inode_info *sf_i,
995 struct sf_dir_info *sf_d, SHFLHANDLE handle)
996{
997 int err;
998 SHFLSTRING *mask;
999 VBOXSFLISTDIRREQ *pReq = NULL;
1000
1001 TRACE();
1002 err = sf_make_path(__func__, sf_i, "*", 1, &mask);
1003 if (err)
1004 goto fail0;
1005 pReq = (VBOXSFLISTDIRREQ *)VbglR0PhysHeapAlloc(sizeof(*pReq));
1006 if (!pReq)
1007 goto fail1;
1008
1009 for (;;) {
1010 int rc;
1011 struct sf_dir_buf *b;
1012
1013 b = sf_get_empty_dir_buf(sf_d);
1014 if (!b) {
1015 b = sf_dir_buf_alloc();
1016 if (!b) {
1017 err = -ENOMEM;
1018 LogRelFunc(("could not alloc directory buffer\n"));
1019 goto fail2;
1020 }
1021 list_add(&b->head, &sf_d->info_list);
1022 }
1023
1024 rc = VbglR0SfHostReqListDirContig2x(sf_g->map.root, pReq, handle, mask, virt_to_phys(mask),
1025 0 /*fFlags*/, b->buf, virt_to_phys(b->buf), b->cbFree);
1026 if (RT_SUCCESS(rc)) {
1027 b->cEntries += pReq->Parms.c32Entries.u.value32;
1028 b->cbFree -= pReq->Parms.cb32Buffer.u.value32;
1029 b->cbUsed += pReq->Parms.cb32Buffer.u.value32;
1030 } else if (rc == VERR_NO_MORE_FILES) {
1031 break;
1032 } else {
1033 err = -RTErrConvertToErrno(rc);
1034 LogFunc(("VbglR0SfHostReqListDirContig2x failed rc=%Rrc err=%d\n", rc, err));
1035 goto fail2;
1036 }
1037 }
1038 err = 0;
1039
1040 fail2:
1041 VbglR0PhysHeapFree(pReq);
1042 fail1:
1043 kfree(mask);
1044
1045 fail0:
1046 return err;
1047}
1048
1049
1050/**
1051 * This is called during name resolution/lookup to check if the @a dentry in the
1052 * cache is still valid. The actual validation is job is handled by
1053 * sf_inode_revalidate_worker().
1054 */
1055static int
1056#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
1057sf_dentry_revalidate(struct dentry *dentry, unsigned flags)
1058#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
1059sf_dentry_revalidate(struct dentry *dentry, struct nameidata *nd)
1060#else
1061sf_dentry_revalidate(struct dentry *dentry, int flags)
1062#endif
1063{
1064#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) && LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
1065 int const flags = nd ? nd->flags : 0;
1066#endif
1067
1068 int rc;
1069
1070 Assert(dentry);
1071 SFLOGFLOW(("sf_dentry_revalidate: %p %#x %s\n", dentry, flags, dentry->d_inode ? GET_INODE_INFO(dentry->d_inode)->path->String.ach : "<negative>"));
1072
1073 /*
1074 * See Documentation/filesystems/vfs.txt why we skip LOOKUP_RCU.
1075 *
1076 * Also recommended: https://lwn.net/Articles/649115/
1077 * https://lwn.net/Articles/649729/
1078 * https://lwn.net/Articles/650786/
1079 *
1080 */
1081#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)
1082 if (flags & LOOKUP_RCU) {
1083 rc = -ECHILD;
1084 SFLOGFLOW(("sf_dentry_revalidate: RCU -> -ECHILD\n"));
1085 } else
1086#endif
1087 {
1088 /*
1089 * Do we have an inode or not? If not it's probably a negative cache
1090 * entry, otherwise most likely a positive one.
1091 */
1092 struct inode *pInode = dentry->d_inode;
1093 if (pInode) {
1094 /*
1095 * Positive entry.
1096 *
1097 * Note! We're more aggressive here than other remote file systems,
1098 * current (4.19) CIFS will for instance revalidate the inode
1099 * and ignore the dentry timestamp for positive entries.
1100 */
1101 //struct sf_inode_info *sf_i = GET_INODE_INFO(pInode);
1102 unsigned long const cJiffiesAge = sf_dentry_get_update_jiffies(dentry) - jiffies;
1103 struct vbsf_super_info *sf_g = VBSF_GET_SUPER_INFO(dentry->d_sb);
1104 if (cJiffiesAge < sf_g->ttl) {
1105 SFLOGFLOW(("sf_dentry_revalidate: age: %lu vs. TTL %lu -> 1\n", cJiffiesAge, sf_g->ttl));
1106 rc = 1;
1107 } else if (!sf_inode_revalidate_worker(dentry, true /*fForced*/)) {
1108 sf_dentry_set_update_jiffies(dentry, jiffies); /** @todo get jiffies from inode. */
1109 SFLOGFLOW(("sf_dentry_revalidate: age: %lu vs. TTL %lu -> reval -> 1\n", cJiffiesAge, sf_g->ttl));
1110 rc = 1;
1111 } else {
1112 SFLOGFLOW(("sf_dentry_revalidate: age: %lu vs. TTL %lu -> reval -> 0\n", cJiffiesAge, sf_g->ttl));
1113 rc = 0;
1114 }
1115 } else {
1116 /*
1117 * Negative entry.
1118 *
1119 * Invalidate dentries for open and renames here as we'll revalidate
1120 * these when taking the actual action (also good for case preservation
1121 * if we do case-insensitive mounts against windows + mac hosts at some
1122 * later point).
1123 */
1124#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)
1125 if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
1126#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 75)
1127 if (flags & LOOKUP_CREATE)
1128#else
1129 if (0)
1130#endif
1131 {
1132 SFLOGFLOW(("sf_dentry_revalidate: negative: create or rename target -> 0\n"));
1133 rc = 0;
1134 } else {
1135 /* Can we skip revalidation based on TTL? */
1136 unsigned long const cJiffiesAge = sf_dentry_get_update_jiffies(dentry) - jiffies;
1137 struct vbsf_super_info *sf_g = VBSF_GET_SUPER_INFO(dentry->d_sb);
1138 if (cJiffiesAge < sf_g->ttl) {
1139 SFLOGFLOW(("sf_dentry_revalidate: negative: age: %lu vs. TTL %lu -> 1\n", cJiffiesAge, sf_g->ttl));
1140 rc = 1;
1141 } else {
1142 /* We could revalidate it here, but we could instead just
1143 have the caller kick it out. */
1144 /** @todo stat the direntry and see if it exists now. */
1145 SFLOGFLOW(("sf_dentry_revalidate: negative: age: %lu vs. TTL %lu -> 0\n", cJiffiesAge, sf_g->ttl));
1146 rc = 0;
1147 }
1148 }
1149 }
1150 }
1151 return rc;
1152}
1153
1154#ifdef SFLOG_ENABLED
1155
1156/** For logging purposes only. */
1157# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)
1158static int sf_dentry_delete(const struct dentry *pDirEntry)
1159# else
1160static int sf_dentry_delete(struct dentry *pDirEntry)
1161# endif
1162{
1163 SFLOGFLOW(("sf_dentry_delete: %p\n", pDirEntry));
1164 return 0;
1165}
1166
1167# if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
1168/** For logging purposes only. */
1169static int sf_dentry_init(struct dentry *pDirEntry)
1170{
1171 SFLOGFLOW(("sf_dentry_init: %p\n", pDirEntry));
1172 return 0;
1173}
1174# endif
1175
1176#endif /* SFLOG_ENABLED */
1177
1178/**
1179 * Directory entry operations.
1180 *
1181 * Since 2.6.38 this is used via the super_block::s_d_op member.
1182 */
1183struct dentry_operations sf_dentry_ops = {
1184 .d_revalidate = sf_dentry_revalidate,
1185#ifdef SFLOG_ENABLED
1186 .d_delete = sf_dentry_delete,
1187# if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)
1188 .d_init = sf_dentry_init,
1189# endif
1190#endif
1191};
1192
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