Skip to content

Commit 7f024fc

Browse files
J. Bruce Fieldschucklever
authored andcommitted
Keep read and write fds with each nlm_file
We shouldn't really be using a read-only file descriptor to take a write lock. Most filesystems will put up with it. But NFS, for example, won't. Signed-off-by: J. Bruce Fields <[email protected]> Signed-off-by: Chuck Lever <[email protected]>
1 parent b661601 commit 7f024fc

File tree

7 files changed

+111
-44
lines changed

7 files changed

+111
-44
lines changed

fs/lockd/svc4proc.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,15 @@ nlm4svc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
4040

4141
/* Obtain file pointer. Not used by FREE_ALL call. */
4242
if (filp != NULL) {
43+
int mode = lock_to_openmode(&lock->fl);
44+
4345
error = nlm_lookup_file(rqstp, &file, lock);
4446
if (error)
4547
goto no_locks;
4648
*filp = file;
4749

4850
/* Set up the missing parts of the file_lock structure */
49-
lock->fl.fl_file = file->f_file;
51+
lock->fl.fl_file = file->f_file[mode];
5052
lock->fl.fl_pid = current->tgid;
5153
lock->fl.fl_lmops = &nlmsvc_lock_operations;
5254
nlmsvc_locks_init_private(&lock->fl, host, (pid_t)lock->svid);

fs/lockd/svclock.c

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,7 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
471471
{
472472
struct nlm_block *block = NULL;
473473
int error;
474+
int mode;
474475
__be32 ret;
475476

476477
dprintk("lockd: nlmsvc_lock(%s/%ld, ty=%d, pi=%d, %Ld-%Ld, bl=%d)\n",
@@ -524,7 +525,8 @@ nlmsvc_lock(struct svc_rqst *rqstp, struct nlm_file *file,
524525

525526
if (!wait)
526527
lock->fl.fl_flags &= ~FL_SLEEP;
527-
error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
528+
mode = lock_to_openmode(&lock->fl);
529+
error = vfs_lock_file(file->f_file[mode], F_SETLK, &lock->fl, NULL);
528530
lock->fl.fl_flags &= ~FL_SLEEP;
529531

530532
dprintk("lockd: vfs_lock_file returned %d\n", error);
@@ -577,6 +579,7 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
577579
struct nlm_lock *conflock, struct nlm_cookie *cookie)
578580
{
579581
int error;
582+
int mode;
580583
__be32 ret;
581584
struct nlm_lockowner *test_owner;
582585

@@ -595,7 +598,8 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
595598
/* If there's a conflicting lock, remember to clean up the test lock */
596599
test_owner = (struct nlm_lockowner *)lock->fl.fl_owner;
597600

598-
error = vfs_test_lock(file->f_file, &lock->fl);
601+
mode = lock_to_openmode(&lock->fl);
602+
error = vfs_test_lock(file->f_file[mode], &lock->fl);
599603
if (error) {
600604
/* We can't currently deal with deferred test requests */
601605
if (error == FILE_LOCK_DEFERRED)
@@ -641,7 +645,7 @@ nlmsvc_testlock(struct svc_rqst *rqstp, struct nlm_file *file,
641645
__be32
642646
nlmsvc_unlock(struct net *net, struct nlm_file *file, struct nlm_lock *lock)
643647
{
644-
int error;
648+
int error = 0;
645649

646650
dprintk("lockd: nlmsvc_unlock(%s/%ld, pi=%d, %Ld-%Ld)\n",
647651
nlmsvc_file_inode(file)->i_sb->s_id,
@@ -654,7 +658,12 @@ nlmsvc_unlock(struct net *net, struct nlm_file *file, struct nlm_lock *lock)
654658
nlmsvc_cancel_blocked(net, file, lock);
655659

656660
lock->fl.fl_type = F_UNLCK;
657-
error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
661+
if (file->f_file[O_RDONLY])
662+
error = vfs_lock_file(file->f_file[O_RDONLY], F_SETLK,
663+
&lock->fl, NULL);
664+
if (file->f_file[O_WRONLY])
665+
error = vfs_lock_file(file->f_file[O_WRONLY], F_SETLK,
666+
&lock->fl, NULL);
658667

659668
return (error < 0)? nlm_lck_denied_nolocks : nlm_granted;
660669
}
@@ -671,6 +680,7 @@ nlmsvc_cancel_blocked(struct net *net, struct nlm_file *file, struct nlm_lock *l
671680
{
672681
struct nlm_block *block;
673682
int status = 0;
683+
int mode;
674684

675685
dprintk("lockd: nlmsvc_cancel(%s/%ld, pi=%d, %Ld-%Ld)\n",
676686
nlmsvc_file_inode(file)->i_sb->s_id,
@@ -686,7 +696,8 @@ nlmsvc_cancel_blocked(struct net *net, struct nlm_file *file, struct nlm_lock *l
686696
block = nlmsvc_lookup_block(file, lock);
687697
mutex_unlock(&file->f_mutex);
688698
if (block != NULL) {
689-
vfs_cancel_lock(block->b_file->f_file,
699+
mode = lock_to_openmode(&lock->fl);
700+
vfs_cancel_lock(block->b_file->f_file[mode],
690701
&block->b_call->a_args.lock.fl);
691702
status = nlmsvc_unlink_block(block);
692703
nlmsvc_release_block(block);
@@ -803,6 +814,7 @@ nlmsvc_grant_blocked(struct nlm_block *block)
803814
{
804815
struct nlm_file *file = block->b_file;
805816
struct nlm_lock *lock = &block->b_call->a_args.lock;
817+
int mode;
806818
int error;
807819
loff_t fl_start, fl_end;
808820

@@ -828,7 +840,8 @@ nlmsvc_grant_blocked(struct nlm_block *block)
828840
lock->fl.fl_flags |= FL_SLEEP;
829841
fl_start = lock->fl.fl_start;
830842
fl_end = lock->fl.fl_end;
831-
error = vfs_lock_file(file->f_file, F_SETLK, &lock->fl, NULL);
843+
mode = lock_to_openmode(&lock->fl);
844+
error = vfs_lock_file(file->f_file[mode], F_SETLK, &lock->fl, NULL);
832845
lock->fl.fl_flags &= ~FL_SLEEP;
833846
lock->fl.fl_start = fl_start;
834847
lock->fl.fl_end = fl_end;

fs/lockd/svcproc.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
5555
struct nlm_host *host = NULL;
5656
struct nlm_file *file = NULL;
5757
struct nlm_lock *lock = &argp->lock;
58+
int mode;
5859
__be32 error = 0;
5960

6061
/* nfsd callbacks must have been installed for this procedure */
@@ -75,7 +76,8 @@ nlmsvc_retrieve_args(struct svc_rqst *rqstp, struct nlm_args *argp,
7576
*filp = file;
7677

7778
/* Set up the missing parts of the file_lock structure */
78-
lock->fl.fl_file = file->f_file;
79+
mode = lock_to_openmode(&lock->fl);
80+
lock->fl.fl_file = file->f_file[mode];
7981
lock->fl.fl_pid = current->tgid;
8082
lock->fl.fl_lmops = &nlmsvc_lock_operations;
8183
nlmsvc_locks_init_private(&lock->fl, host, (pid_t)lock->svid);

fs/lockd/svcsubs.c

Lines changed: 71 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,35 @@ static inline unsigned int file_hash(struct nfs_fh *f)
7171
return tmp & (FILE_NRHASH - 1);
7272
}
7373

74+
int lock_to_openmode(struct file_lock *lock)
75+
{
76+
return (lock->fl_type == F_WRLCK) ? O_WRONLY : O_RDONLY;
77+
}
78+
79+
/*
80+
* Open the file. Note that if we're reexporting, for example,
81+
* this could block the lockd thread for a while.
82+
*
83+
* We have to make sure we have the right credential to open
84+
* the file.
85+
*/
86+
static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
87+
struct nlm_file *file, int mode)
88+
{
89+
struct file **fp = &file->f_file[mode];
90+
__be32 nfserr;
91+
92+
if (*fp)
93+
return 0;
94+
nfserr = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, mode);
95+
if (nfserr)
96+
dprintk("lockd: open failed (error %d)\n", nfserr);
97+
return nfserr;
98+
}
99+
74100
/*
75101
* Lookup file info. If it doesn't exist, create a file info struct
76102
* and open a (VFS) file for the given inode.
77-
*
78-
* FIXME:
79-
* Note that we open the file O_RDONLY even when creating write locks.
80-
* This is not quite right, but for now, we assume the client performs
81-
* the proper R/W checking.
82103
*/
83104
__be32
84105
nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
@@ -87,50 +108,45 @@ nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
87108
struct nlm_file *file;
88109
unsigned int hash;
89110
__be32 nfserr;
111+
int mode;
90112

91113
nlm_debug_print_fh("nlm_lookup_file", &lock->fh);
92114

93115
hash = file_hash(&lock->fh);
116+
mode = lock_to_openmode(&lock->fl);
94117

95118
/* Lock file table */
96119
mutex_lock(&nlm_file_mutex);
97120

98121
hlist_for_each_entry(file, &nlm_files[hash], f_list)
99-
if (!nfs_compare_fh(&file->f_handle, &lock->fh))
122+
if (!nfs_compare_fh(&file->f_handle, &lock->fh)) {
123+
mutex_lock(&file->f_mutex);
124+
nfserr = nlm_do_fopen(rqstp, file, mode);
125+
mutex_unlock(&file->f_mutex);
100126
goto found;
101-
127+
}
102128
nlm_debug_print_fh("creating file for", &lock->fh);
103129

104130
nfserr = nlm_lck_denied_nolocks;
105131
file = kzalloc(sizeof(*file), GFP_KERNEL);
106132
if (!file)
107-
goto out_unlock;
133+
goto out_free;
108134

109135
memcpy(&file->f_handle, &lock->fh, sizeof(struct nfs_fh));
110136
mutex_init(&file->f_mutex);
111137
INIT_HLIST_NODE(&file->f_list);
112138
INIT_LIST_HEAD(&file->f_blocks);
113139

114-
/*
115-
* Open the file. Note that if we're reexporting, for example,
116-
* this could block the lockd thread for a while.
117-
*
118-
* We have to make sure we have the right credential to open
119-
* the file.
120-
*/
121-
nfserr = nlmsvc_ops->fopen(rqstp, &lock->fh, &file->f_file);
122-
if (nfserr) {
123-
dprintk("lockd: open failed (error %d)\n", nfserr);
124-
goto out_free;
125-
}
140+
nfserr = nlm_do_fopen(rqstp, file, mode);
141+
if (nfserr)
142+
goto out_unlock;
126143

127144
hlist_add_head(&file->f_list, &nlm_files[hash]);
128145

129146
found:
130147
dprintk("lockd: found file %p (count %d)\n", file, file->f_count);
131148
*result = file;
132149
file->f_count++;
133-
nfserr = 0;
134150

135151
out_unlock:
136152
mutex_unlock(&nlm_file_mutex);
@@ -150,13 +166,34 @@ nlm_delete_file(struct nlm_file *file)
150166
nlm_debug_print_file("closing file", file);
151167
if (!hlist_unhashed(&file->f_list)) {
152168
hlist_del(&file->f_list);
153-
nlmsvc_ops->fclose(file->f_file);
169+
if (file->f_file[O_RDONLY])
170+
nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
171+
if (file->f_file[O_WRONLY])
172+
nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
154173
kfree(file);
155174
} else {
156175
printk(KERN_WARNING "lockd: attempt to release unknown file!\n");
157176
}
158177
}
159178

179+
static int nlm_unlock_files(struct nlm_file *file)
180+
{
181+
struct file_lock lock;
182+
struct file *f;
183+
184+
lock.fl_type = F_UNLCK;
185+
lock.fl_start = 0;
186+
lock.fl_end = OFFSET_MAX;
187+
for (f = file->f_file[0]; f <= file->f_file[1]; f++) {
188+
if (f && vfs_lock_file(f, F_SETLK, &lock, NULL) < 0) {
189+
pr_warn("lockd: unlock failure in %s:%d\n",
190+
__FILE__, __LINE__);
191+
return 1;
192+
}
193+
}
194+
return 0;
195+
}
196+
160197
/*
161198
* Loop over all locks on the given file and perform the specified
162199
* action.
@@ -184,17 +221,10 @@ nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file,
184221

185222
lockhost = ((struct nlm_lockowner *)fl->fl_owner)->host;
186223
if (match(lockhost, host)) {
187-
struct file_lock lock = *fl;
188224

189225
spin_unlock(&flctx->flc_lock);
190-
lock.fl_type = F_UNLCK;
191-
lock.fl_start = 0;
192-
lock.fl_end = OFFSET_MAX;
193-
if (vfs_lock_file(file->f_file, F_SETLK, &lock, NULL) < 0) {
194-
printk("lockd: unlock failure in %s:%d\n",
195-
__FILE__, __LINE__);
226+
if (nlm_unlock_files(file))
196227
return 1;
197-
}
198228
goto again;
199229
}
200230
}
@@ -248,6 +278,15 @@ nlm_file_inuse(struct nlm_file *file)
248278
return 0;
249279
}
250280

281+
static void nlm_close_files(struct nlm_file *file)
282+
{
283+
struct file *f;
284+
285+
for (f = file->f_file[0]; f <= file->f_file[1]; f++)
286+
if (f)
287+
nlmsvc_ops->fclose(f);
288+
}
289+
251290
/*
252291
* Loop over all files in the file table.
253292
*/
@@ -278,7 +317,7 @@ nlm_traverse_files(void *data, nlm_host_match_fn_t match,
278317
if (list_empty(&file->f_blocks) && !file->f_locks
279318
&& !file->f_shares && !file->f_count) {
280319
hlist_del(&file->f_list);
281-
nlmsvc_ops->fclose(file->f_file);
320+
nlm_close_files(file);
282321
kfree(file);
283322
}
284323
}
@@ -412,6 +451,7 @@ nlmsvc_invalidate_all(void)
412451
nlm_traverse_files(NULL, nlmsvc_is_client, NULL);
413452
}
414453

454+
415455
static int
416456
nlmsvc_match_sb(void *datap, struct nlm_file *file)
417457
{

fs/nfsd/lockd.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@
2525
* Note: we hold the dentry use count while the file is open.
2626
*/
2727
static __be32
28-
nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp)
28+
nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp,
29+
int mode)
2930
{
3031
__be32 nfserr;
32+
int access;
3133
struct svc_fh fh;
3234

3335
/* must initialize before using! but maxsize doesn't matter */
@@ -36,7 +38,9 @@ nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp)
3638
memcpy((char*)&fh.fh_handle.fh_base, f->data, f->size);
3739
fh.fh_export = NULL;
3840

39-
nfserr = nfsd_open(rqstp, &fh, S_IFREG, NFSD_MAY_LOCK, filp);
41+
access = (mode == O_WRONLY) ? NFSD_MAY_WRITE : NFSD_MAY_READ;
42+
access |= NFSD_MAY_LOCK;
43+
nfserr = nfsd_open(rqstp, &fh, S_IFREG, access, filp);
4044
fh_put(&fh);
4145
/* We return nlm error codes as nlm doesn't know
4246
* about nfsd, but nfsd does know about nlm..

include/linux/lockd/bind.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ struct rpc_task;
2727
struct nlmsvc_binding {
2828
__be32 (*fopen)(struct svc_rqst *,
2929
struct nfs_fh *,
30-
struct file **);
30+
struct file **,
31+
int mode);
3132
void (*fclose)(struct file *);
3233
};
3334

include/linux/lockd/lockd.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#ifndef LINUX_LOCKD_LOCKD_H
1111
#define LINUX_LOCKD_LOCKD_H
1212

13+
/* XXX: a lot of this should really be under fs/lockd. */
14+
1315
#include <linux/in.h>
1416
#include <linux/in6.h>
1517
#include <net/ipv6.h>
@@ -154,7 +156,8 @@ struct nlm_rqst {
154156
struct nlm_file {
155157
struct hlist_node f_list; /* linked list */
156158
struct nfs_fh f_handle; /* NFS file handle */
157-
struct file * f_file; /* VFS file pointer */
159+
struct file * f_file[2]; /* VFS file pointers,
160+
indexed by O_ flags */
158161
struct nlm_share * f_shares; /* DOS shares */
159162
struct list_head f_blocks; /* blocked locks */
160163
unsigned int f_locks; /* guesstimate # of locks */
@@ -267,6 +270,7 @@ typedef int (*nlm_host_match_fn_t)(void *cur, struct nlm_host *ref);
267270
/*
268271
* Server-side lock handling
269272
*/
273+
int lock_to_openmode(struct file_lock *);
270274
__be32 nlmsvc_lock(struct svc_rqst *, struct nlm_file *,
271275
struct nlm_host *, struct nlm_lock *, int,
272276
struct nlm_cookie *, int);
@@ -301,7 +305,8 @@ int nlmsvc_unlock_all_by_ip(struct sockaddr *server_addr);
301305

302306
static inline struct inode *nlmsvc_file_inode(struct nlm_file *file)
303307
{
304-
return locks_inode(file->f_file);
308+
return locks_inode(file->f_file[O_RDONLY] ?
309+
file->f_file[O_RDONLY] : file->f_file[O_WRONLY]);
305310
}
306311

307312
static inline int __nlm_privileged_request4(const struct sockaddr *sap)

0 commit comments

Comments
 (0)