VirtualBox

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

Last change on this file was 104455, checked in by vboxsync, 3 months ago

Additions: Linux: vboxsf: More targeted fix for kernel 6.6 and later versions, bugref:10524.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 48.7 KB
Line 
1/* $Id: utils.c 104455 2024-04-29 14:09:02Z 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-2023 Oracle and/or its affiliates.
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
39int vbsf_nlscpy(struct vbsf_super_info *pSuperInfo, char *name, size_t name_bound_len,
40 const unsigned char *utf8_name, size_t utf8_len)
41{
42 Assert(name_bound_len > 1);
43 Assert(RTStrNLen(utf8_name, utf8_len) == utf8_len);
44
45 if (pSuperInfo->nls) {
46 const char *in = utf8_name;
47 size_t in_bound_len = utf8_len;
48 char *out = name;
49 size_t out_bound_len = name_bound_len - 1;
50
51 while (in_bound_len) {
52#if RTLNX_VER_MIN(2,6,31)
53 unicode_t uni;
54 int cbInEnc = utf8_to_utf32(in, in_bound_len, &uni);
55#else
56 linux_wchar_t uni;
57 int cbInEnc = utf8_mbtowc(&uni, in, in_bound_len);
58#endif
59 if (cbInEnc >= 0) {
60 int cbOutEnc = pSuperInfo->nls->uni2char(uni, out, out_bound_len);
61 if (cbOutEnc >= 0) {
62 /*SFLOG3(("vbsf_nlscpy: cbOutEnc=%d cbInEnc=%d uni=%#x in_bound_len=%u\n", cbOutEnc, cbInEnc, uni, in_bound_len));*/
63 out += cbOutEnc;
64 out_bound_len -= cbOutEnc;
65
66 in += cbInEnc;
67 in_bound_len -= cbInEnc;
68 } else {
69 SFLOG(("vbsf_nlscpy: nls->uni2char failed with %d on %#x (pos %u in '%s'), out_bound_len=%u\n",
70 cbOutEnc, uni, in - (const char *)utf8_name, (const char *)utf8_name, (unsigned)out_bound_len));
71 return cbOutEnc;
72 }
73 } else {
74 SFLOG(("vbsf_nlscpy: utf8_to_utf32/utf8_mbtowc failed with %d on %x (pos %u in '%s'), in_bound_len=%u!\n",
75 cbInEnc, *in, in - (const char *)utf8_name, (const char *)utf8_name, (unsigned)in_bound_len));
76 return -EINVAL;
77 }
78 }
79
80 *out = '\0';
81 } else {
82 if (utf8_len + 1 > name_bound_len)
83 return -ENAMETOOLONG;
84
85 memcpy(name, utf8_name, utf8_len + 1);
86 }
87 return 0;
88}
89
90
91/**
92 * Converts the given NLS string to a host one, kmalloc'ing
93 * the output buffer (use kfree on result).
94 */
95int vbsf_nls_to_shflstring(struct vbsf_super_info *pSuperInfo, const char *pszNls, PSHFLSTRING *ppString)
96{
97 int rc;
98 size_t const cchNls = strlen(pszNls);
99 PSHFLSTRING pString = NULL;
100 if (pSuperInfo->nls) {
101 /*
102 * NLS -> UTF-8 w/ SHLF string header.
103 */
104 /* Calc length first: */
105 size_t cchUtf8 = 0;
106 size_t offNls = 0;
107 while (offNls < cchNls) {
108 linux_wchar_t uc; /* Note! We renamed the type due to clashes. */
109 int const cbNlsCodepoint = pSuperInfo->nls->char2uni(&pszNls[offNls], cchNls - offNls, &uc);
110 if (cbNlsCodepoint >= 0) {
111 char achTmp[16];
112#if RTLNX_VER_MIN(2,6,31)
113 int cbUtf8Codepoint = utf32_to_utf8(uc, achTmp, sizeof(achTmp));
114#else
115 int cbUtf8Codepoint = utf8_wctomb(achTmp, uc, sizeof(achTmp));
116#endif
117 if (cbUtf8Codepoint > 0) {
118 cchUtf8 += cbUtf8Codepoint;
119 offNls += cbNlsCodepoint;
120 } else {
121 Log(("vbsf_nls_to_shflstring: nls->uni2char(%#x) failed: %d\n", uc, cbUtf8Codepoint));
122 return -EINVAL;
123 }
124 } else {
125 Log(("vbsf_nls_to_shflstring: nls->char2uni(%.*Rhxs) failed: %d\n",
126 RT_MIN(8, cchNls - offNls), &pszNls[offNls], cbNlsCodepoint));
127 return -EINVAL;
128 }
129 }
130 if (cchUtf8 + 1 < _64K) {
131 /* Allocate: */
132 pString = (PSHFLSTRING)kmalloc(SHFLSTRING_HEADER_SIZE + cchUtf8 + 1, GFP_KERNEL);
133 if (pString) {
134 char *pchDst = pString->String.ach;
135 pString->u16Length = (uint16_t)cchUtf8;
136 pString->u16Size = (uint16_t)(cchUtf8 + 1);
137
138 /* Do the conversion (cchUtf8 is counted down): */
139 rc = 0;
140 offNls = 0;
141 while (offNls < cchNls) {
142 linux_wchar_t uc; /* Note! We renamed the type due to clashes. */
143 int const cbNlsCodepoint = pSuperInfo->nls->char2uni(&pszNls[offNls], cchNls - offNls, &uc);
144 if (cbNlsCodepoint >= 0) {
145#if RTLNX_VER_MIN(2,6,31)
146 int cbUtf8Codepoint = utf32_to_utf8(uc, pchDst, cchUtf8);
147#else
148 int cbUtf8Codepoint = utf8_wctomb(pchDst, uc, cchUtf8);
149#endif
150 if (cbUtf8Codepoint > 0) {
151 AssertBreakStmt(cbUtf8Codepoint <= cchUtf8, rc = -EINVAL);
152 cchUtf8 -= cbUtf8Codepoint;
153 pchDst += cbUtf8Codepoint;
154 offNls += cbNlsCodepoint;
155 } else {
156 Log(("vbsf_nls_to_shflstring: nls->uni2char(%#x) failed! %d, cchUtf8=%zu\n",
157 uc, cbUtf8Codepoint, cchUtf8));
158 rc = -EINVAL;
159 break;
160 }
161 } else {
162 Log(("vbsf_nls_to_shflstring: nls->char2uni(%.*Rhxs) failed! %d\n",
163 RT_MIN(8, cchNls - offNls), &pszNls[offNls], cbNlsCodepoint));
164 rc = -EINVAL;
165 break;
166 }
167 }
168 if (rc == 0) {
169 /*
170 * Succeeded. Just terminate the string and we're good.
171 */
172 Assert(pchDst - pString->String.ach == pString->u16Length);
173 *pchDst = '\0';
174 } else {
175 kfree(pString);
176 pString = NULL;
177 }
178 } else {
179 Log(("vbsf_nls_to_shflstring: failed to allocate %u bytes\n", SHFLSTRING_HEADER_SIZE + cchUtf8 + 1));
180 rc = -ENOMEM;
181 }
182 } else {
183 Log(("vbsf_nls_to_shflstring: too long: %zu bytes (%zu nls bytes)\n", cchUtf8, cchNls));
184 rc = -ENAMETOOLONG;
185 }
186 } else {
187 /*
188 * UTF-8 -> UTF-8 w/ SHLF string header.
189 */
190 if (cchNls + 1 < _64K) {
191 pString = (PSHFLSTRING)kmalloc(SHFLSTRING_HEADER_SIZE + cchNls + 1, GFP_KERNEL);
192 if (pString) {
193 char *pchDst = pString->String.ach;
194 pString->u16Length = (uint16_t)cchNls;
195 pString->u16Size = (uint16_t)(cchNls + 1);
196 RT_BCOPY_UNFORTIFIED(pchDst, pszNls, cchNls);
197 pchDst[cchNls] = '\0';
198 rc = 0;
199 } else {
200 Log(("vbsf_nls_to_shflstring: failed to allocate %u bytes\n", SHFLSTRING_HEADER_SIZE + cchNls + 1));
201 rc = -ENOMEM;
202 }
203 } else {
204 Log(("vbsf_nls_to_shflstring: too long: %zu bytes\n", cchNls));
205 rc = -ENAMETOOLONG;
206 }
207 }
208 *ppString = pString;
209 return rc;
210}
211
212
213/**
214 * Convert from VBox to linux time.
215 */
216#if RTLNX_VER_MAX(2,6,0)
217DECLINLINE(void) vbsf_time_to_linux(time_t *pLinuxDst, PCRTTIMESPEC pVBoxSrc)
218{
219 int64_t t = RTTimeSpecGetNano(pVBoxSrc);
220 do_div(t, RT_NS_1SEC);
221 *pLinuxDst = t;
222}
223#else /* >= 2.6.0 */
224# if RTLNX_VER_MAX(4,18,0)
225DECLINLINE(void) vbsf_time_to_linux(struct timespec *pLinuxDst, PCRTTIMESPEC pVBoxSrc)
226# else
227DECLINLINE(void) vbsf_time_to_linux(struct timespec64 *pLinuxDst, PCRTTIMESPEC pVBoxSrc)
228# endif
229{
230 int64_t t = RTTimeSpecGetNano(pVBoxSrc);
231 pLinuxDst->tv_nsec = do_div(t, RT_NS_1SEC);
232 pLinuxDst->tv_sec = t;
233}
234#endif /* >= 2.6.0 */
235
236
237/**
238 * Convert from linux to VBox time.
239 */
240#if RTLNX_VER_MAX(2,6,0)
241DECLINLINE(void) vbsf_time_to_vbox(PRTTIMESPEC pVBoxDst, time_t *pLinuxSrc)
242{
243 RTTimeSpecSetNano(pVBoxDst, RT_NS_1SEC_64 * *pLinuxSrc);
244}
245#else /* >= 2.6.0 */
246# if RTLNX_VER_MAX(4,18,0)
247DECLINLINE(void) vbsf_time_to_vbox(PRTTIMESPEC pVBoxDst, struct timespec const *pLinuxSrc)
248# else
249DECLINLINE(void) vbsf_time_to_vbox(PRTTIMESPEC pVBoxDst, struct timespec64 const *pLinuxSrc)
250# endif
251{
252 RTTimeSpecSetNano(pVBoxDst, pLinuxSrc->tv_nsec + pLinuxSrc->tv_sec * (int64_t)RT_NS_1SEC);
253}
254#endif /* >= 2.6.0 */
255
256
257/**
258 * Converts VBox access permissions to Linux ones (mode & 0777).
259 *
260 * @note Currently identical.
261 * @sa sf_access_permissions_to_vbox
262 */
263DECLINLINE(int) sf_access_permissions_to_linux(uint32_t fAttr)
264{
265 /* Access bits should be the same: */
266 AssertCompile(RTFS_UNIX_IRUSR == S_IRUSR);
267 AssertCompile(RTFS_UNIX_IWUSR == S_IWUSR);
268 AssertCompile(RTFS_UNIX_IXUSR == S_IXUSR);
269 AssertCompile(RTFS_UNIX_IRGRP == S_IRGRP);
270 AssertCompile(RTFS_UNIX_IWGRP == S_IWGRP);
271 AssertCompile(RTFS_UNIX_IXGRP == S_IXGRP);
272 AssertCompile(RTFS_UNIX_IROTH == S_IROTH);
273 AssertCompile(RTFS_UNIX_IWOTH == S_IWOTH);
274 AssertCompile(RTFS_UNIX_IXOTH == S_IXOTH);
275
276 return fAttr & RTFS_UNIX_ALL_ACCESS_PERMS;
277}
278
279
280/**
281 * Produce the Linux mode mask, given VBox, mount options and file type.
282 */
283DECLINLINE(int) sf_file_mode_to_linux(uint32_t fVBoxMode, int fFixedMode, int fClearMask, int fType)
284{
285 int fLnxMode = sf_access_permissions_to_linux(fVBoxMode);
286 if (fFixedMode != ~0)
287 fLnxMode = fFixedMode & 0777;
288 fLnxMode &= ~fClearMask;
289 fLnxMode |= fType;
290 return fLnxMode;
291}
292
293/**
294 * Update inode timestamps.
295 *
296 * @param pInode Linux inode object.
297 * @param pObjInfo VBox vboxsf object.
298 */
299static void vbsf_update_inode_timestamps(struct inode *pInode, PSHFLFSOBJINFO pObjInfo)
300{
301#if RTLNX_VER_MIN(6,7,0)
302 struct timespec64 tsAccessTime, tsChangeTime, ModificationTime;
303
304 vbsf_time_to_linux(&tsAccessTime, &pObjInfo->AccessTime);
305 vbsf_time_to_linux(&tsChangeTime, &pObjInfo->ChangeTime);
306 vbsf_time_to_linux(&ModificationTime, &pObjInfo->ModificationTime);
307
308 inode_set_atime_to_ts(pInode, tsAccessTime);
309 inode_set_ctime_to_ts(pInode, tsChangeTime);
310 inode_set_mtime_to_ts(pInode, ModificationTime);
311
312# elif RTLNX_VER_MIN(6,6,0)
313 vbsf_time_to_linux(&pInode->i_atime, &pObjInfo->AccessTime);
314 vbsf_time_to_linux(&pInode->__i_ctime, &pObjInfo->ChangeTime);
315 vbsf_time_to_linux(&pInode->i_mtime, &pObjInfo->ModificationTime);
316#else
317 vbsf_time_to_linux(&pInode->i_atime, &pObjInfo->AccessTime);
318 vbsf_time_to_linux(&pInode->i_ctime, &pObjInfo->ChangeTime);
319 vbsf_time_to_linux(&pInode->i_mtime, &pObjInfo->ModificationTime);
320#endif
321}
322
323/**
324 * Initializes the @a inode attributes based on @a pObjInfo and @a pSuperInfo
325 * options.
326 */
327void vbsf_init_inode(struct inode *inode, struct vbsf_inode_info *sf_i, PSHFLFSOBJINFO pObjInfo,
328 struct vbsf_super_info *pSuperInfo)
329{
330 PCSHFLFSOBJATTR pAttr = &pObjInfo->Attr;
331
332 TRACE();
333
334 sf_i->ts_up_to_date = jiffies;
335 sf_i->force_restat = 0;
336
337 if (RTFS_IS_DIRECTORY(pAttr->fMode)) {
338 inode->i_mode = sf_file_mode_to_linux(pAttr->fMode, pSuperInfo->dmode, pSuperInfo->dmask, S_IFDIR);
339 inode->i_op = &vbsf_dir_iops;
340 inode->i_fop = &vbsf_dir_fops;
341
342 /* XXX: this probably should be set to the number of entries
343 in the directory plus two (. ..) */
344 set_nlink(inode, 1);
345 }
346 else if (RTFS_IS_SYMLINK(pAttr->fMode)) {
347 /** @todo r=bird: Aren't System V symlinks w/o any mode mask? IIRC there is
348 * no lchmod on Linux. */
349 inode->i_mode = sf_file_mode_to_linux(pAttr->fMode, pSuperInfo->fmode, pSuperInfo->fmask, S_IFLNK);
350 inode->i_op = &vbsf_lnk_iops;
351 set_nlink(inode, 1);
352 } else {
353 inode->i_mode = sf_file_mode_to_linux(pAttr->fMode, pSuperInfo->fmode, pSuperInfo->fmask, S_IFREG);
354 inode->i_op = &vbsf_reg_iops;
355 inode->i_fop = &vbsf_reg_fops;
356 inode->i_mapping->a_ops = &vbsf_reg_aops;
357#if RTLNX_VER_RANGE(2,5,17, 4,0,0)
358 inode->i_mapping->backing_dev_info = &pSuperInfo->bdi; /* This is needed for mmap. */
359#endif
360 set_nlink(inode, 1);
361 }
362
363#if RTLNX_VER_MIN(3,5,0)
364 inode->i_uid = make_kuid(current_user_ns(), pSuperInfo->uid);
365 inode->i_gid = make_kgid(current_user_ns(), pSuperInfo->gid);
366#else
367 inode->i_uid = pSuperInfo->uid;
368 inode->i_gid = pSuperInfo->gid;
369#endif
370
371 inode->i_size = pObjInfo->cbObject;
372#if RTLNX_VER_MAX(2,6,19) && !defined(KERNEL_FC6)
373 inode->i_blksize = 4096;
374#endif
375#if RTLNX_VER_MIN(2,4,11)
376 inode->i_blkbits = 12;
377#endif
378 /* i_blocks always in units of 512 bytes! */
379 inode->i_blocks = (pObjInfo->cbAllocated + 511) / 512;
380
381 vbsf_update_inode_timestamps(inode, pObjInfo);
382
383 sf_i->BirthTime = pObjInfo->BirthTime;
384 sf_i->ModificationTime = pObjInfo->ModificationTime;
385 RTTimeSpecSetSeconds(&sf_i->ModificationTimeAtOurLastWrite, 0);
386}
387
388
389/**
390 * Update the inode with new object info from the host.
391 *
392 * Called by sf_inode_revalidate() and sf_inode_revalidate_with_handle().
393 */
394void vbsf_update_inode(struct inode *pInode, struct vbsf_inode_info *pInodeInfo, PSHFLFSOBJINFO pObjInfo,
395 struct vbsf_super_info *pSuperInfo, bool fInodeLocked, unsigned fSetAttrs)
396{
397 PCSHFLFSOBJATTR pAttr = &pObjInfo->Attr;
398 int fMode;
399
400 TRACE();
401
402#if RTLNX_VER_MIN(4,5,0)
403 if (!fInodeLocked)
404 inode_lock(pInode);
405#endif
406
407 /*
408 * Calc new mode mask and update it if it changed.
409 */
410 if (RTFS_IS_DIRECTORY(pAttr->fMode))
411 fMode = sf_file_mode_to_linux(pAttr->fMode, pSuperInfo->dmode, pSuperInfo->dmask, S_IFDIR);
412 else if (RTFS_IS_SYMLINK(pAttr->fMode))
413 /** @todo r=bird: Aren't System V symlinks w/o any mode mask? IIRC there is
414 * no lchmod on Linux. */
415 fMode = sf_file_mode_to_linux(pAttr->fMode, pSuperInfo->fmode, pSuperInfo->fmask, S_IFLNK);
416 else
417 fMode = sf_file_mode_to_linux(pAttr->fMode, pSuperInfo->fmode, pSuperInfo->fmask, S_IFREG);
418
419 if (fMode == pInode->i_mode) {
420 /* likely */
421 } else {
422 if ((fMode & S_IFMT) == (pInode->i_mode & S_IFMT))
423 pInode->i_mode = fMode;
424 else {
425 SFLOGFLOW(("vbsf_update_inode: Changed from %o to %o (%s)\n",
426 pInode->i_mode & S_IFMT, fMode & S_IFMT, pInodeInfo->path->String.ach));
427 /** @todo we probably need to be more drastic... */
428 vbsf_init_inode(pInode, pInodeInfo, pObjInfo, pSuperInfo);
429
430#if RTLNX_VER_MIN(4,5,0)
431 if (!fInodeLocked)
432 inode_unlock(pInode);
433#endif
434 return;
435 }
436 }
437
438 /*
439 * Update the sizes.
440 * Note! i_blocks is always in units of 512 bytes!
441 */
442 pInode->i_blocks = (pObjInfo->cbAllocated + 511) / 512;
443 i_size_write(pInode, pObjInfo->cbObject);
444
445 /*
446 * Update the timestamps.
447 */
448 vbsf_update_inode_timestamps(pInode, pObjInfo);
449 pInodeInfo->BirthTime = pObjInfo->BirthTime;
450
451 /*
452 * Mark it as up to date.
453 * Best to do this before we start with any expensive map invalidation.
454 */
455 pInodeInfo->ts_up_to_date = jiffies;
456 pInodeInfo->force_restat = 0;
457
458 /*
459 * If the modification time changed, we may have to invalidate the page
460 * cache pages associated with this inode if we suspect the change was
461 * made by the host. How supicious we are depends on the cache mode.
462 *
463 * Note! The invalidate_inode_pages() call is pretty weak. It will _not_
464 * touch pages that are already mapped into an address space, but it
465 * will help if the file isn't currently mmap'ed or if we're in read
466 * or read/write caching mode.
467 */
468 if (!RTTimeSpecIsEqual(&pInodeInfo->ModificationTime, &pObjInfo->ModificationTime)) {
469 if (RTFS_IS_FILE(pAttr->fMode)) {
470 if (!(fSetAttrs & (ATTR_MTIME | ATTR_SIZE))) {
471 bool fInvalidate;
472 if (pSuperInfo->enmCacheMode == kVbsfCacheMode_None) {
473 fInvalidate = true; /* No-caching: always invalidate. */
474 } else {
475 if (RTTimeSpecIsEqual(&pInodeInfo->ModificationTimeAtOurLastWrite, &pInodeInfo->ModificationTime)) {
476 fInvalidate = false; /* Could be our write, so don't invalidate anything */
477 RTTimeSpecSetSeconds(&pInodeInfo->ModificationTimeAtOurLastWrite, 0);
478 } else {
479 /*RTLogBackdoorPrintf("vbsf_update_inode: Invalidating the mapping %s - %RU64 vs %RU64 vs %RU64 - %#x\n",
480 pInodeInfo->path->String.ach,
481 RTTimeSpecGetNano(&pInodeInfo->ModificationTimeAtOurLastWrite),
482 RTTimeSpecGetNano(&pInodeInfo->ModificationTime),
483 RTTimeSpecGetNano(&pObjInfo->ModificationTime), fSetAttrs);*/
484 fInvalidate = true; /* We haven't modified the file recently, so probably a host update. */
485 }
486 }
487 pInodeInfo->ModificationTime = pObjInfo->ModificationTime;
488
489 if (fInvalidate) {
490 struct address_space *mapping = pInode->i_mapping;
491 if (mapping && mapping->nrpages > 0) {
492 SFLOGFLOW(("vbsf_update_inode: Invalidating the mapping %s (%#x)\n", pInodeInfo->path->String.ach, fSetAttrs));
493#if RTLNX_VER_MIN(2,6,34)
494 invalidate_mapping_pages(mapping, 0, ~(pgoff_t)0);
495#elif RTLNX_VER_MIN(2,5,41)
496 invalidate_inode_pages(mapping);
497#else
498 invalidate_inode_pages(pInode);
499#endif
500 }
501 }
502 } else {
503 RTTimeSpecSetSeconds(&pInodeInfo->ModificationTimeAtOurLastWrite, 0);
504 pInodeInfo->ModificationTime = pObjInfo->ModificationTime;
505 }
506 } else
507 pInodeInfo->ModificationTime = pObjInfo->ModificationTime;
508 }
509
510 /*
511 * Done.
512 */
513#if RTLNX_VER_MIN(4,5,0)
514 if (!fInodeLocked)
515 inode_unlock(pInode);
516#endif
517}
518
519
520/** @note Currently only used for the root directory during (re-)mount. */
521int vbsf_stat(const char *caller, struct vbsf_super_info *pSuperInfo, SHFLSTRING *path, PSHFLFSOBJINFO result, int ok_to_fail)
522{
523 int rc;
524 VBOXSFCREATEREQ *pReq;
525 NOREF(caller);
526
527 TRACE();
528
529 pReq = (VBOXSFCREATEREQ *)VbglR0PhysHeapAlloc(sizeof(*pReq) + path->u16Size);
530 if (pReq) {
531 RT_ZERO(*pReq);
532 RT_BCOPY_UNFORTIFIED(&pReq->StrPath, path, SHFLSTRING_HEADER_SIZE + path->u16Size);
533 pReq->CreateParms.Handle = SHFL_HANDLE_NIL;
534 pReq->CreateParms.CreateFlags = SHFL_CF_LOOKUP | SHFL_CF_ACT_FAIL_IF_NEW;
535
536 LogFunc(("Calling VbglR0SfHostReqCreate on %s\n", path->String.utf8));
537 rc = VbglR0SfHostReqCreate(pSuperInfo->map.root, pReq);
538 if (RT_SUCCESS(rc)) {
539 if (pReq->CreateParms.Result == SHFL_FILE_EXISTS) {
540 *result = pReq->CreateParms.Info;
541 rc = 0;
542 } else {
543 if (!ok_to_fail)
544 LogFunc(("VbglR0SfHostReqCreate on %s: file does not exist: %d (caller=%s)\n",
545 path->String.utf8, pReq->CreateParms.Result, caller));
546 rc = -ENOENT;
547 }
548 } else if (rc == VERR_INVALID_NAME) {
549 rc = -ENOENT; /* this can happen for names like 'foo*' on a Windows host */
550 } else {
551 LogFunc(("VbglR0SfHostReqCreate failed on %s: %Rrc (caller=%s)\n", path->String.utf8, rc, caller));
552 rc = -EPROTO;
553 }
554 VbglR0PhysHeapFree(pReq);
555 }
556 else
557 rc = -ENOMEM;
558 return rc;
559}
560
561
562/**
563 * Revalidate an inode, inner worker.
564 *
565 * @sa sf_inode_revalidate()
566 */
567int vbsf_inode_revalidate_worker(struct dentry *dentry, bool fForced, bool fInodeLocked)
568{
569 int rc;
570 struct inode *pInode = dentry ? dentry->d_inode : NULL;
571 if (pInode) {
572 struct vbsf_inode_info *sf_i = VBSF_GET_INODE_INFO(pInode);
573 struct vbsf_super_info *pSuperInfo = VBSF_GET_SUPER_INFO(pInode->i_sb);
574 AssertReturn(sf_i, -EINVAL);
575 AssertReturn(pSuperInfo, -EINVAL);
576
577 /*
578 * Can we get away without any action here?
579 */
580 if ( !fForced
581 && !sf_i->force_restat
582 && jiffies - sf_i->ts_up_to_date < pSuperInfo->cJiffiesInodeTTL)
583 rc = 0;
584 else {
585 /*
586 * No, we have to query the file info from the host.
587 * Try get a handle we can query, any kind of handle will do here.
588 */
589 struct vbsf_handle *pHandle = vbsf_handle_find(sf_i, 0, 0);
590 if (pHandle) {
591 /* Query thru pHandle. */
592 VBOXSFOBJINFOREQ *pReq = (VBOXSFOBJINFOREQ *)VbglR0PhysHeapAlloc(sizeof(*pReq));
593 if (pReq) {
594 RT_ZERO(*pReq);
595 rc = VbglR0SfHostReqQueryObjInfo(pSuperInfo->map.root, pReq, pHandle->hHost);
596 if (RT_SUCCESS(rc)) {
597 /*
598 * Reset the TTL and copy the info over into the inode structure.
599 */
600 vbsf_update_inode(pInode, sf_i, &pReq->ObjInfo, pSuperInfo, fInodeLocked, 0 /*fSetAttrs*/);
601 } else if (rc == VERR_INVALID_HANDLE) {
602 rc = -ENOENT; /* Restore.*/
603 } else {
604 LogFunc(("VbglR0SfHostReqQueryObjInfo failed on %#RX64: %Rrc\n", pHandle->hHost, rc));
605 rc = -RTErrConvertToErrno(rc);
606 }
607 VbglR0PhysHeapFree(pReq);
608 } else
609 rc = -ENOMEM;
610 vbsf_handle_release(pHandle, pSuperInfo, "vbsf_inode_revalidate_worker");
611
612 } else {
613 /* Query via path. */
614 SHFLSTRING *pPath = sf_i->path;
615 VBOXSFCREATEREQ *pReq = (VBOXSFCREATEREQ *)VbglR0PhysHeapAlloc(sizeof(*pReq) + pPath->u16Size);
616 if (pReq) {
617 RT_ZERO(*pReq);
618 RT_BCOPY_UNFORTIFIED(&pReq->StrPath, pPath, SHFLSTRING_HEADER_SIZE + pPath->u16Size);
619 pReq->CreateParms.Handle = SHFL_HANDLE_NIL;
620 pReq->CreateParms.CreateFlags = SHFL_CF_LOOKUP | SHFL_CF_ACT_FAIL_IF_NEW;
621
622 rc = VbglR0SfHostReqCreate(pSuperInfo->map.root, pReq);
623 if (RT_SUCCESS(rc)) {
624 if (pReq->CreateParms.Result == SHFL_FILE_EXISTS) {
625 /*
626 * Reset the TTL and copy the info over into the inode structure.
627 */
628 vbsf_update_inode(pInode, sf_i, &pReq->CreateParms.Info, pSuperInfo, fInodeLocked, 0 /*fSetAttrs*/);
629 rc = 0;
630 } else {
631 rc = -ENOENT;
632 }
633 } else if (rc == VERR_INVALID_NAME) {
634 rc = -ENOENT; /* this can happen for names like 'foo*' on a Windows host */
635 } else {
636 LogFunc(("VbglR0SfHostReqCreate failed on %s: %Rrc\n", pPath->String.ach, rc));
637 rc = -EPROTO;
638 }
639 VbglR0PhysHeapFree(pReq);
640 }
641 else
642 rc = -ENOMEM;
643 }
644 }
645 } else {
646 LogFunc(("no dentry(%p) or inode(%p)\n", dentry, pInode));
647 rc = -EINVAL;
648 }
649 return rc;
650}
651
652
653#if RTLNX_VER_MAX(2,5,18)
654/**
655 * Revalidate an inode for 2.4.
656 *
657 * This is called in the stat(), lstat() and readlink() code paths. In the stat
658 * cases the caller will use the result afterwards to produce the stat data.
659 *
660 * @note 2.4.x has a getattr() inode operation too, but it is not used.
661 */
662int vbsf_inode_revalidate(struct dentry *dentry)
663{
664 /*
665 * We pretend the inode is locked here, as 2.4.x does not have inode level locking.
666 */
667 return vbsf_inode_revalidate_worker(dentry, false /*fForced*/, true /*fInodeLocked*/);
668}
669#endif /* < 2.5.18 */
670
671
672/**
673 * Similar to sf_inode_revalidate, but uses associated host file handle as that
674 * is quite a bit faster.
675 */
676int vbsf_inode_revalidate_with_handle(struct dentry *dentry, SHFLHANDLE hHostFile, bool fForced, bool fInodeLocked)
677{
678 int err;
679 struct inode *pInode = dentry ? dentry->d_inode : NULL;
680 if (!pInode) {
681 LogFunc(("no dentry(%p) or inode(%p)\n", dentry, pInode));
682 err = -EINVAL;
683 } else {
684 struct vbsf_inode_info *sf_i = VBSF_GET_INODE_INFO(pInode);
685 struct vbsf_super_info *pSuperInfo = VBSF_GET_SUPER_INFO(pInode->i_sb);
686 AssertReturn(sf_i, -EINVAL);
687 AssertReturn(pSuperInfo, -EINVAL);
688
689 /*
690 * Can we get away without any action here?
691 */
692 if ( !fForced
693 && !sf_i->force_restat
694 && jiffies - sf_i->ts_up_to_date < pSuperInfo->cJiffiesInodeTTL)
695 err = 0;
696 else {
697 /*
698 * No, we have to query the file info from the host.
699 */
700 VBOXSFOBJINFOREQ *pReq = (VBOXSFOBJINFOREQ *)VbglR0PhysHeapAlloc(sizeof(*pReq));
701 if (pReq) {
702 RT_ZERO(*pReq);
703 err = VbglR0SfHostReqQueryObjInfo(pSuperInfo->map.root, pReq, hHostFile);
704 if (RT_SUCCESS(err)) {
705 /*
706 * Reset the TTL and copy the info over into the inode structure.
707 */
708 vbsf_update_inode(pInode, sf_i, &pReq->ObjInfo, pSuperInfo, fInodeLocked, 0 /*fSetAttrs*/);
709 } else {
710 LogFunc(("VbglR0SfHostReqQueryObjInfo failed on %#RX64: %Rrc\n", hHostFile, err));
711 err = -RTErrConvertToErrno(err);
712 }
713 VbglR0PhysHeapFree(pReq);
714 } else
715 err = -ENOMEM;
716 }
717 }
718 return err;
719}
720
721
722/* on 2.6 this is a proxy for [sf_inode_revalidate] which (as a side
723 effect) updates inode attributes for [dentry] (given that [dentry]
724 has inode at all) from these new attributes we derive [kstat] via
725 [generic_fillattr] */
726#if RTLNX_VER_MIN(2,5,18)
727# if RTLNX_VER_MIN(6,3,0)
728int vbsf_inode_getattr(struct mnt_idmap *idmap, const struct path *path,
729 struct kstat *kstat, u32 request_mask, unsigned int flags)
730# elif RTLNX_VER_MIN(5,12,0)
731int vbsf_inode_getattr(struct user_namespace *ns, const struct path *path,
732 struct kstat *kstat, u32 request_mask, unsigned int flags)
733# elif RTLNX_VER_MIN(4,11,0)
734int vbsf_inode_getattr(const struct path *path, struct kstat *kstat, u32 request_mask, unsigned int flags)
735# else
736int vbsf_inode_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *kstat)
737# endif
738{
739 int rc;
740# if RTLNX_VER_MIN(4,11,0)
741 struct dentry *dentry = path->dentry;
742# endif
743
744# if RTLNX_VER_MIN(4,11,0)
745 SFLOGFLOW(("vbsf_inode_getattr: dentry=%p request_mask=%#x flags=%#x\n", dentry, request_mask, flags));
746# else
747 SFLOGFLOW(("vbsf_inode_getattr: dentry=%p\n", dentry));
748# endif
749
750# if RTLNX_VER_MIN(4,11,0)
751 /*
752 * With the introduction of statx() userland can control whether we
753 * update the inode information or not.
754 */
755 switch (flags & AT_STATX_SYNC_TYPE) {
756 default:
757 rc = vbsf_inode_revalidate_worker(dentry, false /*fForced*/, false /*fInodeLocked*/);
758 break;
759
760 case AT_STATX_FORCE_SYNC:
761 rc = vbsf_inode_revalidate_worker(dentry, true /*fForced*/, false /*fInodeLocked*/);
762 break;
763
764 case AT_STATX_DONT_SYNC:
765 rc = 0;
766 break;
767 }
768# else
769 rc = vbsf_inode_revalidate_worker(dentry, false /*fForced*/, false /*fInodeLocked*/);
770# endif
771 if (rc == 0) {
772 /* Do generic filling in of info. */
773# if RTLNX_VER_MIN(6,6,0)
774 generic_fillattr(idmap, request_mask, dentry->d_inode, kstat);
775# elif RTLNX_VER_MIN(6,3,0)
776 generic_fillattr(idmap, dentry->d_inode, kstat);
777# elif RTLNX_VER_MIN(5,12,0)
778 generic_fillattr(ns, dentry->d_inode, kstat);
779# else
780 generic_fillattr(dentry->d_inode, kstat);
781# endif
782
783 /* Add birth time. */
784# if RTLNX_VER_MIN(4,11,0)
785 if (dentry->d_inode) {
786 struct vbsf_inode_info *pInodeInfo = VBSF_GET_INODE_INFO(dentry->d_inode);
787 if (pInodeInfo) {
788 vbsf_time_to_linux(&kstat->btime, &pInodeInfo->BirthTime);
789 kstat->result_mask |= STATX_BTIME;
790 }
791 }
792# endif
793
794 /*
795 * FsPerf shows the following numbers for sequential file access against
796 * a tmpfs folder on an AMD 1950X host running debian buster/sid:
797 *
798 * block size = r128600 ----- r128755 -----
799 * reads reads writes
800 * 4096 KB = 2254 MB/s 4953 MB/s 3668 MB/s
801 * 2048 KB = 2368 MB/s 4908 MB/s 3541 MB/s
802 * 1024 KB = 2208 MB/s 4011 MB/s 3291 MB/s
803 * 512 KB = 1908 MB/s 3399 MB/s 2721 MB/s
804 * 256 KB = 1625 MB/s 2679 MB/s 2251 MB/s
805 * 128 KB = 1413 MB/s 1967 MB/s 1684 MB/s
806 * 64 KB = 1152 MB/s 1409 MB/s 1265 MB/s
807 * 32 KB = 726 MB/s 815 MB/s 783 MB/s
808 * 16 KB = 683 MB/s 475 MB/s
809 * 8 KB = 294 MB/s 286 MB/s
810 * 4 KB = 145 MB/s 156 MB/s 149 MB/s
811 *
812 */
813 if (S_ISREG(kstat->mode))
814 kstat->blksize = _1M;
815 else if (S_ISDIR(kstat->mode))
816 /** @todo this may need more tuning after we rewrite the directory handling. */
817 kstat->blksize = _16K;
818 }
819 return rc;
820}
821#endif /* >= 2.5.18 */
822
823
824/**
825 * Modify inode attributes.
826 */
827#if RTLNX_VER_MIN(6,3,0)
828int vbsf_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *iattr)
829#elif RTLNX_VER_MIN(5,12,0)
830int vbsf_inode_setattr(struct user_namespace *ns, struct dentry *dentry, struct iattr *iattr)
831#else
832int vbsf_inode_setattr(struct dentry *dentry, struct iattr *iattr)
833#endif
834{
835 struct inode *pInode = dentry->d_inode;
836 struct vbsf_super_info *pSuperInfo = VBSF_GET_SUPER_INFO(pInode->i_sb);
837 struct vbsf_inode_info *sf_i = VBSF_GET_INODE_INFO(pInode);
838 int vrc;
839 int rc;
840
841 SFLOGFLOW(("vbsf_inode_setattr: dentry=%p inode=%p ia_valid=%#x %s\n",
842 dentry, pInode, iattr->ia_valid, sf_i ? sf_i->path->String.ach : NULL));
843 AssertReturn(sf_i, -EINVAL);
844
845 /*
846 * Do minimal attribute permission checks. We set ATTR_FORCE since we cannot
847 * preserve ownership and such and would end up with EPERM here more often than
848 * we would like. For instance it would cause 'cp' to complain about EPERM
849 * from futimes() when asked to preserve times, see ticketref:18569.
850 */
851 iattr->ia_valid |= ATTR_FORCE;
852#if (RTLNX_VER_RANGE(3,16,39, 3,17,0)) || RTLNX_VER_MIN(4,9,0) || (RTLNX_VER_RANGE(4,1,37, 4,2,0)) || RTLNX_UBUNTU_ABI_MIN(4,4,255,208)
853# if RTLNX_VER_MIN(6,3,0)
854 rc = setattr_prepare(idmap, dentry, iattr);
855# elif RTLNX_VER_MIN(5,12,0)
856 rc = setattr_prepare(ns, dentry, iattr);
857# else
858 rc = setattr_prepare(dentry, iattr);
859# endif
860#else
861 rc = inode_change_ok(pInode, iattr);
862#endif
863 if (rc == 0) {
864 /*
865 * Don't modify MTIME and CTIME for open(O_TRUNC) and ftruncate, those
866 * operations will set those timestamps automatically. Saves a host call.
867 */
868 unsigned fAttrs = iattr->ia_valid;
869#if RTLNX_VER_MIN(2,6,15)
870 fAttrs &= ~ATTR_FILE;
871#endif
872 if ( fAttrs == (ATTR_SIZE | ATTR_MTIME | ATTR_CTIME)
873#if RTLNX_VER_MIN(2,6,24)
874 || (fAttrs & (ATTR_OPEN | ATTR_SIZE)) == (ATTR_OPEN | ATTR_SIZE)
875#endif
876 )
877 fAttrs &= ~(ATTR_MTIME | ATTR_CTIME);
878
879 /*
880 * We only implement a handful of attributes, so ignore any attempts
881 * at setting bits we don't support.
882 */
883 if (fAttrs & (ATTR_MODE | ATTR_ATIME | ATTR_MTIME | ATTR_CTIME | ATTR_SIZE)) {
884 /*
885 * Try find a handle which allows us to modify the attributes, otherwise
886 * open the file/dir/whatever.
887 */
888 union SetAttrReqs
889 {
890 VBOXSFCREATEREQ Create;
891 VBOXSFOBJINFOREQ Info;
892 VBOXSFSETFILESIZEREQ SetSize;
893 VBOXSFCLOSEREQ Close;
894 } *pReq;
895 size_t cbReq;
896 SHFLHANDLE hHostFile;
897 /** @todo ATTR_FILE (2.6.15+) could be helpful here if we like. */
898 struct vbsf_handle *pHandle = fAttrs & ATTR_SIZE
899 ? vbsf_handle_find(sf_i, VBSF_HANDLE_F_WRITE, 0)
900 : vbsf_handle_find(sf_i, 0, 0);
901 if (pHandle) {
902 hHostFile = pHandle->hHost;
903 cbReq = RT_MAX(sizeof(VBOXSFOBJINFOREQ), sizeof(VBOXSFSETFILESIZEREQ));
904 pReq = (union SetAttrReqs *)VbglR0PhysHeapAlloc(cbReq);
905 if (pReq) {
906 /* likely */
907 } else
908 rc = -ENOMEM;
909 } else {
910 hHostFile = SHFL_HANDLE_NIL;
911 cbReq = RT_MAX(sizeof(pReq->Info), sizeof(pReq->Create) + SHFLSTRING_HEADER_SIZE + sf_i->path->u16Size);
912 pReq = (union SetAttrReqs *)VbglR0PhysHeapAlloc(cbReq);
913 if (pReq) {
914 RT_ZERO(pReq->Create.CreateParms);
915 pReq->Create.CreateParms.Handle = SHFL_HANDLE_NIL;
916 pReq->Create.CreateParms.CreateFlags = SHFL_CF_ACT_OPEN_IF_EXISTS
917 | SHFL_CF_ACT_FAIL_IF_NEW
918 | SHFL_CF_ACCESS_ATTR_WRITE;
919 if (fAttrs & ATTR_SIZE)
920 pReq->Create.CreateParms.CreateFlags |= SHFL_CF_ACCESS_WRITE;
921 RT_BCOPY_UNFORTIFIED(&pReq->Create.StrPath, sf_i->path, SHFLSTRING_HEADER_SIZE + sf_i->path->u16Size);
922 vrc = VbglR0SfHostReqCreate(pSuperInfo->map.root, &pReq->Create);
923 if (RT_SUCCESS(vrc)) {
924 if (pReq->Create.CreateParms.Result == SHFL_FILE_EXISTS) {
925 hHostFile = pReq->Create.CreateParms.Handle;
926 Assert(hHostFile != SHFL_HANDLE_NIL);
927 vbsf_dentry_chain_increase_ttl(dentry);
928 } else {
929 LogFunc(("file %s does not exist\n", sf_i->path->String.utf8));
930 vbsf_dentry_invalidate_ttl(dentry);
931 sf_i->force_restat = true;
932 rc = -ENOENT;
933 }
934 } else {
935 rc = -RTErrConvertToErrno(vrc);
936 LogFunc(("VbglR0SfCreate(%s) failed vrc=%Rrc rc=%d\n", sf_i->path->String.ach, vrc, rc));
937 }
938 } else
939 rc = -ENOMEM;
940 }
941 if (rc == 0) {
942 /*
943 * Set mode and/or timestamps.
944 */
945 if (fAttrs & (ATTR_MODE | ATTR_ATIME | ATTR_MTIME | ATTR_CTIME)) {
946 /* Fill in the attributes. Start by setting all to zero
947 since the host will ignore zeroed fields. */
948 RT_ZERO(pReq->Info.ObjInfo);
949
950 if (fAttrs & ATTR_MODE) {
951 pReq->Info.ObjInfo.Attr.fMode = sf_access_permissions_to_vbox(iattr->ia_mode);
952 if (iattr->ia_mode & S_IFDIR)
953 pReq->Info.ObjInfo.Attr.fMode |= RTFS_TYPE_DIRECTORY;
954 else if (iattr->ia_mode & S_IFLNK)
955 pReq->Info.ObjInfo.Attr.fMode |= RTFS_TYPE_SYMLINK;
956 else
957 pReq->Info.ObjInfo.Attr.fMode |= RTFS_TYPE_FILE;
958 }
959 if (fAttrs & ATTR_ATIME)
960 vbsf_time_to_vbox(&pReq->Info.ObjInfo.AccessTime, &iattr->ia_atime);
961 if (fAttrs & ATTR_MTIME)
962 vbsf_time_to_vbox(&pReq->Info.ObjInfo.ModificationTime, &iattr->ia_mtime);
963 if (fAttrs & ATTR_CTIME)
964 vbsf_time_to_vbox(&pReq->Info.ObjInfo.ChangeTime, &iattr->ia_ctime);
965
966 /* Make the change. */
967 vrc = VbglR0SfHostReqSetObjInfo(pSuperInfo->map.root, &pReq->Info, hHostFile);
968 if (RT_SUCCESS(vrc)) {
969 vbsf_update_inode(pInode, sf_i, &pReq->Info.ObjInfo, pSuperInfo, true /*fLocked*/, fAttrs);
970 } else {
971 rc = -RTErrConvertToErrno(vrc);
972 LogFunc(("VbglR0SfHostReqSetObjInfo(%s) failed vrc=%Rrc rc=%d\n", sf_i->path->String.ach, vrc, rc));
973 }
974 }
975
976 /*
977 * Change the file size.
978 * Note! Old API is more convenient here as it gives us up to date
979 * inode info back.
980 */
981 if ((fAttrs & ATTR_SIZE) && rc == 0) {
982 /*vrc = VbglR0SfHostReqSetFileSize(pSuperInfo->map.root, &pReq->SetSize, hHostFile, iattr->ia_size);
983 if (RT_SUCCESS(vrc)) {
984 i_size_write(pInode, iattr->ia_size);
985 } else if (vrc == VERR_NOT_IMPLEMENTED)*/ {
986 /* Fallback for pre 6.0 hosts: */
987 RT_ZERO(pReq->Info.ObjInfo);
988 pReq->Info.ObjInfo.cbObject = iattr->ia_size;
989 vrc = VbglR0SfHostReqSetFileSizeOld(pSuperInfo->map.root, &pReq->Info, hHostFile);
990 if (RT_SUCCESS(vrc))
991 vbsf_update_inode(pInode, sf_i, &pReq->Info.ObjInfo, pSuperInfo, true /*fLocked*/, fAttrs);
992 }
993 if (RT_SUCCESS(vrc)) {
994 /** @todo there is potentially more to be done here if there are mappings of
995 * the lovely file. */
996 } else {
997 rc = -RTErrConvertToErrno(vrc);
998 LogFunc(("VbglR0SfHostReqSetFileSize(%s, %#llx) failed vrc=%Rrc rc=%d\n",
999 sf_i->path->String.ach, (unsigned long long)iattr->ia_size, vrc, rc));
1000 }
1001 }
1002
1003 /*
1004 * Clean up.
1005 */
1006 if (!pHandle) {
1007 vrc = VbglR0SfHostReqClose(pSuperInfo->map.root, &pReq->Close, hHostFile);
1008 if (RT_FAILURE(vrc))
1009 LogFunc(("VbglR0SfHostReqClose(%s [%#llx]) failed vrc=%Rrc\n", sf_i->path->String.utf8, hHostFile, vrc));
1010 }
1011 }
1012 if (pReq)
1013 VbglR0PhysHeapFree(pReq);
1014 if (pHandle)
1015 vbsf_handle_release(pHandle, pSuperInfo, "vbsf_inode_setattr");
1016 } else
1017 SFLOGFLOW(("vbsf_inode_setattr: Nothing to do here: %#x (was %#x).\n", fAttrs, iattr->ia_valid));
1018 }
1019 return rc;
1020}
1021
1022
1023static int vbsf_make_path(const char *caller, struct vbsf_inode_info *sf_i,
1024 const char *d_name, size_t d_len, SHFLSTRING **result)
1025{
1026 size_t path_len, shflstring_len;
1027 SHFLSTRING *tmp;
1028 uint16_t p_len;
1029 uint8_t *p_name;
1030 int fRoot = 0;
1031
1032 TRACE();
1033 p_len = sf_i->path->u16Length;
1034 p_name = sf_i->path->String.utf8;
1035
1036 if (p_len == 1 && *p_name == '/') {
1037 path_len = d_len + 1;
1038 fRoot = 1;
1039 } else {
1040 /* lengths of constituents plus terminating zero plus slash */
1041 path_len = p_len + d_len + 2;
1042 if (path_len > 0xffff) {
1043 LogFunc(("path too long. caller=%s, path_len=%zu\n",
1044 caller, path_len));
1045 return -ENAMETOOLONG;
1046 }
1047 }
1048
1049 shflstring_len = offsetof(SHFLSTRING, String.utf8) + path_len;
1050 tmp = kmalloc(shflstring_len, GFP_KERNEL);
1051 if (!tmp) {
1052 LogRelFunc(("kmalloc failed, caller=%s\n", caller));
1053 return -ENOMEM;
1054 }
1055 tmp->u16Length = path_len - 1;
1056 tmp->u16Size = path_len;
1057
1058 if (fRoot)
1059 RT_BCOPY_UNFORTIFIED(&tmp->String.utf8[0], d_name, d_len + 1);
1060 else {
1061 uint8_t *pszPath = tmp->String.utf8;
1062 RT_BCOPY_UNFORTIFIED(&pszPath[0], p_name, p_len);
1063 pszPath[p_len] = '/';
1064 RT_BCOPY_UNFORTIFIED(&pszPath[p_len + 1], d_name, d_len);
1065 pszPath[p_len + 1 + d_len] = '\0';
1066 }
1067
1068 *result = tmp;
1069 return 0;
1070}
1071
1072
1073/**
1074 * [dentry] contains string encoded in coding system that corresponds
1075 * to [pSuperInfo]->nls, we must convert it to UTF8 here and pass down to
1076 * [vbsf_make_path] which will allocate SHFLSTRING and fill it in
1077 */
1078int vbsf_path_from_dentry(struct vbsf_super_info *pSuperInfo, struct vbsf_inode_info *sf_i, struct dentry *dentry,
1079 SHFLSTRING **result, const char *caller)
1080{
1081 int err;
1082 const char *d_name;
1083 size_t d_len;
1084 const char *name;
1085 size_t len = 0;
1086
1087 TRACE();
1088 d_name = dentry->d_name.name;
1089 d_len = dentry->d_name.len;
1090
1091 if (pSuperInfo->nls) {
1092 size_t in_len, i, out_bound_len;
1093 const char *in;
1094 char *out;
1095
1096 in = d_name;
1097 in_len = d_len;
1098
1099 out_bound_len = PATH_MAX;
1100 out = kmalloc(out_bound_len, GFP_KERNEL);
1101 name = out;
1102
1103 for (i = 0; i < d_len; ++i) {
1104 /* We renamed the linux kernel wchar_t type to linux_wchar_t in
1105 the-linux-kernel.h, as it conflicts with the C++ type of that name. */
1106 linux_wchar_t uni;
1107 int nb;
1108
1109 nb = pSuperInfo->nls->char2uni(in, in_len, &uni);
1110 if (nb < 0) {
1111 LogFunc(("nls->char2uni failed %x %d\n",
1112 *in, in_len));
1113 err = -EINVAL;
1114 goto fail1;
1115 }
1116 in_len -= nb;
1117 in += nb;
1118
1119#if RTLNX_VER_MIN(2,6,31)
1120 nb = utf32_to_utf8(uni, out, out_bound_len);
1121#else
1122 nb = utf8_wctomb(out, uni, out_bound_len);
1123#endif
1124 if (nb < 0) {
1125 LogFunc(("nls->uni2char failed %x %d\n",
1126 uni, out_bound_len));
1127 err = -EINVAL;
1128 goto fail1;
1129 }
1130 out_bound_len -= nb;
1131 out += nb;
1132 len += nb;
1133 }
1134 if (len >= PATH_MAX - 1) {
1135 err = -ENAMETOOLONG;
1136 goto fail1;
1137 }
1138
1139 LogFunc(("result(%d) = %.*s\n", len, len, name));
1140 *out = 0;
1141 } else {
1142 name = d_name;
1143 len = d_len;
1144 }
1145
1146 err = vbsf_make_path(caller, sf_i, name, len, result);
1147 if (name != d_name)
1148 kfree(name);
1149
1150 return err;
1151
1152 fail1:
1153 kfree(name);
1154 return err;
1155}
1156
1157
1158/**
1159 * This is called during name resolution/lookup to check if the @a dentry in the
1160 * cache is still valid. The actual validation is job is handled by
1161 * vbsf_inode_revalidate_worker().
1162 *
1163 * @note Caller holds no relevant locks, just a dentry reference.
1164 */
1165#if RTLNX_VER_MIN(3,6,0)
1166static int vbsf_dentry_revalidate(struct dentry *dentry, unsigned flags)
1167#elif RTLNX_VER_MIN(2,6,0)
1168static int vbsf_dentry_revalidate(struct dentry *dentry, struct nameidata *nd)
1169#else
1170static int vbsf_dentry_revalidate(struct dentry *dentry, int flags)
1171#endif
1172{
1173#if RTLNX_VER_RANGE(2,6,0, 3,6,0)
1174 int const flags = nd ? nd->flags : 0;
1175#endif
1176
1177 int rc;
1178
1179 Assert(dentry);
1180 SFLOGFLOW(("vbsf_dentry_revalidate: %p %#x %s\n", dentry, flags,
1181 dentry->d_inode ? VBSF_GET_INODE_INFO(dentry->d_inode)->path->String.ach : "<negative>"));
1182
1183 /*
1184 * See Documentation/filesystems/vfs.txt why we skip LOOKUP_RCU.
1185 *
1186 * Also recommended: https://lwn.net/Articles/649115/
1187 * https://lwn.net/Articles/649729/
1188 * https://lwn.net/Articles/650786/
1189 *
1190 */
1191#if RTLNX_VER_MIN(2,6,38)
1192 if (flags & LOOKUP_RCU) {
1193 rc = -ECHILD;
1194 SFLOGFLOW(("vbsf_dentry_revalidate: RCU -> -ECHILD\n"));
1195 } else
1196#endif
1197 {
1198 /*
1199 * Do we have an inode or not? If not it's probably a negative cache
1200 * entry, otherwise most likely a positive one.
1201 */
1202 struct inode *pInode = dentry->d_inode;
1203 if (pInode) {
1204 /*
1205 * Positive entry.
1206 *
1207 * Note! We're more aggressive here than other remote file systems,
1208 * current (4.19) CIFS will for instance revalidate the inode
1209 * and ignore the dentry timestamp for positive entries.
1210 */
1211 unsigned long const cJiffiesAge = jiffies - vbsf_dentry_get_update_jiffies(dentry);
1212 struct vbsf_super_info *pSuperInfo = VBSF_GET_SUPER_INFO(dentry->d_sb);
1213 if (cJiffiesAge < pSuperInfo->cJiffiesDirCacheTTL) {
1214 SFLOGFLOW(("vbsf_dentry_revalidate: age: %lu vs. TTL %lu -> 1\n", cJiffiesAge, pSuperInfo->cJiffiesDirCacheTTL));
1215 rc = 1;
1216 } else if (!vbsf_inode_revalidate_worker(dentry, true /*fForced*/, false /*fInodeLocked*/)) {
1217 vbsf_dentry_set_update_jiffies(dentry, jiffies);
1218 SFLOGFLOW(("vbsf_dentry_revalidate: age: %lu vs. TTL %lu -> reval -> 1\n", cJiffiesAge, pSuperInfo->cJiffiesDirCacheTTL));
1219 rc = 1;
1220 } else {
1221 SFLOGFLOW(("vbsf_dentry_revalidate: age: %lu vs. TTL %lu -> reval -> 0\n", cJiffiesAge, pSuperInfo->cJiffiesDirCacheTTL));
1222 rc = 0;
1223 }
1224 } else {
1225 /*
1226 * Negative entry.
1227 *
1228 * Invalidate dentries for open and renames here as we'll revalidate
1229 * these when taking the actual action (also good for case preservation
1230 * if we do case-insensitive mounts against windows + mac hosts at some
1231 * later point).
1232 */
1233#if RTLNX_VER_MIN(2,6,28)
1234 if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
1235#elif RTLNX_VER_MIN(2,5,75)
1236 if (flags & LOOKUP_CREATE)
1237#else
1238 if (0)
1239#endif
1240 {
1241 SFLOGFLOW(("vbsf_dentry_revalidate: negative: create or rename target -> 0\n"));
1242 rc = 0;
1243 } else {
1244 /* Can we skip revalidation based on TTL? */
1245 unsigned long const cJiffiesAge = vbsf_dentry_get_update_jiffies(dentry) - jiffies;
1246 struct vbsf_super_info *pSuperInfo = VBSF_GET_SUPER_INFO(dentry->d_sb);
1247 if (cJiffiesAge < pSuperInfo->cJiffiesDirCacheTTL) {
1248 SFLOGFLOW(("vbsf_dentry_revalidate: negative: age: %lu vs. TTL %lu -> 1\n", cJiffiesAge, pSuperInfo->cJiffiesDirCacheTTL));
1249 rc = 1;
1250 } else {
1251 /* We could revalidate it here, but we could instead just
1252 have the caller kick it out. */
1253 /** @todo stat the direntry and see if it exists now. */
1254 SFLOGFLOW(("vbsf_dentry_revalidate: negative: age: %lu vs. TTL %lu -> 0\n", cJiffiesAge, pSuperInfo->cJiffiesDirCacheTTL));
1255 rc = 0;
1256 }
1257 }
1258 }
1259 }
1260 return rc;
1261}
1262
1263#ifdef SFLOG_ENABLED
1264
1265/** For logging purposes only. */
1266# if RTLNX_VER_MIN(2,6,38)
1267static int vbsf_dentry_delete(const struct dentry *pDirEntry)
1268# else
1269static int vbsf_dentry_delete(struct dentry *pDirEntry)
1270# endif
1271{
1272 SFLOGFLOW(("vbsf_dentry_delete: %p\n", pDirEntry));
1273 return 0;
1274}
1275
1276# if RTLNX_VER_MIN(4,8,0)
1277/** For logging purposes only. */
1278static int vbsf_dentry_init(struct dentry *pDirEntry)
1279{
1280 SFLOGFLOW(("vbsf_dentry_init: %p\n", pDirEntry));
1281 return 0;
1282}
1283# endif
1284
1285#endif /* SFLOG_ENABLED */
1286
1287/**
1288 * Directory entry operations.
1289 *
1290 * Since 2.6.38 this is used via the super_block::s_d_op member.
1291 */
1292struct dentry_operations vbsf_dentry_ops = {
1293 .d_revalidate = vbsf_dentry_revalidate,
1294#ifdef SFLOG_ENABLED
1295 .d_delete = vbsf_dentry_delete,
1296# if RTLNX_VER_MIN(4,8,0)
1297 .d_init = vbsf_dentry_init,
1298# endif
1299#endif
1300};
1301
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use