]>
Commit | Line | Data |
---|---|---|
2cb7cef9 BS |
1 | From: Trond Myklebust <Trond.Myklebust@netapp.com> |
2 | Subject: NFSv3: Fix posix ACL code | |
3 | References: 465854 | |
4 | Patch-mainline: 2.6.29-rc7 | |
5 | ||
6 | Upstream commit ae46141ff08f1965b17c531b571953c39ce8b9e2. | |
7 | ||
8 | Fix a memory leak due to allocation in the XDR layer. In cases where the | |
9 | RPC call needs to be retransmitted, we end up allocating new pages without | |
10 | clearing the old ones. Fix this by moving the allocation into | |
11 | nfs3_proc_setacls(). | |
12 | ||
13 | Also fix an issue discovered by Kevin Rudd, whereby the amount of memory | |
14 | reserved for the acls in the xdr_buf->head was miscalculated, and causing | |
15 | corruption. | |
16 | ||
17 | Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com> | |
18 | Signed-off-by: Suresh Jayaraman <sjayaraman@suse.de> | |
19 | --- | |
20 | ||
21 | fs/nfs/nfs3acl.c | 27 +++++++++++++++++++++------ | |
22 | fs/nfs/nfs3xdr.c | 34 +++++++++++++--------------------- | |
23 | include/linux/nfs_xdr.h | 2 ++ | |
24 | include/linux/nfsacl.h | 3 +++ | |
25 | 4 files changed, 39 insertions(+), 27 deletions(-) | |
26 | ||
27 | Index: linux-2.6.27-SLE11_BRANCH/fs/nfs/nfs3acl.c | |
28 | =================================================================== | |
29 | --- linux-2.6.27-SLE11_BRANCH.orig/fs/nfs/nfs3acl.c | |
30 | +++ linux-2.6.27-SLE11_BRANCH/fs/nfs/nfs3acl.c | |
31 | @@ -291,7 +291,7 @@ static int nfs3_proc_setacls(struct inod | |
32 | { | |
33 | struct nfs_server *server = NFS_SERVER(inode); | |
34 | struct nfs_fattr fattr; | |
35 | - struct page *pages[NFSACL_MAXPAGES] = { }; | |
36 | + struct page *pages[NFSACL_MAXPAGES]; | |
37 | struct nfs3_setaclargs args = { | |
38 | .inode = inode, | |
39 | .mask = NFS_ACL, | |
40 | @@ -302,7 +302,7 @@ static int nfs3_proc_setacls(struct inod | |
41 | .rpc_argp = &args, | |
42 | .rpc_resp = &fattr, | |
43 | }; | |
44 | - int status, count; | |
45 | + int status; | |
46 | ||
47 | status = -EOPNOTSUPP; | |
48 | if (!nfs_server_capable(inode, NFS_CAP_ACLS)) | |
49 | @@ -318,6 +318,20 @@ static int nfs3_proc_setacls(struct inod | |
50 | if (S_ISDIR(inode->i_mode)) { | |
51 | args.mask |= NFS_DFACL; | |
52 | args.acl_default = dfacl; | |
53 | + args.len = nfsacl_size(acl, dfacl); | |
54 | + } else | |
55 | + args.len = nfsacl_size(acl, NULL); | |
56 | + | |
57 | + if (args.len > NFS_ACL_INLINE_BUFSIZE) { | |
58 | + unsigned int npages = 1 + ((args.len - 1) >> PAGE_SHIFT); | |
59 | + | |
60 | + status = -ENOMEM; | |
61 | + do { | |
62 | + args.pages[args.npages] = alloc_page(GFP_KERNEL); | |
63 | + if (args.pages[args.npages] == NULL) | |
64 | + goto out_freepages; | |
65 | + args.npages++; | |
66 | + } while (args.npages < npages); | |
67 | } | |
68 | ||
69 | dprintk("NFS call setacl\n"); | |
70 | @@ -327,10 +341,6 @@ static int nfs3_proc_setacls(struct inod | |
71 | nfs_zap_acl_cache(inode); | |
72 | dprintk("NFS reply setacl: %d\n", status); | |
73 | ||
74 | - /* pages may have been allocated at the xdr layer. */ | |
75 | - for (count = 0; count < NFSACL_MAXPAGES && args.pages[count]; count++) | |
76 | - __free_page(args.pages[count]); | |
77 | - | |
78 | switch (status) { | |
79 | case 0: | |
80 | status = nfs_refresh_inode(inode, &fattr); | |
81 | @@ -344,6 +354,11 @@ static int nfs3_proc_setacls(struct inod | |
82 | case -ENOTSUPP: | |
83 | status = -EOPNOTSUPP; | |
84 | } | |
85 | +out_freepages: | |
86 | + while (args.npages != 0) { | |
87 | + args.npages--; | |
88 | + __free_page(args.pages[args.npages]); | |
89 | + } | |
90 | out: | |
91 | return status; | |
92 | } | |
93 | Index: linux-2.6.27-SLE11_BRANCH/fs/nfs/nfs3xdr.c | |
94 | =================================================================== | |
95 | --- linux-2.6.27-SLE11_BRANCH.orig/fs/nfs/nfs3xdr.c | |
96 | +++ linux-2.6.27-SLE11_BRANCH/fs/nfs/nfs3xdr.c | |
97 | @@ -82,8 +82,10 @@ | |
98 | #define NFS3_commitres_sz (1+NFS3_wcc_data_sz+2) | |
99 | ||
100 | #define ACL3_getaclargs_sz (NFS3_fh_sz+1) | |
101 | -#define ACL3_setaclargs_sz (NFS3_fh_sz+1+2*(2+5*3)) | |
102 | -#define ACL3_getaclres_sz (1+NFS3_post_op_attr_sz+1+2*(2+5*3)) | |
103 | +#define ACL3_setaclargs_sz (NFS3_fh_sz+1+ \ | |
104 | + XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE)) | |
105 | +#define ACL3_getaclres_sz (1+NFS3_post_op_attr_sz+1+ \ | |
106 | + XDR_QUADLEN(NFS_ACL_INLINE_BUFSIZE)) | |
107 | #define ACL3_setaclres_sz (1+NFS3_post_op_attr_sz) | |
108 | ||
109 | /* | |
110 | @@ -703,28 +705,18 @@ nfs3_xdr_setaclargs(struct rpc_rqst *req | |
111 | struct nfs3_setaclargs *args) | |
112 | { | |
113 | struct xdr_buf *buf = &req->rq_snd_buf; | |
114 | - unsigned int base, len_in_head, len = nfsacl_size( | |
115 | - (args->mask & NFS_ACL) ? args->acl_access : NULL, | |
116 | - (args->mask & NFS_DFACL) ? args->acl_default : NULL); | |
117 | - int count, err; | |
118 | + unsigned int base; | |
119 | + int err; | |
120 | ||
121 | p = xdr_encode_fhandle(p, NFS_FH(args->inode)); | |
122 | *p++ = htonl(args->mask); | |
123 | - base = (char *)p - (char *)buf->head->iov_base; | |
124 | - /* put as much of the acls into head as possible. */ | |
125 | - len_in_head = min_t(unsigned int, buf->head->iov_len - base, len); | |
126 | - len -= len_in_head; | |
127 | - req->rq_slen = xdr_adjust_iovec(req->rq_svec, p + (len_in_head >> 2)); | |
128 | - | |
129 | - for (count = 0; (count << PAGE_SHIFT) < len; count++) { | |
130 | - args->pages[count] = alloc_page(GFP_KERNEL); | |
131 | - if (!args->pages[count]) { | |
132 | - while (count) | |
133 | - __free_page(args->pages[--count]); | |
134 | - return -ENOMEM; | |
135 | - } | |
136 | - } | |
137 | - xdr_encode_pages(buf, args->pages, 0, len); | |
138 | + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); | |
139 | + base = req->rq_slen; | |
140 | + | |
141 | + if (args->npages != 0) | |
142 | + xdr_encode_pages(buf, args->pages, 0, args->len); | |
143 | + else | |
144 | + req->rq_slen += args->len; | |
145 | ||
146 | err = nfsacl_encode(buf, base, args->inode, | |
147 | (args->mask & NFS_ACL) ? | |
148 | Index: linux-2.6.27-SLE11_BRANCH/include/linux/nfs_xdr.h | |
149 | =================================================================== | |
150 | --- linux-2.6.27-SLE11_BRANCH.orig/include/linux/nfs_xdr.h | |
151 | +++ linux-2.6.27-SLE11_BRANCH/include/linux/nfs_xdr.h | |
152 | @@ -404,6 +404,8 @@ struct nfs3_setaclargs { | |
153 | int mask; | |
154 | struct posix_acl * acl_access; | |
155 | struct posix_acl * acl_default; | |
156 | + size_t len; | |
157 | + unsigned int npages; | |
158 | struct page ** pages; | |
159 | }; | |
160 | ||
161 | Index: linux-2.6.27-SLE11_BRANCH/include/linux/nfsacl.h | |
162 | =================================================================== | |
163 | --- linux-2.6.27-SLE11_BRANCH.orig/include/linux/nfsacl.h | |
164 | +++ linux-2.6.27-SLE11_BRANCH/include/linux/nfsacl.h | |
165 | @@ -37,6 +37,9 @@ | |
166 | #define NFSACL_MAXPAGES ((2*(8+12*NFS_ACL_MAX_ENTRIES) + PAGE_SIZE-1) \ | |
167 | >> PAGE_SHIFT) | |
168 | ||
169 | +#define NFS_ACL_MAX_ENTRIES_INLINE (5) | |
170 | +#define NFS_ACL_INLINE_BUFSIZE ((2*(2+3*NFS_ACL_MAX_ENTRIES_INLINE)) << 2) | |
171 | + | |
172 | static inline unsigned int | |
173 | nfsacl_size(struct posix_acl *acl_access, struct posix_acl *acl_default) | |
174 | { |