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