VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.6 KB
Line 
1/* $Id: lnkops.c 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * vboxsf - VBox Linux Shared Folders VFS, operations for symbolic links.
4 */
5
6/*
7 * Copyright (C) 2010-2023 Oracle and/or its affiliates.
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without
12 * restriction, including without limitation the rights to use,
13 * copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following
16 * conditions:
17 *
18 * The above copyright notice and this permission notice shall be
19 * included in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
23 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
25 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
26 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
28 * OTHER DEALINGS IN THE SOFTWARE.
29 */
30
31
32/*********************************************************************************************************************************
33* Header Files *
34*********************************************************************************************************************************/
35#include "vfsmod.h"
36
37
38/**
39 * Converts error codes as best we can.
40 */
41DECLINLINE(int) vbsf_convert_symlink_error(int vrc)
42{
43 if ( vrc == VERR_IS_A_DIRECTORY
44 || vrc == VERR_IS_A_FIFO
45 || vrc == VERR_IS_A_FILE
46 || vrc == VERR_IS_A_BLOCK_DEVICE
47 || vrc == VERR_IS_A_CHAR_DEVICE
48 || vrc == VERR_IS_A_SOCKET
49 || vrc == VERR_NOT_SYMLINK)
50 return -EINVAL;
51 if (vrc == VERR_PATH_NOT_FOUND)
52 return -ENOTDIR;
53 if (vrc == VERR_FILE_NOT_FOUND)
54 return -ENOENT;
55 return -EPROTO;
56}
57
58
59/**
60 * Does the NLS conversion of the symlink target.
61 */
62static int vbsf_symlink_nls_convert_slow(struct vbsf_super_info *pSuperInfo, char *pszTarget, size_t cbTargetBuf)
63{
64 int rc;
65 size_t const cchUtf8 = RTStrNLen(pszTarget, cbTargetBuf);
66 if (cchUtf8 < cbTargetBuf) {
67 /*
68 * If the target is short and there is a lot of space left in the target
69 * buffer (typically PAGE_SIZE in size), we move the input to the end
70 * instead of allocating a temporary buffer for it. This works because
71 * there shouldn't be anything that is more than 8x worse than UTF-8
72 * when it comes to efficiency.
73 */
74 char *pszFree = NULL;
75 char *pszUtf8;
76 if (cchUtf8 - 1 <= cbTargetBuf / 8) {
77 pszUtf8 = &pszTarget[cbTargetBuf - cchUtf8 - 1];
78 cbTargetBuf -= cchUtf8 - 1;
79 } else {
80 pszFree = pszUtf8 = kmalloc(cchUtf8 + 1, GFP_KERNEL);
81 if (RT_UNLIKELY(!pszUtf8)) {
82 LogRelMax(50, ("vbsf_symlink_nls_convert_slow: failed to allocate %u bytes\n", cchUtf8 + 1));
83 return -ENOMEM;
84 }
85 }
86 memcpy(pszUtf8, pszTarget, cchUtf8);
87 pszUtf8[cchUtf8] = '\0';
88
89 rc = vbsf_nlscpy(pSuperInfo, pszTarget, cbTargetBuf, pszUtf8, cchUtf8);
90 if (pszFree)
91 kfree(pszFree);
92 } else {
93 SFLOGFLOW(("vbsf_symlink_nls_convert_slow: Impossible! Unterminated target!\n"));
94 rc = -ENAMETOOLONG;
95 }
96 return rc;
97}
98
99
100/**
101 * Does NLS conversion if needed.
102 */
103DECLINLINE(int) vbsf_symlink_nls_convert(struct vbsf_super_info *pSuperInfo, char *pszTarget, size_t cbTargetBuf)
104{
105 if (pSuperInfo->fNlsIsUtf8)
106 return 0;
107 return vbsf_symlink_nls_convert_slow(pSuperInfo, pszTarget, cbTargetBuf);
108}
109
110#if RTLNX_VER_MIN(4,5,0)
111
112/**
113 * Get symbolic link.
114 */
115static const char *vbsf_get_link(struct dentry *dentry, struct inode *inode, struct delayed_call *done)
116{
117 char *pszTarget;
118 if (dentry) {
119 pszTarget = (char *)kzalloc(PAGE_SIZE, GFP_KERNEL);
120 if (pszTarget) {
121 struct vbsf_super_info *pSuperInfo = VBSF_GET_SUPER_INFO(inode->i_sb);
122 struct vbsf_inode_info *sf_i = VBSF_GET_INODE_INFO(inode);
123 int rc = VbglR0SfHostReqReadLinkContigSimple(pSuperInfo->map.root, sf_i->path->String.ach, sf_i->path->u16Length,
124 pszTarget, virt_to_phys(pszTarget), RT_MIN(PATH_MAX, PAGE_SIZE - 1));
125 if (RT_SUCCESS(rc)) {
126 pszTarget[PAGE_SIZE - 1] = '\0';
127 SFLOGFLOW(("vbsf_get_link: %s -> %s\n", sf_i->path->String.ach, pszTarget));
128 rc = vbsf_symlink_nls_convert(pSuperInfo, pszTarget, PAGE_SIZE);
129 if (rc == 0) {
130 vbsf_dentry_chain_increase_ttl(dentry);
131 set_delayed_call(done, kfree_link, pszTarget);
132 return pszTarget;
133 }
134 } else {
135 SFLOGFLOW(("vbsf_get_link: VbglR0SfHostReqReadLinkContigSimple failed on '%s': %Rrc\n",
136 sf_i->path->String.ach, rc));
137 }
138 kfree(pszTarget);
139 pszTarget = ERR_PTR(vbsf_convert_symlink_error(rc));
140 } else
141 pszTarget = ERR_PTR(-ENOMEM);
142 } else
143 pszTarget = ERR_PTR(-ECHILD);
144 return pszTarget;
145}
146
147#else /* < 4.5 */
148
149# if RTLNX_VER_MAX(2,6,8)
150/**
151 * Reads the link into the given buffer.
152 */
153static int vbsf_readlink(struct dentry *dentry, char *buffer, int len)
154{
155 int rc;
156 char *pszTarget = (char *)get_zeroed_page(GFP_KERNEL);
157 if (pszTarget) {
158 struct inode *inode = dentry->d_inode;
159 struct vbsf_super_info *pSuperInfo = VBSF_GET_SUPER_INFO(inode->i_sb);
160 struct vbsf_inode_info *sf_i = VBSF_GET_INODE_INFO(inode);
161 rc = VbglR0SfHostReqReadLinkContigSimple(pSuperInfo->map.root, sf_i->path->String.ach, sf_i->path->u16Length,
162 pszTarget, virt_to_phys(pszTarget), RT_MIN(PATH_MAX, PAGE_SIZE - 1));
163 if (RT_SUCCESS(rc)) {
164 pszTarget[PAGE_SIZE - 1] = '\0';
165 SFLOGFLOW(("vbsf_readlink: %s -> %*s\n", sf_i->path->String.ach, pszTarget));
166 rc = vbsf_symlink_nls_convert(pSuperInfo, pszTarget, PAGE_SIZE);
167 if (rc == 0) {
168 vbsf_dentry_chain_increase_ttl(dentry);
169 rc = vfs_readlink(dentry, buffer, len, pszTarget);
170 }
171 } else {
172 SFLOGFLOW(("vbsf_readlink: VbglR0SfHostReqReadLinkContigSimple failed on '%s': %Rrc\n", sf_i->path->String.ach, rc));
173 rc = vbsf_convert_symlink_error(rc);
174 }
175 free_page((unsigned long)pszTarget);
176 } else
177 rc = -ENOMEM;
178 return rc;
179}
180# endif /* < 2.6.8 */
181
182/**
183 * Follow link in dentry.
184 */
185# if RTLNX_VER_MIN(4,2,0)
186static const char *vbsf_follow_link(struct dentry *dentry, void **cookie)
187# elif RTLNX_VER_MIN(2,6,13)
188static void *vbsf_follow_link(struct dentry *dentry, struct nameidata *nd)
189# else
190static int vbsf_follow_link(struct dentry *dentry, struct nameidata *nd)
191# endif
192{
193 int rc;
194 char *pszTarget = (char *)get_zeroed_page(GFP_KERNEL);
195 if (pszTarget) {
196 struct inode *inode = dentry->d_inode;
197 struct vbsf_super_info *pSuperInfo = VBSF_GET_SUPER_INFO(inode->i_sb);
198 struct vbsf_inode_info *sf_i = VBSF_GET_INODE_INFO(inode);
199
200 rc = VbglR0SfHostReqReadLinkContigSimple(pSuperInfo->map.root, sf_i->path->String.ach, sf_i->path->u16Length,
201 pszTarget, virt_to_phys(pszTarget), RT_MIN(PATH_MAX, PAGE_SIZE - 1));
202 if (RT_SUCCESS(rc)) {
203 pszTarget[PAGE_SIZE - 1] = '\0';
204 SFLOGFLOW(("vbsf_follow_link: %s -> %s\n", sf_i->path->String.ach, pszTarget));
205 rc = vbsf_symlink_nls_convert(pSuperInfo, pszTarget, PAGE_SIZE);
206 if (rc == 0) {
207 /*
208 * Succeeded. For 2.6.8 and later the page gets associated
209 * with the caller-cookie or nameidata structure and freed
210 * later by vbsf_put_link(). On earlier kernels we have to
211 * call vfs_follow_link() which will try continue the walking
212 * using the buffer we pass it here.
213 */
214 vbsf_dentry_chain_increase_ttl(dentry);
215# if RTLNX_VER_MIN(4,2,0)
216 *cookie = pszTarget;
217 return pszTarget;
218# elif RTLNX_VER_MIN(2,6,8)
219 nd_set_link(nd, pszTarget);
220# if RTLNX_VER_MIN(2,6,13)
221 return NULL;
222# else
223 return 0;
224# endif
225# else /* < 2.6.8 */
226 rc = vfs_follow_link(nd, pszTarget);
227 free_page((unsigned long)pszTarget);
228 return rc;
229# endif
230 }
231
232 /*
233 * Failed.
234 */
235 } else {
236 LogFunc(("VbglR0SfReadLink failed, caller=%s, rc=%Rrc\n", __func__, rc));
237 rc = vbsf_convert_symlink_error(rc);
238 }
239 free_page((unsigned long)pszTarget);
240 } else {
241 rc = -ENOMEM;
242 }
243# if RTLNX_VER_MIN(4,2,0)
244 *cookie = ERR_PTR(rc);
245 return (const char *)ERR_PTR(rc);
246# elif RTLNX_VER_MIN(2,6,8)
247 nd_set_link(nd, (char *)ERR_PTR(rc));
248# if RTLNX_VER_MIN(2,6,13)
249 return NULL;
250# else
251 return 0;
252# endif
253# else /* < 2.6.8 */
254 return rc;
255# endif /* < 2.6.8 */
256}
257
258# if RTLNX_VER_MIN(2,6,8)
259/**
260 * For freeing target link buffer allocated by vbsf_follow_link.
261 *
262 * For kernels before 2.6.8 memory isn't being kept around.
263 */
264# if RTLNX_VER_MIN(4,2,0)
265static void vbsf_put_link(struct inode *inode, void *cookie)
266# elif RTLNX_VER_MIN(2,6,13)
267static void vbsf_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie)
268# else
269static void vbsf_put_link(struct dentry *dentry, struct nameidata *nd)
270# endif
271{
272# if RTLNX_VER_MIN(2,6,13)
273 char *page = cookie;
274# else
275 char *page = nd_get_link(nd);
276# endif
277 SFLOGFLOW(("vbsf_put_link: page=%p\n", page));
278 if (!IS_ERR(page))
279 free_page((unsigned long)page);
280}
281# endif /* >= 2.6.8 */
282
283#endif /* < 4.5.0 */
284
285/**
286 * Symlink inode operations.
287 */
288struct inode_operations vbsf_lnk_iops = {
289#if RTLNX_VER_MAX(4,10,0)
290# if RTLNX_VER_MIN(2,6,8)
291 .readlink = generic_readlink,
292# else
293 .readlink = vbsf_readlink,
294# endif
295#endif
296#if RTLNX_VER_MIN(4,5,0)
297 .get_link = vbsf_get_link
298#else
299 .follow_link = vbsf_follow_link,
300# if RTLNX_VER_MIN(2,6,8)
301 .put_link = vbsf_put_link,
302# endif
303#endif
304};
305
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use