Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/fs/cifs/link.c
15109 views
1
/*
2
* fs/cifs/link.c
3
*
4
* Copyright (C) International Business Machines Corp., 2002,2008
5
* Author(s): Steve French ([email protected])
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/slab.h>
24
#include <linux/namei.h>
25
#include "cifsfs.h"
26
#include "cifspdu.h"
27
#include "cifsglob.h"
28
#include "cifsproto.h"
29
#include "cifs_debug.h"
30
#include "cifs_fs_sb.h"
31
32
#define CIFS_MF_SYMLINK_LEN_OFFSET (4+1)
33
#define CIFS_MF_SYMLINK_MD5_OFFSET (CIFS_MF_SYMLINK_LEN_OFFSET+(4+1))
34
#define CIFS_MF_SYMLINK_LINK_OFFSET (CIFS_MF_SYMLINK_MD5_OFFSET+(32+1))
35
#define CIFS_MF_SYMLINK_LINK_MAXLEN (1024)
36
#define CIFS_MF_SYMLINK_FILE_SIZE \
37
(CIFS_MF_SYMLINK_LINK_OFFSET + CIFS_MF_SYMLINK_LINK_MAXLEN)
38
39
#define CIFS_MF_SYMLINK_LEN_FORMAT "XSym\n%04u\n"
40
#define CIFS_MF_SYMLINK_MD5_FORMAT \
41
"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n"
42
#define CIFS_MF_SYMLINK_MD5_ARGS(md5_hash) \
43
md5_hash[0], md5_hash[1], md5_hash[2], md5_hash[3], \
44
md5_hash[4], md5_hash[5], md5_hash[6], md5_hash[7], \
45
md5_hash[8], md5_hash[9], md5_hash[10], md5_hash[11],\
46
md5_hash[12], md5_hash[13], md5_hash[14], md5_hash[15]
47
48
static int
49
symlink_hash(unsigned int link_len, const char *link_str, u8 *md5_hash)
50
{
51
int rc;
52
unsigned int size;
53
struct crypto_shash *md5;
54
struct sdesc *sdescmd5;
55
56
md5 = crypto_alloc_shash("md5", 0, 0);
57
if (IS_ERR(md5)) {
58
rc = PTR_ERR(md5);
59
cERROR(1, "%s: Crypto md5 allocation error %d\n", __func__, rc);
60
return rc;
61
}
62
size = sizeof(struct shash_desc) + crypto_shash_descsize(md5);
63
sdescmd5 = kmalloc(size, GFP_KERNEL);
64
if (!sdescmd5) {
65
rc = -ENOMEM;
66
cERROR(1, "%s: Memory allocation failure\n", __func__);
67
goto symlink_hash_err;
68
}
69
sdescmd5->shash.tfm = md5;
70
sdescmd5->shash.flags = 0x0;
71
72
rc = crypto_shash_init(&sdescmd5->shash);
73
if (rc) {
74
cERROR(1, "%s: Could not init md5 shash\n", __func__);
75
goto symlink_hash_err;
76
}
77
crypto_shash_update(&sdescmd5->shash, link_str, link_len);
78
rc = crypto_shash_final(&sdescmd5->shash, md5_hash);
79
80
symlink_hash_err:
81
crypto_free_shash(md5);
82
kfree(sdescmd5);
83
84
return rc;
85
}
86
87
static int
88
CIFSParseMFSymlink(const u8 *buf,
89
unsigned int buf_len,
90
unsigned int *_link_len,
91
char **_link_str)
92
{
93
int rc;
94
unsigned int link_len;
95
const char *md5_str1;
96
const char *link_str;
97
u8 md5_hash[16];
98
char md5_str2[34];
99
100
if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE)
101
return -EINVAL;
102
103
md5_str1 = (const char *)&buf[CIFS_MF_SYMLINK_MD5_OFFSET];
104
link_str = (const char *)&buf[CIFS_MF_SYMLINK_LINK_OFFSET];
105
106
rc = sscanf(buf, CIFS_MF_SYMLINK_LEN_FORMAT, &link_len);
107
if (rc != 1)
108
return -EINVAL;
109
110
rc = symlink_hash(link_len, link_str, md5_hash);
111
if (rc) {
112
cFYI(1, "%s: MD5 hash failure: %d\n", __func__, rc);
113
return rc;
114
}
115
116
snprintf(md5_str2, sizeof(md5_str2),
117
CIFS_MF_SYMLINK_MD5_FORMAT,
118
CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));
119
120
if (strncmp(md5_str1, md5_str2, 17) != 0)
121
return -EINVAL;
122
123
if (_link_str) {
124
*_link_str = kstrndup(link_str, link_len, GFP_KERNEL);
125
if (!*_link_str)
126
return -ENOMEM;
127
}
128
129
*_link_len = link_len;
130
return 0;
131
}
132
133
static int
134
CIFSFormatMFSymlink(u8 *buf, unsigned int buf_len, const char *link_str)
135
{
136
int rc;
137
unsigned int link_len;
138
unsigned int ofs;
139
u8 md5_hash[16];
140
141
if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE)
142
return -EINVAL;
143
144
link_len = strlen(link_str);
145
146
if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN)
147
return -ENAMETOOLONG;
148
149
rc = symlink_hash(link_len, link_str, md5_hash);
150
if (rc) {
151
cFYI(1, "%s: MD5 hash failure: %d\n", __func__, rc);
152
return rc;
153
}
154
155
snprintf(buf, buf_len,
156
CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT,
157
link_len,
158
CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));
159
160
ofs = CIFS_MF_SYMLINK_LINK_OFFSET;
161
memcpy(buf + ofs, link_str, link_len);
162
163
ofs += link_len;
164
if (ofs < CIFS_MF_SYMLINK_FILE_SIZE) {
165
buf[ofs] = '\n';
166
ofs++;
167
}
168
169
while (ofs < CIFS_MF_SYMLINK_FILE_SIZE) {
170
buf[ofs] = ' ';
171
ofs++;
172
}
173
174
return 0;
175
}
176
177
static int
178
CIFSCreateMFSymLink(const int xid, struct cifs_tcon *tcon,
179
const char *fromName, const char *toName,
180
const struct nls_table *nls_codepage, int remap)
181
{
182
int rc;
183
int oplock = 0;
184
__u16 netfid = 0;
185
u8 *buf;
186
unsigned int bytes_written = 0;
187
struct cifs_io_parms io_parms;
188
189
buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
190
if (!buf)
191
return -ENOMEM;
192
193
rc = CIFSFormatMFSymlink(buf, CIFS_MF_SYMLINK_FILE_SIZE, toName);
194
if (rc != 0) {
195
kfree(buf);
196
return rc;
197
}
198
199
rc = CIFSSMBOpen(xid, tcon, fromName, FILE_CREATE, GENERIC_WRITE,
200
CREATE_NOT_DIR, &netfid, &oplock, NULL,
201
nls_codepage, remap);
202
if (rc != 0) {
203
kfree(buf);
204
return rc;
205
}
206
207
io_parms.netfid = netfid;
208
io_parms.pid = current->tgid;
209
io_parms.tcon = tcon;
210
io_parms.offset = 0;
211
io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;
212
213
rc = CIFSSMBWrite(xid, &io_parms, &bytes_written, buf, NULL, 0);
214
CIFSSMBClose(xid, tcon, netfid);
215
kfree(buf);
216
if (rc != 0)
217
return rc;
218
219
if (bytes_written != CIFS_MF_SYMLINK_FILE_SIZE)
220
return -EIO;
221
222
return 0;
223
}
224
225
static int
226
CIFSQueryMFSymLink(const int xid, struct cifs_tcon *tcon,
227
const unsigned char *searchName, char **symlinkinfo,
228
const struct nls_table *nls_codepage, int remap)
229
{
230
int rc;
231
int oplock = 0;
232
__u16 netfid = 0;
233
u8 *buf;
234
char *pbuf;
235
unsigned int bytes_read = 0;
236
int buf_type = CIFS_NO_BUFFER;
237
unsigned int link_len = 0;
238
struct cifs_io_parms io_parms;
239
FILE_ALL_INFO file_info;
240
241
rc = CIFSSMBOpen(xid, tcon, searchName, FILE_OPEN, GENERIC_READ,
242
CREATE_NOT_DIR, &netfid, &oplock, &file_info,
243
nls_codepage, remap);
244
if (rc != 0)
245
return rc;
246
247
if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
248
CIFSSMBClose(xid, tcon, netfid);
249
/* it's not a symlink */
250
return -EINVAL;
251
}
252
253
buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
254
if (!buf)
255
return -ENOMEM;
256
pbuf = buf;
257
io_parms.netfid = netfid;
258
io_parms.pid = current->tgid;
259
io_parms.tcon = tcon;
260
io_parms.offset = 0;
261
io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;
262
263
rc = CIFSSMBRead(xid, &io_parms, &bytes_read, &pbuf, &buf_type);
264
CIFSSMBClose(xid, tcon, netfid);
265
if (rc != 0) {
266
kfree(buf);
267
return rc;
268
}
269
270
rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, symlinkinfo);
271
kfree(buf);
272
if (rc != 0)
273
return rc;
274
275
return 0;
276
}
277
278
bool
279
CIFSCouldBeMFSymlink(const struct cifs_fattr *fattr)
280
{
281
if (!(fattr->cf_mode & S_IFREG))
282
/* it's not a symlink */
283
return false;
284
285
if (fattr->cf_eof != CIFS_MF_SYMLINK_FILE_SIZE)
286
/* it's not a symlink */
287
return false;
288
289
return true;
290
}
291
292
int
293
CIFSCheckMFSymlink(struct cifs_fattr *fattr,
294
const unsigned char *path,
295
struct cifs_sb_info *cifs_sb, int xid)
296
{
297
int rc;
298
int oplock = 0;
299
__u16 netfid = 0;
300
struct tcon_link *tlink;
301
struct cifs_tcon *pTcon;
302
struct cifs_io_parms io_parms;
303
u8 *buf;
304
char *pbuf;
305
unsigned int bytes_read = 0;
306
int buf_type = CIFS_NO_BUFFER;
307
unsigned int link_len = 0;
308
FILE_ALL_INFO file_info;
309
310
if (!CIFSCouldBeMFSymlink(fattr))
311
/* it's not a symlink */
312
return 0;
313
314
tlink = cifs_sb_tlink(cifs_sb);
315
if (IS_ERR(tlink))
316
return PTR_ERR(tlink);
317
pTcon = tlink_tcon(tlink);
318
319
rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ,
320
CREATE_NOT_DIR, &netfid, &oplock, &file_info,
321
cifs_sb->local_nls,
322
cifs_sb->mnt_cifs_flags &
323
CIFS_MOUNT_MAP_SPECIAL_CHR);
324
if (rc != 0)
325
goto out;
326
327
if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) {
328
CIFSSMBClose(xid, pTcon, netfid);
329
/* it's not a symlink */
330
goto out;
331
}
332
333
buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
334
if (!buf) {
335
rc = -ENOMEM;
336
goto out;
337
}
338
pbuf = buf;
339
io_parms.netfid = netfid;
340
io_parms.pid = current->tgid;
341
io_parms.tcon = pTcon;
342
io_parms.offset = 0;
343
io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE;
344
345
rc = CIFSSMBRead(xid, &io_parms, &bytes_read, &pbuf, &buf_type);
346
CIFSSMBClose(xid, pTcon, netfid);
347
if (rc != 0) {
348
kfree(buf);
349
goto out;
350
}
351
352
rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, NULL);
353
kfree(buf);
354
if (rc == -EINVAL) {
355
/* it's not a symlink */
356
rc = 0;
357
goto out;
358
}
359
360
if (rc != 0)
361
goto out;
362
363
/* it is a symlink */
364
fattr->cf_eof = link_len;
365
fattr->cf_mode &= ~S_IFMT;
366
fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
367
fattr->cf_dtype = DT_LNK;
368
out:
369
cifs_put_tlink(tlink);
370
return rc;
371
}
372
373
int
374
cifs_hardlink(struct dentry *old_file, struct inode *inode,
375
struct dentry *direntry)
376
{
377
int rc = -EACCES;
378
int xid;
379
char *fromName = NULL;
380
char *toName = NULL;
381
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
382
struct tcon_link *tlink;
383
struct cifs_tcon *pTcon;
384
struct cifsInodeInfo *cifsInode;
385
386
tlink = cifs_sb_tlink(cifs_sb);
387
if (IS_ERR(tlink))
388
return PTR_ERR(tlink);
389
pTcon = tlink_tcon(tlink);
390
391
xid = GetXid();
392
393
fromName = build_path_from_dentry(old_file);
394
toName = build_path_from_dentry(direntry);
395
if ((fromName == NULL) || (toName == NULL)) {
396
rc = -ENOMEM;
397
goto cifs_hl_exit;
398
}
399
400
if (pTcon->unix_ext)
401
rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName,
402
cifs_sb->local_nls,
403
cifs_sb->mnt_cifs_flags &
404
CIFS_MOUNT_MAP_SPECIAL_CHR);
405
else {
406
rc = CIFSCreateHardLink(xid, pTcon, fromName, toName,
407
cifs_sb->local_nls,
408
cifs_sb->mnt_cifs_flags &
409
CIFS_MOUNT_MAP_SPECIAL_CHR);
410
if ((rc == -EIO) || (rc == -EINVAL))
411
rc = -EOPNOTSUPP;
412
}
413
414
d_drop(direntry); /* force new lookup from server of target */
415
416
/* if source file is cached (oplocked) revalidate will not go to server
417
until the file is closed or oplock broken so update nlinks locally */
418
if (old_file->d_inode) {
419
cifsInode = CIFS_I(old_file->d_inode);
420
if (rc == 0) {
421
old_file->d_inode->i_nlink++;
422
/* BB should we make this contingent on superblock flag NOATIME? */
423
/* old_file->d_inode->i_ctime = CURRENT_TIME;*/
424
/* parent dir timestamps will update from srv
425
within a second, would it really be worth it
426
to set the parent dir cifs inode time to zero
427
to force revalidate (faster) for it too? */
428
}
429
/* if not oplocked will force revalidate to get info
430
on source file from srv */
431
cifsInode->time = 0;
432
433
/* Will update parent dir timestamps from srv within a second.
434
Would it really be worth it to set the parent dir (cifs
435
inode) time field to zero to force revalidate on parent
436
directory faster ie
437
CIFS_I(inode)->time = 0; */
438
}
439
440
cifs_hl_exit:
441
kfree(fromName);
442
kfree(toName);
443
FreeXid(xid);
444
cifs_put_tlink(tlink);
445
return rc;
446
}
447
448
void *
449
cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
450
{
451
struct inode *inode = direntry->d_inode;
452
int rc = -ENOMEM;
453
int xid;
454
char *full_path = NULL;
455
char *target_path = NULL;
456
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
457
struct tcon_link *tlink = NULL;
458
struct cifs_tcon *tcon;
459
460
xid = GetXid();
461
462
tlink = cifs_sb_tlink(cifs_sb);
463
if (IS_ERR(tlink)) {
464
rc = PTR_ERR(tlink);
465
tlink = NULL;
466
goto out;
467
}
468
tcon = tlink_tcon(tlink);
469
470
/*
471
* For now, we just handle symlinks with unix extensions enabled.
472
* Eventually we should handle NTFS reparse points, and MacOS
473
* symlink support. For instance...
474
*
475
* rc = CIFSSMBQueryReparseLinkInfo(...)
476
*
477
* For now, just return -EACCES when the server doesn't support posix
478
* extensions. Note that we still allow querying symlinks when posix
479
* extensions are manually disabled. We could disable these as well
480
* but there doesn't seem to be any harm in allowing the client to
481
* read them.
482
*/
483
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
484
&& !(tcon->ses->capabilities & CAP_UNIX)) {
485
rc = -EACCES;
486
goto out;
487
}
488
489
full_path = build_path_from_dentry(direntry);
490
if (!full_path)
491
goto out;
492
493
cFYI(1, "Full path: %s inode = 0x%p", full_path, inode);
494
495
rc = -EACCES;
496
/*
497
* First try Minshall+French Symlinks, if configured
498
* and fallback to UNIX Extensions Symlinks.
499
*/
500
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
501
rc = CIFSQueryMFSymLink(xid, tcon, full_path, &target_path,
502
cifs_sb->local_nls,
503
cifs_sb->mnt_cifs_flags &
504
CIFS_MOUNT_MAP_SPECIAL_CHR);
505
506
if ((rc != 0) && (tcon->ses->capabilities & CAP_UNIX))
507
rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path,
508
cifs_sb->local_nls);
509
510
kfree(full_path);
511
out:
512
if (rc != 0) {
513
kfree(target_path);
514
target_path = ERR_PTR(rc);
515
}
516
517
FreeXid(xid);
518
if (tlink)
519
cifs_put_tlink(tlink);
520
nd_set_link(nd, target_path);
521
return NULL;
522
}
523
524
int
525
cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
526
{
527
int rc = -EOPNOTSUPP;
528
int xid;
529
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
530
struct tcon_link *tlink;
531
struct cifs_tcon *pTcon;
532
char *full_path = NULL;
533
struct inode *newinode = NULL;
534
535
xid = GetXid();
536
537
tlink = cifs_sb_tlink(cifs_sb);
538
if (IS_ERR(tlink)) {
539
rc = PTR_ERR(tlink);
540
goto symlink_exit;
541
}
542
pTcon = tlink_tcon(tlink);
543
544
full_path = build_path_from_dentry(direntry);
545
if (full_path == NULL) {
546
rc = -ENOMEM;
547
goto symlink_exit;
548
}
549
550
cFYI(1, "Full path: %s", full_path);
551
cFYI(1, "symname is %s", symname);
552
553
/* BB what if DFS and this volume is on different share? BB */
554
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
555
rc = CIFSCreateMFSymLink(xid, pTcon, full_path, symname,
556
cifs_sb->local_nls,
557
cifs_sb->mnt_cifs_flags &
558
CIFS_MOUNT_MAP_SPECIAL_CHR);
559
else if (pTcon->unix_ext)
560
rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
561
cifs_sb->local_nls);
562
/* else
563
rc = CIFSCreateReparseSymLink(xid, pTcon, fromName, toName,
564
cifs_sb_target->local_nls); */
565
566
if (rc == 0) {
567
if (pTcon->unix_ext)
568
rc = cifs_get_inode_info_unix(&newinode, full_path,
569
inode->i_sb, xid);
570
else
571
rc = cifs_get_inode_info(&newinode, full_path, NULL,
572
inode->i_sb, xid, NULL);
573
574
if (rc != 0) {
575
cFYI(1, "Create symlink ok, getinodeinfo fail rc = %d",
576
rc);
577
} else {
578
d_instantiate(direntry, newinode);
579
}
580
}
581
symlink_exit:
582
kfree(full_path);
583
cifs_put_tlink(tlink);
584
FreeXid(xid);
585
return rc;
586
}
587
588
void cifs_put_link(struct dentry *direntry, struct nameidata *nd, void *cookie)
589
{
590
char *p = nd_get_link(nd);
591
if (!IS_ERR(p))
592
kfree(p);
593
}
594
595