1 /* NFSv4.1 client for Windows
2 * Copyright © 2012 The Regents of the University of Michigan
4 * Olga Kornievskaia <aglo@umich.edu>
5 * Casey Bodley <cbodley@umich.edu>
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.
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.
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
25 #include "nfs41_ops.h"
28 #include "daemon_debug.h"
31 static int abs_path_link(
32 OUT nfs41_abs_path
*path
,
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
;
43 /* if link is an absolute path, start path_pos at the beginning */
44 if (is_delimiter(*link
))
45 path_pos
= path
->path
;
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
;
51 if (is_delimiter(*path_pos
))
54 /* handle special components . and .. */
55 if (name
.len
== 1 && name
.name
[0] == '.')
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
;
64 path_pos
= (char*)prev_delimiter(name
.name
, path
->path
);
68 /* copy the component and add a \ */
69 if (FAILED(StringCchCopyNA(path_pos
, path_max
-path_pos
, name
.name
,
71 status
= ERROR_BUFFER_OVERFLOW
;
75 if (FAILED(StringCchCopyNA(path_pos
, path_max
-path_pos
, "\\", 1))) {
76 status
= ERROR_BUFFER_OVERFLOW
;
81 /* make sure the path is null terminated */
82 if (path_pos
== path_max
) {
83 status
= ERROR_BUFFER_OVERFLOW
;
88 path
->len
= (unsigned short)(path_pos
- path
->path
);
92 int nfs41_symlink_target(
93 IN nfs41_session
*session
,
94 IN nfs41_path_fh
*file
,
95 OUT nfs41_abs_path
*target
)
97 char link
[NFS41_MAX_PATH_LEN
];
98 const nfs41_abs_path
*path
= file
->path
;
99 ptrdiff_t path_offset
;
104 status
= nfs41_readlink(session
, file
, NFS41_MAX_PATH_LEN
, link
, &link_len
);
106 eprintf("nfs41_readlink() for %s failed with %s\n", file
->path
->path
,
107 nfs_error_string(status
));
108 status
= ERROR_PATH_NOT_FOUND
;
112 dprintf(2, "--> nfs41_symlink_target('%s', '%s')\n", path
->path
, link
);
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
;
120 link_len
= (uint32_t)strlen(link
);
122 /* overwrite the last component of the path; get the starting offset */
123 path_offset
= file
->name
.name
- path
->path
;
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
;
134 status
= abs_path_link(target
, target
->path
+ path_offset
, link
, link_len
);
136 eprintf("abs_path_link() for path %s with link %s failed with %d\n",
137 target
->path
, link
, status
);
141 dprintf(2, "<-- nfs41_symlink_target('%s') returning %d\n",
142 target
->path
, status
);
146 int nfs41_symlink_follow(
148 IN nfs41_session
*session
,
149 IN nfs41_path_fh
*symlink
,
150 OUT nfs41_file_info
*info
)
155 int status
= NO_ERROR
;
158 InitializeSRWLock(&path
.lock
);
160 dprintf(2, "--> nfs41_symlink_follow('%s')\n", symlink
->path
->path
);
163 if (++depth
> NFS41_MAX_SYMLINK_DEPTH
) {
164 status
= ERROR_TOO_MANY_LINKS
;
168 /* construct the target path */
169 status
= nfs41_symlink_target(session
, symlink
, &path
);
170 if (status
) goto out
;
172 dprintf(2, "looking up '%s'\n", path
.path
);
174 last_component(path
.path
, path
.path
+ path
.len
, &file
.name
);
176 /* get attributes for the target */
177 status
= nfs41_lookup(root
, session
, &path
,
178 NULL
, &file
, info
, &session
);
179 if (status
) goto out
;
182 } while (info
->type
== NF4LNK
);
184 dprintf(2, "<-- nfs41_symlink_follow() returning %d\n", status
);
190 static int parse_symlink(unsigned char *buffer
, uint32_t length
, nfs41_upcall
*upcall
)
192 symlink_upcall_args
*args
= &upcall
->args
.symlink
;
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
;
201 status
= get_name(&buffer
, &length
, &args
->target_set
);
203 args
->target_set
= NULL
;
205 dprintf(1, "parsing NFS41_SYMLINK: path='%s' set=%u target='%s'\n",
206 args
->path
, args
->set
, args
->target_set
);
211 static int handle_symlink(nfs41_upcall
*upcall
)
213 symlink_upcall_args
*args
= &upcall
->args
.symlink
;
214 nfs41_open_state
*state
= upcall
->state_ref
;
215 int status
= NO_ERROR
;
218 nfs41_file_info info
, createattrs
;
220 /* don't send windows slashes to the server */
222 for (p
= args
->target_set
; *p
; p
++) if (*p
== '\\') *p
= '/';
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
);
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
);
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
);
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
);
257 status
= nfs41_readlink(state
->session
, &state
->file
,
258 NFS41_MAX_PATH_LEN
, args
->target_get
.path
, &len
);
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
);
265 args
->target_get
.len
= (unsigned short)len
;
266 dprintf(2, "returning symlink target '%s'\n", args
->target_get
.path
);
272 static int marshall_symlink(unsigned char *buffer
, uint32_t *length
, nfs41_upcall
*upcall
)
274 symlink_upcall_args
*args
= &upcall
->args
.symlink
;
275 unsigned short len
= (args
->target_get
.len
+ 1) * sizeof(WCHAR
);
276 int status
= NO_ERROR
;
281 status
= safe_write(&buffer
, length
, &len
, sizeof(len
));
282 if (status
) goto out
;
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
;
295 const nfs41_upcall_op nfs41_op_symlink
= {