Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag '6.12-rc2-cifs-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb client fixes from Steve French:
"Two fixes for Windows symlink handling"

* tag '6.12-rc2-cifs-fixes' of git://git.samba.org/sfrench/cifs-2.6:
cifs: Fix creating native symlinks pointing to current or parent directory
cifs: Improve creating native symlinks pointing to directory

+178 -7
+14 -3
fs/smb/client/cifs_unicode.c
··· 484 484 /** 485 485 * Remap spaces and periods found at the end of every 486 486 * component of the path. The special cases of '.' and 487 - * '..' do not need to be dealt with explicitly because 488 - * they are addressed in namei.c:link_path_walk(). 487 + * '..' are need to be handled because of symlinks. 488 + * They are treated as non-end-of-string to avoid 489 + * remapping and breaking symlinks pointing to . or .. 489 490 **/ 490 - if ((i == srclen - 1) || (source[i+1] == '\\')) 491 + if ((i == 0 || source[i-1] == '\\') && 492 + source[i] == '.' && 493 + (i == srclen-1 || source[i+1] == '\\')) 494 + end_of_string = false; /* "." case */ 495 + else if (i >= 1 && 496 + (i == 1 || source[i-2] == '\\') && 497 + source[i-1] == '.' && 498 + source[i] == '.' && 499 + (i == srclen-1 || source[i+1] == '\\')) 500 + end_of_string = false; /* ".." case */ 501 + else if ((i == srclen - 1) || (source[i+1] == '\\')) 491 502 end_of_string = true; 492 503 else 493 504 end_of_string = false;
+161 -3
fs/smb/client/reparse.c
··· 14 14 #include "fs_context.h" 15 15 #include "reparse.h" 16 16 17 + static int detect_directory_symlink_target(struct cifs_sb_info *cifs_sb, 18 + const unsigned int xid, 19 + const char *full_path, 20 + const char *symname, 21 + bool *directory); 22 + 17 23 int smb2_create_reparse_symlink(const unsigned int xid, struct inode *inode, 18 24 struct dentry *dentry, struct cifs_tcon *tcon, 19 25 const char *full_path, const char *symname) ··· 30 24 struct inode *new; 31 25 struct kvec iov; 32 26 __le16 *path; 27 + bool directory; 33 28 char *sym, sep = CIFS_DIR_SEP(cifs_sb); 34 29 u16 len, plen; 35 30 int rc = 0; ··· 51 44 rc = -ENOMEM; 52 45 goto out; 53 46 } 47 + 48 + /* 49 + * SMB distinguish between symlink to directory and symlink to file. 50 + * They cannot be exchanged (symlink of file type which points to 51 + * directory cannot be resolved and vice-versa). Try to detect if 52 + * the symlink target could be a directory or not. When detection 53 + * fails then treat symlink as a file (non-directory) symlink. 54 + */ 55 + directory = false; 56 + rc = detect_directory_symlink_target(cifs_sb, xid, full_path, symname, &directory); 57 + if (rc < 0) 58 + goto out; 54 59 55 60 plen = 2 * UniStrnlen((wchar_t *)path, PATH_MAX); 56 61 len = sizeof(*buf) + plen * 2; ··· 88 69 iov.iov_base = buf; 89 70 iov.iov_len = len; 90 71 new = smb2_get_reparse_inode(&data, inode->i_sb, xid, 91 - tcon, full_path, &iov, NULL); 72 + tcon, full_path, directory, 73 + &iov, NULL); 92 74 if (!IS_ERR(new)) 93 75 d_instantiate(dentry, new); 94 76 else ··· 99 79 cifs_free_open_info(&data); 100 80 kfree(buf); 101 81 return rc; 82 + } 83 + 84 + static int detect_directory_symlink_target(struct cifs_sb_info *cifs_sb, 85 + const unsigned int xid, 86 + const char *full_path, 87 + const char *symname, 88 + bool *directory) 89 + { 90 + char sep = CIFS_DIR_SEP(cifs_sb); 91 + struct cifs_open_parms oparms; 92 + struct tcon_link *tlink; 93 + struct cifs_tcon *tcon; 94 + const char *basename; 95 + struct cifs_fid fid; 96 + char *resolved_path; 97 + int full_path_len; 98 + int basename_len; 99 + int symname_len; 100 + char *path_sep; 101 + __u32 oplock; 102 + int open_rc; 103 + 104 + /* 105 + * First do some simple check. If the original Linux symlink target ends 106 + * with slash, or last path component is dot or dot-dot then it is for 107 + * sure symlink to the directory. 108 + */ 109 + basename = kbasename(symname); 110 + basename_len = strlen(basename); 111 + if (basename_len == 0 || /* symname ends with slash */ 112 + (basename_len == 1 && basename[0] == '.') || /* last component is "." */ 113 + (basename_len == 2 && basename[0] == '.' && basename[1] == '.')) { /* or ".." */ 114 + *directory = true; 115 + return 0; 116 + } 117 + 118 + /* 119 + * For absolute symlinks it is not possible to determinate 120 + * if it should point to directory or file. 121 + */ 122 + if (symname[0] == '/') { 123 + cifs_dbg(FYI, 124 + "%s: cannot determinate if the symlink target path '%s' " 125 + "is directory or not, creating '%s' as file symlink\n", 126 + __func__, symname, full_path); 127 + return 0; 128 + } 129 + 130 + /* 131 + * If it was not detected as directory yet and the symlink is relative 132 + * then try to resolve the path on the SMB server, check if the path 133 + * exists and determinate if it is a directory or not. 134 + */ 135 + 136 + full_path_len = strlen(full_path); 137 + symname_len = strlen(symname); 138 + 139 + tlink = cifs_sb_tlink(cifs_sb); 140 + if (IS_ERR(tlink)) 141 + return PTR_ERR(tlink); 142 + 143 + resolved_path = kzalloc(full_path_len + symname_len + 1, GFP_KERNEL); 144 + if (!resolved_path) { 145 + cifs_put_tlink(tlink); 146 + return -ENOMEM; 147 + } 148 + 149 + /* 150 + * Compose the resolved SMB symlink path from the SMB full path 151 + * and Linux target symlink path. 152 + */ 153 + memcpy(resolved_path, full_path, full_path_len+1); 154 + path_sep = strrchr(resolved_path, sep); 155 + if (path_sep) 156 + path_sep++; 157 + else 158 + path_sep = resolved_path; 159 + memcpy(path_sep, symname, symname_len+1); 160 + if (sep == '\\') 161 + convert_delimiter(path_sep, sep); 162 + 163 + tcon = tlink_tcon(tlink); 164 + oparms = CIFS_OPARMS(cifs_sb, tcon, resolved_path, 165 + FILE_READ_ATTRIBUTES, FILE_OPEN, 0, ACL_NO_MODE); 166 + oparms.fid = &fid; 167 + 168 + /* Try to open as a directory (NOT_FILE) */ 169 + oplock = 0; 170 + oparms.create_options = cifs_create_options(cifs_sb, 171 + CREATE_NOT_FILE | OPEN_REPARSE_POINT); 172 + open_rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL); 173 + if (open_rc == 0) { 174 + /* Successful open means that the target path is definitely a directory. */ 175 + *directory = true; 176 + tcon->ses->server->ops->close(xid, tcon, &fid); 177 + } else if (open_rc == -ENOTDIR) { 178 + /* -ENOTDIR means that the target path is definitely a file. */ 179 + *directory = false; 180 + } else if (open_rc == -ENOENT) { 181 + /* -ENOENT means that the target path does not exist. */ 182 + cifs_dbg(FYI, 183 + "%s: symlink target path '%s' does not exist, " 184 + "creating '%s' as file symlink\n", 185 + __func__, symname, full_path); 186 + } else { 187 + /* Try to open as a file (NOT_DIR) */ 188 + oplock = 0; 189 + oparms.create_options = cifs_create_options(cifs_sb, 190 + CREATE_NOT_DIR | OPEN_REPARSE_POINT); 191 + open_rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL); 192 + if (open_rc == 0) { 193 + /* Successful open means that the target path is definitely a file. */ 194 + *directory = false; 195 + tcon->ses->server->ops->close(xid, tcon, &fid); 196 + } else if (open_rc == -EISDIR) { 197 + /* -EISDIR means that the target path is definitely a directory. */ 198 + *directory = true; 199 + } else { 200 + /* 201 + * This code branch is called when we do not have a permission to 202 + * open the resolved_path or some other client/process denied 203 + * opening the resolved_path. 204 + * 205 + * TODO: Try to use ops->query_dir_first on the parent directory 206 + * of resolved_path, search for basename of resolved_path and 207 + * check if the ATTR_DIRECTORY is set in fi.Attributes. In some 208 + * case this could work also when opening of the path is denied. 209 + */ 210 + cifs_dbg(FYI, 211 + "%s: cannot determinate if the symlink target path '%s' " 212 + "is directory or not, creating '%s' as file symlink\n", 213 + __func__, symname, full_path); 214 + } 215 + } 216 + 217 + kfree(resolved_path); 218 + cifs_put_tlink(tlink); 219 + return 0; 102 220 } 103 221 104 222 static int nfs_set_reparse_buf(struct reparse_posix_data *buf, ··· 295 137 }; 296 138 297 139 new = smb2_get_reparse_inode(&data, inode->i_sb, xid, 298 - tcon, full_path, &iov, NULL); 140 + tcon, full_path, false, &iov, NULL); 299 141 if (!IS_ERR(new)) 300 142 d_instantiate(dentry, new); 301 143 else ··· 441 283 data.wsl.eas_len = len; 442 284 443 285 new = smb2_get_reparse_inode(&data, inode->i_sb, 444 - xid, tcon, full_path, 286 + xid, tcon, full_path, false, 445 287 &reparse_iov, &xattr_iov); 446 288 if (!IS_ERR(new)) 447 289 d_instantiate(dentry, new);
+2 -1
fs/smb/client/smb2inode.c
··· 1198 1198 const unsigned int xid, 1199 1199 struct cifs_tcon *tcon, 1200 1200 const char *full_path, 1201 + bool directory, 1201 1202 struct kvec *reparse_iov, 1202 1203 struct kvec *xattr_iov) 1203 1204 { ··· 1218 1217 FILE_READ_ATTRIBUTES | 1219 1218 FILE_WRITE_ATTRIBUTES, 1220 1219 FILE_CREATE, 1221 - CREATE_NOT_DIR | OPEN_REPARSE_POINT, 1220 + (directory ? CREATE_NOT_FILE : CREATE_NOT_DIR) | OPEN_REPARSE_POINT, 1222 1221 ACL_NO_MODE); 1223 1222 if (xattr_iov) 1224 1223 oparms.ea_cctx = xattr_iov;
+1
fs/smb/client/smb2proto.h
··· 61 61 const unsigned int xid, 62 62 struct cifs_tcon *tcon, 63 63 const char *full_path, 64 + bool directory, 64 65 struct kvec *reparse_iov, 65 66 struct kvec *xattr_iov); 66 67 int smb2_query_reparse_point(const unsigned int xid,