xref: /reactos/base/services/nfsd/symlink.c (revision 8a978a17)
1 /* NFSv4.1 client for Windows
2  * Copyright � 2012 The Regents of the University of Michigan
3  *
4  * Olga Kornievskaia <aglo@umich.edu>
5  * Casey Bodley <cbodley@umich.edu>
6  *
7  * This library is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as published by
9  * the Free Software Foundation; either version 2.1 of the License, or (at
10  * your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful, but
13  * without any warranty; without even the implied warranty of merchantability
14  * or fitness for a particular purpose.  See the GNU Lesser General Public
15  * License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this library; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20  */
21 
22 #include <windows.h>
23 #include <strsafe.h>
24 
25 #include "nfs41_ops.h"
26 #include "upcall.h"
27 #include "util.h"
28 #include "daemon_debug.h"
29 
30 
31 static int abs_path_link(
32     OUT nfs41_abs_path *path,
33     IN char *path_pos,
34     IN const char *link,
35     IN uint32_t link_len)
36 {
37     nfs41_component name;
38     const char *path_max = path->path + NFS41_MAX_PATH_LEN;
39     const char *link_pos = link;
40     const char *link_end = link + link_len;
41     int status = NO_ERROR;
42 
43     /* if link is an absolute path, start path_pos at the beginning */
44     if (is_delimiter(*link))
45         path_pos = path->path;
46 
47     /* copy each component of link into the path */
48     while (next_component(link_pos, link_end, &name)) {
49         link_pos = name.name + name.len;
50 
51         if (is_delimiter(*path_pos))
52             path_pos++;
53 
54         /* handle special components . and .. */
55         if (name.len == 1 && name.name[0] == '.')
56             continue;
57         if (name.len == 2 && name.name[0] == '.' && name.name[1] == '.') {
58             /* back path_pos up by one component */
59             if (!last_component(path->path, path_pos, &name)) {
60                 eprintf("symlink with .. that points below server root!\n");
61                 status = ERROR_BAD_NETPATH;
62                 goto out;
63             }
64             path_pos = (char*)prev_delimiter(name.name, path->path);
65             continue;
66         }
67 
68         /* copy the component and add a \ */
69         if (FAILED(StringCchCopyNA(path_pos, path_max-path_pos, name.name,
70                 name.len))) {
71             status = ERROR_BUFFER_OVERFLOW;
72             goto out;
73         }
74         path_pos += name.len;
75         if (FAILED(StringCchCopyNA(path_pos, path_max-path_pos, "\\", 1))) {
76             status = ERROR_BUFFER_OVERFLOW;
77             goto out;
78         }
79     }
80 
81     /* make sure the path is null terminated */
82     if (path_pos == path_max) {
83         status = ERROR_BUFFER_OVERFLOW;
84         goto out;
85     }
86     *path_pos = '\0';
87 out:
88     path->len = (unsigned short)(path_pos - path->path);
89     return status;
90 }
91 
92 int nfs41_symlink_target(
93     IN nfs41_session *session,
94     IN nfs41_path_fh *file,
95     OUT nfs41_abs_path *target)
96 {
97     char link[NFS41_MAX_PATH_LEN];
98     const nfs41_abs_path *path = file->path;
99     ptrdiff_t path_offset;
100     uint32_t link_len;
101     int status;
102 
103     /* read the link */
104     status = nfs41_readlink(session, file, NFS41_MAX_PATH_LEN, link, &link_len);
105     if (status) {
106         eprintf("nfs41_readlink() for %s failed with %s\n", file->path->path,
107             nfs_error_string(status));
108         status = ERROR_PATH_NOT_FOUND;
109         goto out;
110     }
111 
112     dprintf(2, "--> nfs41_symlink_target('%s', '%s')\n", path->path, link);
113 
114     /* append any components after the symlink */
115     if (FAILED(StringCchCatA(link, NFS41_MAX_PATH_LEN,
116             file->name.name + file->name.len))) {
117         status = ERROR_BUFFER_OVERFLOW;
118         goto out;
119     }
120     link_len = (uint32_t)strlen(link);
121 
122     /* overwrite the last component of the path; get the starting offset */
123     path_offset = file->name.name - path->path;
124 
125     /* copy the path and update it with the results from link */
126     if (target != path) {
127         target->len = path->len;
128         if (FAILED(StringCchCopyNA(target->path, NFS41_MAX_PATH_LEN,
129                 path->path, path->len))) {
130             status = ERROR_BUFFER_OVERFLOW;
131             goto out;
132         }
133     }
134     status = abs_path_link(target, target->path + path_offset, link, link_len);
135     if (status) {
136         eprintf("abs_path_link() for path %s with link %s failed with %d\n",
137             target->path, link, status);
138         goto out;
139     }
140 out:
141     dprintf(2, "<-- nfs41_symlink_target('%s') returning %d\n",
142         target->path, status);
143     return status;
144 }
145 
146 int nfs41_symlink_follow(
147     IN nfs41_root *root,
148     IN nfs41_session *session,
149     IN nfs41_path_fh *symlink,
150     OUT nfs41_file_info *info)
151 {
152     nfs41_abs_path path;
153     nfs41_path_fh file;
154     uint32_t depth = 0;
155     int status = NO_ERROR;
156 
157     file.path = &path;
158     InitializeSRWLock(&path.lock);
159 
160     dprintf(2, "--> nfs41_symlink_follow('%s')\n", symlink->path->path);
161 
162     do {
163         if (++depth > NFS41_MAX_SYMLINK_DEPTH) {
164             status = ERROR_TOO_MANY_LINKS;
165             goto out;
166         }
167 
168         /* construct the target path */
169         status = nfs41_symlink_target(session, symlink, &path);
170         if (status) goto out;
171 
172         dprintf(2, "looking up '%s'\n", path.path);
173 
174         last_component(path.path, path.path + path.len, &file.name);
175 
176         /* get attributes for the target */
177         status = nfs41_lookup(root, session, &path,
178             NULL, &file, info, &session);
179         if (status) goto out;
180 
181         symlink = &file;
182     } while (info->type == NF4LNK);
183 out:
184     dprintf(2, "<-- nfs41_symlink_follow() returning %d\n", status);
185     return status;
186 }
187 
188 
189 /* NFS41_SYMLINK */
190 static int parse_symlink(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
191 {
192     symlink_upcall_args *args = &upcall->args.symlink;
193     int status;
194 
195     status = get_name(&buffer, &length, &args->path);
196     if (status) goto out;
197     status = safe_read(&buffer, &length, &args->set, sizeof(BOOLEAN));
198     if (status) goto out;
199 
200     if (args->set)
201         status = get_name(&buffer, &length, &args->target_set);
202     else
203         args->target_set = NULL;
204 
205     dprintf(1, "parsing NFS41_SYMLINK: path='%s' set=%u target='%s'\n",
206         args->path, args->set, args->target_set);
207 out:
208     return status;
209 }
210 
211 static int handle_symlink(nfs41_upcall *upcall)
212 {
213     symlink_upcall_args *args = &upcall->args.symlink;
214     nfs41_open_state *state = upcall->state_ref;
215     int status = NO_ERROR;
216 
217     if (args->set) {
218         nfs41_file_info info, createattrs;
219 
220         /* don't send windows slashes to the server */
221         char *p;
222         for (p = args->target_set; *p; p++) if (*p == '\\') *p = '/';
223 
224         if (state->file.fh.len) {
225             /* the check in handle_open() didn't catch that we're creating
226              * a symlink, so we have to remove the file it already created */
227             eprintf("handle_symlink: attempting to create a symlink when "
228                 "the file=%s was already created on open; sending REMOVE "
229                 "first\n", state->file.path->path);
230             status = nfs41_remove(state->session, &state->parent,
231                 &state->file.name, state->file.fh.fileid);
232             if (status) {
233                 eprintf("nfs41_remove() for symlink=%s failed with %s\n",
234                     args->target_set, nfs_error_string(status));
235                 status = map_symlink_errors(status);
236                 goto out;
237             }
238         }
239 
240         /* create the symlink */
241         createattrs.attrmask.count = 2;
242         createattrs.attrmask.arr[0] = 0;
243         createattrs.attrmask.arr[1] = FATTR4_WORD1_MODE;
244         createattrs.mode = 0777;
245         status = nfs41_create(state->session, NF4LNK, &createattrs,
246             args->target_set, &state->parent, &state->file, &info);
247         if (status) {
248             eprintf("nfs41_create() for symlink=%s failed with %s\n",
249                 args->target_set, nfs_error_string(status));
250             status = map_symlink_errors(status);
251             goto out;
252         }
253     } else {
254         uint32_t len;
255 
256         /* read the link */
257         status = nfs41_readlink(state->session, &state->file,
258             NFS41_MAX_PATH_LEN, args->target_get.path, &len);
259         if (status) {
260             eprintf("nfs41_readlink() for filename=%s failed with %s\n",
261                 state->file.path->path, nfs_error_string(status));
262             status = map_symlink_errors(status);
263             goto out;
264         }
265         args->target_get.len = (unsigned short)len;
266         dprintf(2, "returning symlink target '%s'\n", args->target_get.path);
267     }
268 out:
269     return status;
270 }
271 
272 static int marshall_symlink(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
273 {
274     symlink_upcall_args *args = &upcall->args.symlink;
275     unsigned short len = (args->target_get.len + 1) * sizeof(WCHAR);
276     int status = NO_ERROR;
277 
278     if (args->set)
279         goto out;
280 
281     status = safe_write(&buffer, length, &len, sizeof(len));
282     if (status) goto out;
283 
284     if (*length <= len || !MultiByteToWideChar(CP_UTF8, 0,
285             args->target_get.path, args->target_get.len,
286             (LPWSTR)buffer, len / sizeof(WCHAR))) {
287         status = ERROR_BUFFER_OVERFLOW;
288         goto out;
289     }
290 out:
291     return status;
292 }
293 
294 
295 const nfs41_upcall_op nfs41_op_symlink = {
296     parse_symlink,
297     handle_symlink,
298     marshall_symlink
299 };
300