diff options
author | Pavel Shilovsky <pshilovsky@etersoft.ru> | 2012-09-19 06:22:43 -0700 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2012-09-24 21:46:33 -0500 |
commit | f7ba7fe685bc3ed8fd0687870e68b2567d17357f (patch) | |
tree | 364ff08cf616cc740467d44d5026a05ce9d0c33c /fs/cifs/smb2file.c | |
parent | 027e8eec31d8141a6231f772e10ccae60c9d5c13 (diff) | |
download | linux-stable-f7ba7fe685bc3ed8fd0687870e68b2567d17357f.tar.gz linux-stable-f7ba7fe685bc3ed8fd0687870e68b2567d17357f.tar.bz2 linux-stable-f7ba7fe685bc3ed8fd0687870e68b2567d17357f.zip |
CIFS: Add brlock support for SMB2
Signed-off-by: Pavel Shilovsky <pshilovsky@etersoft.ru>
Diffstat (limited to 'fs/cifs/smb2file.c')
-rw-r--r-- | fs/cifs/smb2file.c | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c index 5ff25e025215..a25ea02149e7 100644 --- a/fs/cifs/smb2file.c +++ b/fs/cifs/smb2file.c @@ -104,3 +104,100 @@ out: kfree(smb2_path); return rc; } + +int +smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock, + const unsigned int xid) +{ + int rc = 0, stored_rc; + unsigned int max_num, num = 0, max_buf; + struct smb2_lock_element *buf, *cur; + struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); + struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode); + struct cifsLockInfo *li, *tmp; + __u64 length = 1 + flock->fl_end - flock->fl_start; + struct list_head tmp_llist; + + INIT_LIST_HEAD(&tmp_llist); + + /* + * Accessing maxBuf is racy with cifs_reconnect - need to store value + * and check it for zero before using. + */ + max_buf = tcon->ses->server->maxBuf; + if (!max_buf) + return -EINVAL; + + max_num = max_buf / sizeof(struct smb2_lock_element); + buf = kzalloc(max_num * sizeof(struct smb2_lock_element), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + cur = buf; + + mutex_lock(&cinode->lock_mutex); + list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) { + if (flock->fl_start > li->offset || + (flock->fl_start + length) < + (li->offset + li->length)) + continue; + if (current->tgid != li->pid) + continue; + if (cinode->can_cache_brlcks) { + /* + * We can cache brlock requests - simply remove a lock + * from the file's list. + */ + list_del(&li->llist); + cifs_del_lock_waiters(li); + kfree(li); + continue; + } + cur->Length = cpu_to_le64(li->length); + cur->Offset = cpu_to_le64(li->offset); + cur->Flags = cpu_to_le32(SMB2_LOCKFLAG_UNLOCK); + /* + * We need to save a lock here to let us add it again to the + * file's list if the unlock range request fails on the server. + */ + list_move(&li->llist, &tmp_llist); + if (++num == max_num) { + stored_rc = smb2_lockv(xid, tcon, + cfile->fid.persistent_fid, + cfile->fid.volatile_fid, + current->tgid, num, buf); + if (stored_rc) { + /* + * We failed on the unlock range request - add + * all locks from the tmp list to the head of + * the file's list. + */ + cifs_move_llist(&tmp_llist, + &cfile->llist->locks); + rc = stored_rc; + } else + /* + * The unlock range request succeed - free the + * tmp list. + */ + cifs_free_llist(&tmp_llist); + cur = buf; + num = 0; + } else + cur++; + } + if (num) { + stored_rc = smb2_lockv(xid, tcon, cfile->fid.persistent_fid, + cfile->fid.volatile_fid, current->tgid, + num, buf); + if (stored_rc) { + cifs_move_llist(&tmp_llist, &cfile->llist->locks); + rc = stored_rc; + } else + cifs_free_llist(&tmp_llist); + } + mutex_unlock(&cinode->lock_mutex); + + kfree(buf); + return rc; +} |