diff options
Diffstat (limited to 'fs/cifs/link.c')
-rw-r--r-- | fs/cifs/link.c | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/fs/cifs/link.c b/fs/cifs/link.c new file mode 100644 index 000000000000..1455810ba1cb --- /dev/null +++ b/fs/cifs/link.c | |||
@@ -0,0 +1,328 @@ | |||
1 | /* | ||
2 | * fs/cifs/link.c | ||
3 | * | ||
4 | * Copyright (C) International Business Machines Corp., 2002,2003 | ||
5 | * Author(s): Steve French (sfrench@us.ibm.com) | ||
6 | * | ||
7 | * This library is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU Lesser General Public License as published | ||
9 | * by the Free Software Foundation; either version 2.1 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This library is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See | ||
15 | * the GNU Lesser General Public 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 | ||
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
20 | */ | ||
21 | #include <linux/fs.h> | ||
22 | #include <linux/stat.h> | ||
23 | #include <linux/namei.h> | ||
24 | #include "cifsfs.h" | ||
25 | #include "cifspdu.h" | ||
26 | #include "cifsglob.h" | ||
27 | #include "cifsproto.h" | ||
28 | #include "cifs_debug.h" | ||
29 | #include "cifs_fs_sb.h" | ||
30 | |||
31 | int | ||
32 | cifs_hardlink(struct dentry *old_file, struct inode *inode, | ||
33 | struct dentry *direntry) | ||
34 | { | ||
35 | int rc = -EACCES; | ||
36 | int xid; | ||
37 | char *fromName = NULL; | ||
38 | char *toName = NULL; | ||
39 | struct cifs_sb_info *cifs_sb_target; | ||
40 | struct cifsTconInfo *pTcon; | ||
41 | struct cifsInodeInfo *cifsInode; | ||
42 | |||
43 | xid = GetXid(); | ||
44 | |||
45 | cifs_sb_target = CIFS_SB(inode->i_sb); | ||
46 | pTcon = cifs_sb_target->tcon; | ||
47 | |||
48 | /* No need to check for cross device links since server will do that | ||
49 | BB note DFS case in future though (when we may have to check) */ | ||
50 | |||
51 | down(&inode->i_sb->s_vfs_rename_sem); | ||
52 | fromName = build_path_from_dentry(old_file); | ||
53 | toName = build_path_from_dentry(direntry); | ||
54 | up(&inode->i_sb->s_vfs_rename_sem); | ||
55 | if((fromName == NULL) || (toName == NULL)) { | ||
56 | rc = -ENOMEM; | ||
57 | goto cifs_hl_exit; | ||
58 | } | ||
59 | |||
60 | if (cifs_sb_target->tcon->ses->capabilities & CAP_UNIX) | ||
61 | rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName, | ||
62 | cifs_sb_target->local_nls); | ||
63 | else { | ||
64 | rc = CIFSCreateHardLink(xid, pTcon, fromName, toName, | ||
65 | cifs_sb_target->local_nls); | ||
66 | if(rc == -EIO) | ||
67 | rc = -EOPNOTSUPP; | ||
68 | } | ||
69 | |||
70 | /* if (!rc) */ | ||
71 | { | ||
72 | /* renew_parental_timestamps(old_file); | ||
73 | inode->i_nlink++; | ||
74 | mark_inode_dirty(inode); | ||
75 | d_instantiate(direntry, inode); */ | ||
76 | /* BB add call to either mark inode dirty or refresh its data and timestamp to current time */ | ||
77 | } | ||
78 | d_drop(direntry); /* force new lookup from server */ | ||
79 | cifsInode = CIFS_I(old_file->d_inode); | ||
80 | cifsInode->time = 0; /* will force revalidate to go get info when needed */ | ||
81 | |||
82 | cifs_hl_exit: | ||
83 | if (fromName) | ||
84 | kfree(fromName); | ||
85 | if (toName) | ||
86 | kfree(toName); | ||
87 | FreeXid(xid); | ||
88 | return rc; | ||
89 | } | ||
90 | |||
91 | int | ||
92 | cifs_follow_link(struct dentry *direntry, struct nameidata *nd) | ||
93 | { | ||
94 | struct inode *inode = direntry->d_inode; | ||
95 | int rc = -EACCES; | ||
96 | int xid; | ||
97 | char *full_path = NULL; | ||
98 | char * target_path = ERR_PTR(-ENOMEM); | ||
99 | struct cifs_sb_info *cifs_sb; | ||
100 | struct cifsTconInfo *pTcon; | ||
101 | |||
102 | xid = GetXid(); | ||
103 | |||
104 | down(&direntry->d_sb->s_vfs_rename_sem); | ||
105 | full_path = build_path_from_dentry(direntry); | ||
106 | up(&direntry->d_sb->s_vfs_rename_sem); | ||
107 | |||
108 | if (!full_path) | ||
109 | goto out_no_free; | ||
110 | |||
111 | cFYI(1, ("Full path: %s inode = 0x%p", full_path, inode)); | ||
112 | cifs_sb = CIFS_SB(inode->i_sb); | ||
113 | pTcon = cifs_sb->tcon; | ||
114 | target_path = kmalloc(PATH_MAX, GFP_KERNEL); | ||
115 | if (!target_path) { | ||
116 | target_path = ERR_PTR(-ENOMEM); | ||
117 | goto out; | ||
118 | } | ||
119 | |||
120 | /* BB add read reparse point symlink code and Unix extensions symlink code here BB */ | ||
121 | if (pTcon->ses->capabilities & CAP_UNIX) | ||
122 | rc = CIFSSMBUnixQuerySymLink(xid, pTcon, full_path, | ||
123 | target_path, | ||
124 | PATH_MAX-1, | ||
125 | cifs_sb->local_nls); | ||
126 | else { | ||
127 | /* rc = CIFSSMBQueryReparseLinkInfo */ | ||
128 | /* BB Add code to Query ReparsePoint info */ | ||
129 | /* BB Add MAC style xsymlink check here if enabled */ | ||
130 | } | ||
131 | |||
132 | if (rc == 0) { | ||
133 | |||
134 | /* BB Add special case check for Samba DFS symlinks */ | ||
135 | |||
136 | target_path[PATH_MAX-1] = 0; | ||
137 | } else { | ||
138 | kfree(target_path); | ||
139 | target_path = ERR_PTR(rc); | ||
140 | } | ||
141 | |||
142 | out: | ||
143 | kfree(full_path); | ||
144 | out_no_free: | ||
145 | FreeXid(xid); | ||
146 | nd_set_link(nd, target_path); | ||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | int | ||
151 | cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) | ||
152 | { | ||
153 | int rc = -EOPNOTSUPP; | ||
154 | int xid; | ||
155 | struct cifs_sb_info *cifs_sb; | ||
156 | struct cifsTconInfo *pTcon; | ||
157 | char *full_path = NULL; | ||
158 | struct inode *newinode = NULL; | ||
159 | |||
160 | xid = GetXid(); | ||
161 | |||
162 | cifs_sb = CIFS_SB(inode->i_sb); | ||
163 | pTcon = cifs_sb->tcon; | ||
164 | |||
165 | down(&inode->i_sb->s_vfs_rename_sem); | ||
166 | full_path = build_path_from_dentry(direntry); | ||
167 | up(&inode->i_sb->s_vfs_rename_sem); | ||
168 | |||
169 | if(full_path == NULL) { | ||
170 | FreeXid(xid); | ||
171 | return -ENOMEM; | ||
172 | } | ||
173 | |||
174 | cFYI(1, ("Full path: %s ", full_path)); | ||
175 | cFYI(1, ("symname is %s", symname)); | ||
176 | |||
177 | /* BB what if DFS and this volume is on different share? BB */ | ||
178 | if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) | ||
179 | rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname, | ||
180 | cifs_sb->local_nls); | ||
181 | /* else | ||
182 | rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName,cifs_sb_target->local_nls); */ | ||
183 | |||
184 | if (rc == 0) { | ||
185 | if (pTcon->ses->capabilities & CAP_UNIX) | ||
186 | rc = cifs_get_inode_info_unix(&newinode, full_path, | ||
187 | inode->i_sb,xid); | ||
188 | else | ||
189 | rc = cifs_get_inode_info(&newinode, full_path, NULL, | ||
190 | inode->i_sb,xid); | ||
191 | |||
192 | if (rc != 0) { | ||
193 | cFYI(1, | ||
194 | ("Create symlink worked but get_inode_info failed with rc = %d ", | ||
195 | rc)); | ||
196 | } else { | ||
197 | direntry->d_op = &cifs_dentry_ops; | ||
198 | d_instantiate(direntry, newinode); | ||
199 | } | ||
200 | } | ||
201 | |||
202 | if (full_path) | ||
203 | kfree(full_path); | ||
204 | FreeXid(xid); | ||
205 | return rc; | ||
206 | } | ||
207 | |||
208 | int | ||
209 | cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen) | ||
210 | { | ||
211 | struct inode *inode = direntry->d_inode; | ||
212 | int rc = -EACCES; | ||
213 | int xid; | ||
214 | int oplock = FALSE; | ||
215 | struct cifs_sb_info *cifs_sb; | ||
216 | struct cifsTconInfo *pTcon; | ||
217 | char *full_path = NULL; | ||
218 | char *tmp_path = NULL; | ||
219 | char * tmpbuffer; | ||
220 | unsigned char * referrals = NULL; | ||
221 | int num_referrals = 0; | ||
222 | int len; | ||
223 | __u16 fid; | ||
224 | |||
225 | xid = GetXid(); | ||
226 | cifs_sb = CIFS_SB(inode->i_sb); | ||
227 | pTcon = cifs_sb->tcon; | ||
228 | |||
229 | /* BB would it be safe against deadlock to grab this sem | ||
230 | even though rename itself grabs the sem and calls lookup? */ | ||
231 | /* down(&inode->i_sb->s_vfs_rename_sem);*/ | ||
232 | full_path = build_path_from_dentry(direntry); | ||
233 | /* up(&inode->i_sb->s_vfs_rename_sem);*/ | ||
234 | |||
235 | if(full_path == NULL) { | ||
236 | FreeXid(xid); | ||
237 | return -ENOMEM; | ||
238 | } | ||
239 | |||
240 | cFYI(1, | ||
241 | ("Full path: %s inode = 0x%p pBuffer = 0x%p buflen = %d", | ||
242 | full_path, inode, pBuffer, buflen)); | ||
243 | if(buflen > PATH_MAX) | ||
244 | len = PATH_MAX; | ||
245 | else | ||
246 | len = buflen; | ||
247 | tmpbuffer = kmalloc(len,GFP_KERNEL); | ||
248 | if(tmpbuffer == NULL) { | ||
249 | if (full_path) | ||
250 | kfree(full_path); | ||
251 | FreeXid(xid); | ||
252 | return -ENOMEM; | ||
253 | } | ||
254 | |||
255 | /* BB add read reparse point symlink code and Unix extensions symlink code here BB */ | ||
256 | if (cifs_sb->tcon->ses->capabilities & CAP_UNIX) | ||
257 | rc = CIFSSMBUnixQuerySymLink(xid, pTcon, full_path, | ||
258 | tmpbuffer, | ||
259 | len - 1, | ||
260 | cifs_sb->local_nls); | ||
261 | else { | ||
262 | rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, GENERIC_READ, | ||
263 | OPEN_REPARSE_POINT,&fid, &oplock, NULL, cifs_sb->local_nls); | ||
264 | if(!rc) { | ||
265 | rc = CIFSSMBQueryReparseLinkInfo(xid, pTcon, full_path, | ||
266 | tmpbuffer, | ||
267 | len - 1, | ||
268 | fid, | ||
269 | cifs_sb->local_nls); | ||
270 | if(CIFSSMBClose(xid, pTcon, fid)) { | ||
271 | cFYI(1,("Error closing junction point (open for ioctl)")); | ||
272 | } | ||
273 | if(rc == -EIO) { | ||
274 | /* Query if DFS Junction */ | ||
275 | tmp_path = | ||
276 | kmalloc(MAX_TREE_SIZE + MAX_PATHCONF + 1, | ||
277 | GFP_KERNEL); | ||
278 | if (tmp_path) { | ||
279 | strncpy(tmp_path, pTcon->treeName, MAX_TREE_SIZE); | ||
280 | strncat(tmp_path, full_path, MAX_PATHCONF); | ||
281 | rc = get_dfs_path(xid, pTcon->ses, tmp_path, | ||
282 | cifs_sb->local_nls, &num_referrals, &referrals); | ||
283 | cFYI(1,("Get DFS for %s rc = %d ",tmp_path, rc)); | ||
284 | if((num_referrals == 0) && (rc == 0)) | ||
285 | rc = -EACCES; | ||
286 | else { | ||
287 | cFYI(1,("num referral: %d",num_referrals)); | ||
288 | if(referrals) { | ||
289 | cFYI(1,("referral string: %s ",referrals)); | ||
290 | strncpy(tmpbuffer, referrals, len-1); | ||
291 | } | ||
292 | } | ||
293 | if(referrals) | ||
294 | kfree(referrals); | ||
295 | kfree(tmp_path); | ||
296 | } | ||
297 | /* BB add code like else decode referrals then memcpy to | ||
298 | tmpbuffer and free referrals string array BB */ | ||
299 | } | ||
300 | } | ||
301 | } | ||
302 | /* BB Anything else to do to handle recursive links? */ | ||
303 | /* BB Should we be using page ops here? */ | ||
304 | |||
305 | /* BB null terminate returned string in pBuffer? BB */ | ||
306 | if (rc == 0) { | ||
307 | rc = vfs_readlink(direntry, pBuffer, len, tmpbuffer); | ||
308 | cFYI(1, | ||
309 | ("vfs_readlink called from cifs_readlink returned %d", | ||
310 | rc)); | ||
311 | } | ||
312 | |||
313 | if (tmpbuffer) { | ||
314 | kfree(tmpbuffer); | ||
315 | } | ||
316 | if (full_path) { | ||
317 | kfree(full_path); | ||
318 | } | ||
319 | FreeXid(xid); | ||
320 | return rc; | ||
321 | } | ||
322 | |||
323 | void cifs_put_link(struct dentry *direntry, struct nameidata *nd) | ||
324 | { | ||
325 | char *p = nd_get_link(nd); | ||
326 | if (!IS_ERR(p)) | ||
327 | kfree(p); | ||
328 | } | ||