Skip to content

Commit 76894f3

Browse files
pcacjrSteve French
authored and
Steve French
committed
cifs: improve symlink handling for smb2+
When creating inode for symlink, the client used to send below requests to fill it in: * create+query_info+close (STATUS_STOPPED_ON_SYMLINK) * create(+reparse_flag)+query_info+close (set file attrs) * create+ioctl(get_reparse)+close (query reparse tag) and then for every access to the symlink dentry, the ->link() method would send another: * create+ioctl(get_reparse)+close (parse symlink) So, in order to improve: (i) Get rid of unnecessary roundtrips and then resolve symlinks as follows: * create+query_info+close (STATUS_STOPPED_ON_SYMLINK + parse symlink + get reparse tag) * create(+reparse_flag)+query_info+close (set file attrs) (ii) Set the resolved symlink target directly in inode->i_link and use simple_get_link() for ->link() to simply return it. Signed-off-by: Paulo Alcantara (SUSE) <[email protected]> Reviewed-by: Ronnie Sahlberg <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 977bb65 commit 76894f3

14 files changed

+451
-453
lines changed

fs/cifs/cifsfs.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,7 @@ cifs_alloc_inode(struct super_block *sb)
396396
cifs_inode->epoch = 0;
397397
spin_lock_init(&cifs_inode->open_file_lock);
398398
generate_random_uuid(cifs_inode->lease_key);
399+
cifs_inode->symlink_target = NULL;
399400

400401
/*
401402
* Can not set i_flags here - they get immediately overwritten to zero
@@ -412,7 +413,11 @@ cifs_alloc_inode(struct super_block *sb)
412413
static void
413414
cifs_free_inode(struct inode *inode)
414415
{
415-
kmem_cache_free(cifs_inode_cachep, CIFS_I(inode));
416+
struct cifsInodeInfo *cinode = CIFS_I(inode);
417+
418+
if (S_ISLNK(inode->i_mode))
419+
kfree(cinode->symlink_target);
420+
kmem_cache_free(cifs_inode_cachep, cinode);
416421
}
417422

418423
static void
@@ -1139,7 +1144,7 @@ const struct inode_operations cifs_file_inode_ops = {
11391144
};
11401145

11411146
const struct inode_operations cifs_symlink_inode_ops = {
1142-
.get_link = cifs_get_link,
1147+
.get_link = simple_get_link,
11431148
.permission = cifs_permission,
11441149
.listxattr = cifs_listxattr,
11451150
};

fs/cifs/cifsglob.h

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,19 @@ struct cifs_cred {
185185
struct cifs_ace *aces;
186186
};
187187

188+
struct cifs_open_info_data {
189+
char *symlink_target;
190+
union {
191+
struct smb2_file_all_info fi;
192+
struct smb311_posix_qinfo posix_fi;
193+
};
194+
};
195+
196+
static inline void cifs_free_open_info(struct cifs_open_info_data *data)
197+
{
198+
kfree(data->symlink_target);
199+
}
200+
188201
/*
189202
*****************************************************************
190203
* Except the CIFS PDUs themselves all the
@@ -307,20 +320,20 @@ struct smb_version_operations {
307320
int (*is_path_accessible)(const unsigned int, struct cifs_tcon *,
308321
struct cifs_sb_info *, const char *);
309322
/* query path data from the server */
310-
int (*query_path_info)(const unsigned int, struct cifs_tcon *,
311-
struct cifs_sb_info *, const char *,
312-
FILE_ALL_INFO *, bool *, bool *);
323+
int (*query_path_info)(const unsigned int xid, struct cifs_tcon *tcon,
324+
struct cifs_sb_info *cifs_sb, const char *full_path,
325+
struct cifs_open_info_data *data, bool *adjust_tz, bool *reparse);
313326
/* query file data from the server */
314-
int (*query_file_info)(const unsigned int, struct cifs_tcon *,
315-
struct cifs_fid *, FILE_ALL_INFO *);
327+
int (*query_file_info)(const unsigned int xid, struct cifs_tcon *tcon,
328+
struct cifsFileInfo *cfile, struct cifs_open_info_data *data);
316329
/* query reparse tag from srv to determine which type of special file */
317330
int (*query_reparse_tag)(const unsigned int xid, struct cifs_tcon *tcon,
318331
struct cifs_sb_info *cifs_sb, const char *path,
319332
__u32 *reparse_tag);
320333
/* get server index number */
321-
int (*get_srv_inum)(const unsigned int, struct cifs_tcon *,
322-
struct cifs_sb_info *, const char *,
323-
u64 *uniqueid, FILE_ALL_INFO *);
334+
int (*get_srv_inum)(const unsigned int xid, struct cifs_tcon *tcon,
335+
struct cifs_sb_info *cifs_sb, const char *full_path, u64 *uniqueid,
336+
struct cifs_open_info_data *data);
324337
/* set size by path */
325338
int (*set_path_size)(const unsigned int, struct cifs_tcon *,
326339
const char *, __u64, struct cifs_sb_info *, bool);
@@ -369,8 +382,8 @@ struct smb_version_operations {
369382
struct cifs_sb_info *, const char *,
370383
char **, bool);
371384
/* open a file for non-posix mounts */
372-
int (*open)(const unsigned int, struct cifs_open_parms *,
373-
__u32 *, FILE_ALL_INFO *);
385+
int (*open)(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock,
386+
void *buf);
374387
/* set fid protocol-specific info */
375388
void (*set_fid)(struct cifsFileInfo *, struct cifs_fid *, __u32);
376389
/* close a file */
@@ -1123,6 +1136,7 @@ struct cifs_fattr {
11231136
struct timespec64 cf_mtime;
11241137
struct timespec64 cf_ctime;
11251138
u32 cf_cifstag;
1139+
char *cf_symlink_target;
11261140
};
11271141

11281142
/*
@@ -1385,6 +1399,7 @@ struct cifsFileInfo {
13851399
struct work_struct put; /* work for the final part of _put */
13861400
struct delayed_work deferred;
13871401
bool deferred_close_scheduled; /* Flag to indicate close is scheduled */
1402+
char *symlink_target;
13881403
};
13891404

13901405
struct cifs_io_parms {
@@ -1543,6 +1558,7 @@ struct cifsInodeInfo {
15431558
struct list_head deferred_closes; /* list of deferred closes */
15441559
spinlock_t deferred_lock; /* protection on deferred list */
15451560
bool lease_granted; /* Flag to indicate whether lease or oplock is granted. */
1561+
char *symlink_target;
15461562
};
15471563

15481564
static inline struct cifsInodeInfo *
@@ -2111,4 +2127,14 @@ static inline size_t ntlmssp_workstation_name_size(const struct cifs_ses *ses)
21112127
return sizeof(ses->workstation_name);
21122128
}
21132129

2130+
static inline void move_cifs_info_to_smb2(struct smb2_file_all_info *dst, const FILE_ALL_INFO *src)
2131+
{
2132+
memcpy(dst, src, (size_t)((u8 *)&src->AccessFlags - (u8 *)src));
2133+
dst->AccessFlags = src->AccessFlags;
2134+
dst->CurrentByteOffset = src->CurrentByteOffset;
2135+
dst->Mode = src->Mode;
2136+
dst->AlignmentRequirement = src->AlignmentRequirement;
2137+
dst->FileNameLength = src->FileNameLength;
2138+
}
2139+
21142140
#endif /* _CIFS_GLOB_H */

fs/cifs/cifsproto.h

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -182,10 +182,9 @@ extern int cifs_unlock_range(struct cifsFileInfo *cfile,
182182
extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile);
183183

184184
extern void cifs_down_write(struct rw_semaphore *sem);
185-
extern struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid,
186-
struct file *file,
187-
struct tcon_link *tlink,
188-
__u32 oplock);
185+
struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
186+
struct tcon_link *tlink, __u32 oplock,
187+
const char *symlink_target);
189188
extern int cifs_posix_open(const char *full_path, struct inode **inode,
190189
struct super_block *sb, int mode,
191190
unsigned int f_flags, __u32 *oplock, __u16 *netfid,
@@ -200,9 +199,9 @@ extern int cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr);
200199
extern struct inode *cifs_iget(struct super_block *sb,
201200
struct cifs_fattr *fattr);
202201

203-
extern int cifs_get_inode_info(struct inode **inode, const char *full_path,
204-
FILE_ALL_INFO *data, struct super_block *sb,
205-
int xid, const struct cifs_fid *fid);
202+
int cifs_get_inode_info(struct inode **inode, const char *full_path,
203+
struct cifs_open_info_data *data, struct super_block *sb, int xid,
204+
const struct cifs_fid *fid);
206205
extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path,
207206
struct super_block *sb, unsigned int xid);
208207
extern int cifs_get_inode_info_unix(struct inode **pinode,

fs/cifs/dir.c

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,9 @@ check_name(struct dentry *direntry, struct cifs_tcon *tcon)
165165

166166
/* Inode operations in similar order to how they appear in Linux file fs.h */
167167

168-
static int
169-
cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
170-
struct tcon_link *tlink, unsigned oflags, umode_t mode,
171-
__u32 *oplock, struct cifs_fid *fid)
168+
static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
169+
struct tcon_link *tlink, unsigned int oflags, umode_t mode, __u32 *oplock,
170+
struct cifs_fid *fid, struct cifs_open_info_data *buf)
172171
{
173172
int rc = -ENOENT;
174173
int create_options = CREATE_NOT_DIR;
@@ -177,7 +176,6 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
177176
struct cifs_tcon *tcon = tlink_tcon(tlink);
178177
const char *full_path;
179178
void *page = alloc_dentry_path();
180-
FILE_ALL_INFO *buf = NULL;
181179
struct inode *newinode = NULL;
182180
int disposition;
183181
struct TCP_Server_Info *server = tcon->ses->server;
@@ -290,12 +288,6 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
290288
goto out;
291289
}
292290

293-
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
294-
if (buf == NULL) {
295-
rc = -ENOMEM;
296-
goto out;
297-
}
298-
299291
/*
300292
* if we're not using unix extensions, see if we need to set
301293
* ATTR_READONLY on the create call
@@ -364,8 +356,7 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
364356
{
365357
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
366358
/* TODO: Add support for calling POSIX query info here, but passing in fid */
367-
rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb,
368-
xid, fid);
359+
rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb, xid, fid);
369360
if (newinode) {
370361
if (server->ops->set_lease_key)
371362
server->ops->set_lease_key(newinode, fid);
@@ -402,7 +393,6 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
402393
d_add(direntry, newinode);
403394

404395
out:
405-
kfree(buf);
406396
free_dentry_path(page);
407397
return rc;
408398

@@ -427,6 +417,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
427417
struct cifs_pending_open open;
428418
__u32 oplock;
429419
struct cifsFileInfo *file_info;
420+
struct cifs_open_info_data buf = {};
430421

431422
if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb))))
432423
return -EIO;
@@ -484,8 +475,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
484475
cifs_add_pending_open(&fid, tlink, &open);
485476

486477
rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode,
487-
&oplock, &fid);
488-
478+
&oplock, &fid, &buf);
489479
if (rc) {
490480
cifs_del_pending_open(&open);
491481
goto out;
@@ -510,7 +500,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
510500
file->f_op = &cifs_file_direct_ops;
511501
}
512502

513-
file_info = cifs_new_fileinfo(&fid, file, tlink, oplock);
503+
file_info = cifs_new_fileinfo(&fid, file, tlink, oplock, buf.symlink_target);
514504
if (file_info == NULL) {
515505
if (server->ops->close)
516506
server->ops->close(xid, tcon, &fid);
@@ -526,6 +516,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
526516
cifs_put_tlink(tlink);
527517
out_free_xid:
528518
free_xid(xid);
519+
cifs_free_open_info(&buf);
529520
return rc;
530521
}
531522

@@ -547,6 +538,7 @@ int cifs_create(struct user_namespace *mnt_userns, struct inode *inode,
547538
struct TCP_Server_Info *server;
548539
struct cifs_fid fid;
549540
__u32 oplock;
541+
struct cifs_open_info_data buf = {};
550542

551543
cifs_dbg(FYI, "cifs_create parent inode = 0x%p name is: %pd and dentry = 0x%p\n",
552544
inode, direntry, direntry);
@@ -565,11 +557,11 @@ int cifs_create(struct user_namespace *mnt_userns, struct inode *inode,
565557
if (server->ops->new_lease_key)
566558
server->ops->new_lease_key(&fid);
567559

568-
rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode,
569-
&oplock, &fid);
560+
rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, &oplock, &fid, &buf);
570561
if (!rc && server->ops->close)
571562
server->ops->close(xid, tcon, &fid);
572563

564+
cifs_free_open_info(&buf);
573565
cifs_put_tlink(tlink);
574566
out_free_xid:
575567
free_xid(xid);

fs/cifs/file.c

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -209,16 +209,14 @@ int cifs_posix_open(const char *full_path, struct inode **pinode,
209209
}
210210
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
211211

212-
static int
213-
cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
214-
struct cifs_tcon *tcon, unsigned int f_flags, __u32 *oplock,
215-
struct cifs_fid *fid, unsigned int xid)
212+
static int cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
213+
struct cifs_tcon *tcon, unsigned int f_flags, __u32 *oplock,
214+
struct cifs_fid *fid, unsigned int xid, struct cifs_open_info_data *buf)
216215
{
217216
int rc;
218217
int desired_access;
219218
int disposition;
220219
int create_options = CREATE_NOT_DIR;
221-
FILE_ALL_INFO *buf;
222220
struct TCP_Server_Info *server = tcon->ses->server;
223221
struct cifs_open_parms oparms;
224222

@@ -255,10 +253,6 @@ cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *ci
255253

256254
/* BB pass O_SYNC flag through on file attributes .. BB */
257255

258-
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
259-
if (!buf)
260-
return -ENOMEM;
261-
262256
/* O_SYNC also has bit for O_DSYNC so following check picks up either */
263257
if (f_flags & O_SYNC)
264258
create_options |= CREATE_WRITE_THROUGH;
@@ -276,9 +270,8 @@ cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *ci
276270
oparms.reconnect = false;
277271

278272
rc = server->ops->open(xid, &oparms, oplock, buf);
279-
280273
if (rc)
281-
goto out;
274+
return rc;
282275

283276
/* TODO: Add support for calling posix query info but with passing in fid */
284277
if (tcon->unix_ext)
@@ -294,8 +287,6 @@ cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_sb_info *ci
294287
rc = -EOPENSTALE;
295288
}
296289

297-
out:
298-
kfree(buf);
299290
return rc;
300291
}
301292

@@ -325,9 +316,9 @@ cifs_down_write(struct rw_semaphore *sem)
325316

326317
static void cifsFileInfo_put_work(struct work_struct *work);
327318

328-
struct cifsFileInfo *
329-
cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
330-
struct tcon_link *tlink, __u32 oplock)
319+
struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
320+
struct tcon_link *tlink, __u32 oplock,
321+
const char *symlink_target)
331322
{
332323
struct dentry *dentry = file_dentry(file);
333324
struct inode *inode = d_inode(dentry);
@@ -347,6 +338,15 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
347338
return NULL;
348339
}
349340

341+
if (symlink_target) {
342+
cfile->symlink_target = kstrdup(symlink_target, GFP_KERNEL);
343+
if (!cfile->symlink_target) {
344+
kfree(fdlocks);
345+
kfree(cfile);
346+
return NULL;
347+
}
348+
}
349+
350350
INIT_LIST_HEAD(&fdlocks->locks);
351351
fdlocks->cfile = cfile;
352352
cfile->llist = fdlocks;
@@ -440,6 +440,7 @@ static void cifsFileInfo_put_final(struct cifsFileInfo *cifs_file)
440440
cifs_put_tlink(cifs_file->tlink);
441441
dput(cifs_file->dentry);
442442
cifs_sb_deactive(sb);
443+
kfree(cifs_file->symlink_target);
443444
kfree(cifs_file);
444445
}
445446

@@ -572,6 +573,7 @@ int cifs_open(struct inode *inode, struct file *file)
572573
bool posix_open_ok = false;
573574
struct cifs_fid fid;
574575
struct cifs_pending_open open;
576+
struct cifs_open_info_data data = {};
575577

576578
xid = get_xid();
577579

@@ -662,15 +664,15 @@ int cifs_open(struct inode *inode, struct file *file)
662664
if (server->ops->get_lease_key)
663665
server->ops->get_lease_key(inode, &fid);
664666

665-
rc = cifs_nt_open(full_path, inode, cifs_sb, tcon,
666-
file->f_flags, &oplock, &fid, xid);
667+
rc = cifs_nt_open(full_path, inode, cifs_sb, tcon, file->f_flags, &oplock, &fid,
668+
xid, &data);
667669
if (rc) {
668670
cifs_del_pending_open(&open);
669671
goto out;
670672
}
671673
}
672674

673-
cfile = cifs_new_fileinfo(&fid, file, tlink, oplock);
675+
cfile = cifs_new_fileinfo(&fid, file, tlink, oplock, data.symlink_target);
674676
if (cfile == NULL) {
675677
if (server->ops->close)
676678
server->ops->close(xid, tcon, &fid);
@@ -712,6 +714,7 @@ int cifs_open(struct inode *inode, struct file *file)
712714
free_dentry_path(page);
713715
free_xid(xid);
714716
cifs_put_tlink(tlink);
717+
cifs_free_open_info(&data);
715718
return rc;
716719
}
717720

0 commit comments

Comments
 (0)