VirtualBox

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

Last change on this file was 104345, checked in by vboxsync, 4 weeks ago

Additions: Linux: UBSAN: vboxsf: update naming for r162827, bugref:10585.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use