Commit faa63e9584df41020440756b8b90b7b63f95e4f6
Committed by
Tim Shimmin
1 parent
1fa503df66
Exists in
master
and in
4 other branches
[XFS] Fix XFS_IOC_FSBULKSTAT{,_SINGLE} & XFS_IOC_FSINUMBERS in compat mode
* 32bit struct xfs_fsop_bulkreq has different size and layout of members, no matter the alignment. Move the code out of the #else branch (why was it there in the first place?). Define _32 variants of the ioctl constants. * 32bit struct xfs_bstat is different because of time_t and on i386 because of different padding. Make xfs_bulkstat_one() accept a custom "output formatter" in the private_data argument which takes care of the xfs_bulkstat_one_compat() that takes care of the different layout in the compat case. * i386 struct xfs_inogrp has different padding. Add a similar "output formatter" mecanism to xfs_inumbers(). SGI-PV: 967354 SGI-Modid: xfs-linux-melb:xfs-kern:29102a Signed-off-by: Michal Marek <mmarek@suse.cz> Signed-off-by: David Chinner <dgc@sgi.com> Signed-off-by: Tim Shimmin <tes@sgi.com>
Showing 4 changed files with 255 additions and 33 deletions Side-by-side Diff
fs/xfs/linux-2.6/xfs_ioctl.c
... | ... | @@ -1019,7 +1019,7 @@ |
1019 | 1019 | |
1020 | 1020 | if (cmd == XFS_IOC_FSINUMBERS) |
1021 | 1021 | error = xfs_inumbers(mp, &inlast, &count, |
1022 | - bulkreq.ubuffer); | |
1022 | + bulkreq.ubuffer, xfs_inumbers_fmt); | |
1023 | 1023 | else if (cmd == XFS_IOC_FSBULKSTAT_SINGLE) |
1024 | 1024 | error = xfs_bulkstat_single(mp, &inlast, |
1025 | 1025 | bulkreq.ubuffer, &done); |
fs/xfs/linux-2.6/xfs_ioctl32.c
... | ... | @@ -23,10 +23,25 @@ |
23 | 23 | #include <linux/fs.h> |
24 | 24 | #include <asm/uaccess.h> |
25 | 25 | #include "xfs.h" |
26 | -#include "xfs_types.h" | |
27 | 26 | #include "xfs_fs.h" |
27 | +#include "xfs_bit.h" | |
28 | +#include "xfs_log.h" | |
29 | +#include "xfs_inum.h" | |
30 | +#include "xfs_trans.h" | |
31 | +#include "xfs_sb.h" | |
32 | +#include "xfs_ag.h" | |
33 | +#include "xfs_dir2.h" | |
34 | +#include "xfs_dmapi.h" | |
35 | +#include "xfs_mount.h" | |
36 | +#include "xfs_bmap_btree.h" | |
37 | +#include "xfs_attr_sf.h" | |
38 | +#include "xfs_dir2_sf.h" | |
28 | 39 | #include "xfs_vfs.h" |
29 | 40 | #include "xfs_vnode.h" |
41 | +#include "xfs_dinode.h" | |
42 | +#include "xfs_inode.h" | |
43 | +#include "xfs_itable.h" | |
44 | +#include "xfs_error.h" | |
30 | 45 | #include "xfs_dfrag.h" |
31 | 46 | |
32 | 47 | #define _NATIVE_IOC(cmd, type) \ |
... | ... | @@ -34,6 +49,7 @@ |
34 | 49 | |
35 | 50 | #if defined(CONFIG_IA64) || defined(CONFIG_X86_64) |
36 | 51 | #define BROKEN_X86_ALIGNMENT |
52 | +#define _PACKED __attribute__((packed)) | |
37 | 53 | /* on ia32 l_start is on a 32-bit boundary */ |
38 | 54 | typedef struct xfs_flock64_32 { |
39 | 55 | __s16 l_type; |
40 | 56 | |
41 | 57 | |
42 | 58 | |
43 | 59 | |
44 | 60 | |
45 | 61 | |
46 | 62 | |
47 | 63 | |
48 | 64 | |
49 | 65 | |
... | ... | @@ -111,36 +127,197 @@ |
111 | 127 | return (unsigned long)p; |
112 | 128 | } |
113 | 129 | |
130 | +typedef struct compat_xfs_inogrp { | |
131 | + __u64 xi_startino; /* starting inode number */ | |
132 | + __s32 xi_alloccount; /* # bits set in allocmask */ | |
133 | + __u64 xi_allocmask; /* mask of allocated inodes */ | |
134 | +} __attribute__((packed)) compat_xfs_inogrp_t; | |
135 | + | |
136 | +STATIC int xfs_inumbers_fmt_compat( | |
137 | + void __user *ubuffer, | |
138 | + const xfs_inogrp_t *buffer, | |
139 | + long count, | |
140 | + long *written) | |
141 | +{ | |
142 | + compat_xfs_inogrp_t *p32 = ubuffer; | |
143 | + long i; | |
144 | + | |
145 | + for (i = 0; i < count; i++) { | |
146 | + if (put_user(buffer[i].xi_startino, &p32[i].xi_startino) || | |
147 | + put_user(buffer[i].xi_alloccount, &p32[i].xi_alloccount) || | |
148 | + put_user(buffer[i].xi_allocmask, &p32[i].xi_allocmask)) | |
149 | + return -EFAULT; | |
150 | + } | |
151 | + *written = count * sizeof(*p32); | |
152 | + return 0; | |
153 | +} | |
154 | + | |
114 | 155 | #else |
115 | 156 | |
116 | -typedef struct xfs_fsop_bulkreq32 { | |
157 | +#define xfs_inumbers_fmt_compat xfs_inumbers_fmt | |
158 | +#define _PACKED | |
159 | + | |
160 | +#endif | |
161 | + | |
162 | +/* XFS_IOC_FSBULKSTAT and friends */ | |
163 | + | |
164 | +typedef struct compat_xfs_bstime { | |
165 | + __s32 tv_sec; /* seconds */ | |
166 | + __s32 tv_nsec; /* and nanoseconds */ | |
167 | +} compat_xfs_bstime_t; | |
168 | + | |
169 | +STATIC int xfs_bstime_store_compat( | |
170 | + compat_xfs_bstime_t __user *p32, | |
171 | + const xfs_bstime_t *p) | |
172 | +{ | |
173 | + __s32 sec32; | |
174 | + | |
175 | + sec32 = p->tv_sec; | |
176 | + if (put_user(sec32, &p32->tv_sec) || | |
177 | + put_user(p->tv_nsec, &p32->tv_nsec)) | |
178 | + return -EFAULT; | |
179 | + return 0; | |
180 | +} | |
181 | + | |
182 | +typedef struct compat_xfs_bstat { | |
183 | + __u64 bs_ino; /* inode number */ | |
184 | + __u16 bs_mode; /* type and mode */ | |
185 | + __u16 bs_nlink; /* number of links */ | |
186 | + __u32 bs_uid; /* user id */ | |
187 | + __u32 bs_gid; /* group id */ | |
188 | + __u32 bs_rdev; /* device value */ | |
189 | + __s32 bs_blksize; /* block size */ | |
190 | + __s64 bs_size; /* file size */ | |
191 | + compat_xfs_bstime_t bs_atime; /* access time */ | |
192 | + compat_xfs_bstime_t bs_mtime; /* modify time */ | |
193 | + compat_xfs_bstime_t bs_ctime; /* inode change time */ | |
194 | + int64_t bs_blocks; /* number of blocks */ | |
195 | + __u32 bs_xflags; /* extended flags */ | |
196 | + __s32 bs_extsize; /* extent size */ | |
197 | + __s32 bs_extents; /* number of extents */ | |
198 | + __u32 bs_gen; /* generation count */ | |
199 | + __u16 bs_projid; /* project id */ | |
200 | + unsigned char bs_pad[14]; /* pad space, unused */ | |
201 | + __u32 bs_dmevmask; /* DMIG event mask */ | |
202 | + __u16 bs_dmstate; /* DMIG state info */ | |
203 | + __u16 bs_aextents; /* attribute number of extents */ | |
204 | +} _PACKED compat_xfs_bstat_t; | |
205 | + | |
206 | +STATIC int xfs_bulkstat_one_fmt_compat( | |
207 | + void __user *ubuffer, | |
208 | + const xfs_bstat_t *buffer) | |
209 | +{ | |
210 | + compat_xfs_bstat_t __user *p32 = ubuffer; | |
211 | + | |
212 | + if (put_user(buffer->bs_ino, &p32->bs_ino) || | |
213 | + put_user(buffer->bs_mode, &p32->bs_mode) || | |
214 | + put_user(buffer->bs_nlink, &p32->bs_nlink) || | |
215 | + put_user(buffer->bs_uid, &p32->bs_uid) || | |
216 | + put_user(buffer->bs_gid, &p32->bs_gid) || | |
217 | + put_user(buffer->bs_rdev, &p32->bs_rdev) || | |
218 | + put_user(buffer->bs_blksize, &p32->bs_blksize) || | |
219 | + put_user(buffer->bs_size, &p32->bs_size) || | |
220 | + xfs_bstime_store_compat(&p32->bs_atime, &buffer->bs_atime) || | |
221 | + xfs_bstime_store_compat(&p32->bs_mtime, &buffer->bs_mtime) || | |
222 | + xfs_bstime_store_compat(&p32->bs_ctime, &buffer->bs_ctime) || | |
223 | + put_user(buffer->bs_blocks, &p32->bs_blocks) || | |
224 | + put_user(buffer->bs_xflags, &p32->bs_xflags) || | |
225 | + put_user(buffer->bs_extsize, &p32->bs_extsize) || | |
226 | + put_user(buffer->bs_extents, &p32->bs_extents) || | |
227 | + put_user(buffer->bs_gen, &p32->bs_gen) || | |
228 | + put_user(buffer->bs_projid, &p32->bs_projid) || | |
229 | + put_user(buffer->bs_dmevmask, &p32->bs_dmevmask) || | |
230 | + put_user(buffer->bs_dmstate, &p32->bs_dmstate) || | |
231 | + put_user(buffer->bs_aextents, &p32->bs_aextents)) | |
232 | + return -EFAULT; | |
233 | + return sizeof(*p32); | |
234 | +} | |
235 | + | |
236 | + | |
237 | + | |
238 | +typedef struct compat_xfs_fsop_bulkreq { | |
117 | 239 | compat_uptr_t lastip; /* last inode # pointer */ |
118 | 240 | __s32 icount; /* count of entries in buffer */ |
119 | 241 | compat_uptr_t ubuffer; /* user buffer for inode desc. */ |
120 | - __s32 ocount; /* output count pointer */ | |
121 | -} xfs_fsop_bulkreq32_t; | |
242 | + compat_uptr_t ocount; /* output count pointer */ | |
243 | +} compat_xfs_fsop_bulkreq_t; | |
122 | 244 | |
123 | -STATIC unsigned long | |
124 | -xfs_ioctl32_bulkstat( | |
125 | - unsigned long arg) | |
245 | +#define XFS_IOC_FSBULKSTAT_32 \ | |
246 | + _IOWR('X', 101, struct compat_xfs_fsop_bulkreq) | |
247 | +#define XFS_IOC_FSBULKSTAT_SINGLE_32 \ | |
248 | + _IOWR('X', 102, struct compat_xfs_fsop_bulkreq) | |
249 | +#define XFS_IOC_FSINUMBERS_32 \ | |
250 | + _IOWR('X', 103, struct compat_xfs_fsop_bulkreq) | |
251 | + | |
252 | +/* copied from xfs_ioctl.c */ | |
253 | +STATIC int | |
254 | +xfs_ioc_bulkstat_compat( | |
255 | + xfs_mount_t *mp, | |
256 | + unsigned int cmd, | |
257 | + void __user *arg) | |
126 | 258 | { |
127 | - xfs_fsop_bulkreq32_t __user *p32 = (void __user *)arg; | |
128 | - xfs_fsop_bulkreq_t __user *p = compat_alloc_user_space(sizeof(*p)); | |
259 | + compat_xfs_fsop_bulkreq_t __user *p32 = (void __user *)arg; | |
129 | 260 | u32 addr; |
261 | + xfs_fsop_bulkreq_t bulkreq; | |
262 | + int count; /* # of records returned */ | |
263 | + xfs_ino_t inlast; /* last inode number */ | |
264 | + int done; | |
265 | + int error; | |
130 | 266 | |
131 | - if (get_user(addr, &p32->lastip) || | |
132 | - put_user(compat_ptr(addr), &p->lastip) || | |
133 | - copy_in_user(&p->icount, &p32->icount, sizeof(s32)) || | |
134 | - get_user(addr, &p32->ubuffer) || | |
135 | - put_user(compat_ptr(addr), &p->ubuffer) || | |
136 | - get_user(addr, &p32->ocount) || | |
137 | - put_user(compat_ptr(addr), &p->ocount)) | |
267 | + /* done = 1 if there are more stats to get and if bulkstat */ | |
268 | + /* should be called again (unused here, but used in dmapi) */ | |
269 | + | |
270 | + if (!capable(CAP_SYS_ADMIN)) | |
271 | + return -EPERM; | |
272 | + | |
273 | + if (XFS_FORCED_SHUTDOWN(mp)) | |
274 | + return -XFS_ERROR(EIO); | |
275 | + | |
276 | + if (get_user(addr, &p32->lastip)) | |
138 | 277 | return -EFAULT; |
278 | + bulkreq.lastip = compat_ptr(addr); | |
279 | + if (get_user(bulkreq.icount, &p32->icount) || | |
280 | + get_user(addr, &p32->ubuffer)) | |
281 | + return -EFAULT; | |
282 | + bulkreq.ubuffer = compat_ptr(addr); | |
283 | + if (get_user(addr, &p32->ocount)) | |
284 | + return -EFAULT; | |
285 | + bulkreq.ocount = compat_ptr(addr); | |
139 | 286 | |
140 | - return (unsigned long)p; | |
287 | + if (copy_from_user(&inlast, bulkreq.lastip, sizeof(__s64))) | |
288 | + return -XFS_ERROR(EFAULT); | |
289 | + | |
290 | + if ((count = bulkreq.icount) <= 0) | |
291 | + return -XFS_ERROR(EINVAL); | |
292 | + | |
293 | + if (cmd == XFS_IOC_FSINUMBERS) | |
294 | + error = xfs_inumbers(mp, &inlast, &count, | |
295 | + bulkreq.ubuffer, xfs_inumbers_fmt_compat); | |
296 | + else { | |
297 | + /* declare a var to get a warning in case the type changes */ | |
298 | + bulkstat_one_fmt_pf formatter = xfs_bulkstat_one_fmt_compat; | |
299 | + error = xfs_bulkstat(mp, &inlast, &count, | |
300 | + xfs_bulkstat_one, formatter, | |
301 | + sizeof(compat_xfs_bstat_t), bulkreq.ubuffer, | |
302 | + BULKSTAT_FG_QUICK, &done); | |
303 | + } | |
304 | + if (error) | |
305 | + return -error; | |
306 | + | |
307 | + if (bulkreq.ocount != NULL) { | |
308 | + if (copy_to_user(bulkreq.lastip, &inlast, | |
309 | + sizeof(xfs_ino_t))) | |
310 | + return -XFS_ERROR(EFAULT); | |
311 | + | |
312 | + if (copy_to_user(bulkreq.ocount, &count, sizeof(count))) | |
313 | + return -XFS_ERROR(EFAULT); | |
314 | + } | |
315 | + | |
316 | + return 0; | |
141 | 317 | } |
142 | -#endif | |
143 | 318 | |
319 | + | |
320 | + | |
144 | 321 | typedef struct compat_xfs_fsop_handlereq { |
145 | 322 | __u32 fd; /* fd for FD_TO_HANDLE */ |
146 | 323 | compat_uptr_t path; /* user pathname */ |
147 | 324 | |
... | ... | @@ -261,12 +438,13 @@ |
261 | 438 | case XFS_IOC_SWAPEXT: |
262 | 439 | break; |
263 | 440 | |
264 | - case XFS_IOC_FSBULKSTAT_SINGLE: | |
265 | - case XFS_IOC_FSBULKSTAT: | |
266 | - case XFS_IOC_FSINUMBERS: | |
267 | - arg = xfs_ioctl32_bulkstat(arg); | |
268 | - break; | |
269 | 441 | #endif |
442 | + case XFS_IOC_FSBULKSTAT_32: | |
443 | + case XFS_IOC_FSBULKSTAT_SINGLE_32: | |
444 | + case XFS_IOC_FSINUMBERS_32: | |
445 | + cmd = _NATIVE_IOC(cmd, struct xfs_fsop_bulkreq); | |
446 | + return xfs_ioc_bulkstat_compat(XFS_BHVTOI(VNHEAD(vp))->i_mount, | |
447 | + cmd, (void*)arg); | |
270 | 448 | case XFS_IOC_FD_TO_HANDLE_32: |
271 | 449 | case XFS_IOC_PATH_TO_HANDLE_32: |
272 | 450 | case XFS_IOC_PATH_TO_FSHANDLE_32: |
fs/xfs/xfs_itable.c
... | ... | @@ -202,6 +202,16 @@ |
202 | 202 | return 0; |
203 | 203 | } |
204 | 204 | |
205 | +STATIC int | |
206 | +xfs_bulkstat_one_fmt( | |
207 | + void __user *ubuffer, | |
208 | + const xfs_bstat_t *buffer) | |
209 | +{ | |
210 | + if (copy_to_user(ubuffer, buffer, sizeof(*buffer))) | |
211 | + return -EFAULT; | |
212 | + return sizeof(*buffer); | |
213 | +} | |
214 | + | |
205 | 215 | /* |
206 | 216 | * Return stat information for one inode. |
207 | 217 | * Return 0 if ok, else errno. |
... | ... | @@ -221,6 +231,7 @@ |
221 | 231 | xfs_bstat_t *buf; /* return buffer */ |
222 | 232 | int error = 0; /* error value */ |
223 | 233 | xfs_dinode_t *dip; /* dinode inode pointer */ |
234 | + bulkstat_one_fmt_pf formatter = private_data ? : xfs_bulkstat_one_fmt; | |
224 | 235 | |
225 | 236 | dip = (xfs_dinode_t *)dibuff; |
226 | 237 | *stat = BULKSTAT_RV_NOTHING; |
227 | 238 | |
... | ... | @@ -243,14 +254,15 @@ |
243 | 254 | xfs_bulkstat_one_dinode(mp, ino, dip, buf); |
244 | 255 | } |
245 | 256 | |
246 | - if (copy_to_user(buffer, buf, sizeof(*buf))) { | |
257 | + error = formatter(buffer, buf); | |
258 | + if (error < 0) { | |
247 | 259 | error = EFAULT; |
248 | 260 | goto out_free; |
249 | 261 | } |
250 | 262 | |
251 | 263 | *stat = BULKSTAT_RV_DIDONE; |
252 | 264 | if (ubused) |
253 | - *ubused = sizeof(*buf); | |
265 | + *ubused = error; | |
254 | 266 | |
255 | 267 | out_free: |
256 | 268 | kmem_free(buf, sizeof(*buf)); |
... | ... | @@ -748,6 +760,19 @@ |
748 | 760 | return 0; |
749 | 761 | } |
750 | 762 | |
763 | +int | |
764 | +xfs_inumbers_fmt( | |
765 | + void __user *ubuffer, /* buffer to write to */ | |
766 | + const xfs_inogrp_t *buffer, /* buffer to read from */ | |
767 | + long count, /* # of elements to read */ | |
768 | + long *written) /* # of bytes written */ | |
769 | +{ | |
770 | + if (copy_to_user(ubuffer, buffer, count * sizeof(*buffer))) | |
771 | + return -EFAULT; | |
772 | + *written = count * sizeof(*buffer); | |
773 | + return 0; | |
774 | +} | |
775 | + | |
751 | 776 | /* |
752 | 777 | * Return inode number table for the filesystem. |
753 | 778 | */ |
... | ... | @@ -756,7 +781,8 @@ |
756 | 781 | xfs_mount_t *mp, /* mount point for filesystem */ |
757 | 782 | xfs_ino_t *lastino, /* last inode returned */ |
758 | 783 | int *count, /* size of buffer/count returned */ |
759 | - xfs_inogrp_t __user *ubuffer)/* buffer with inode descriptions */ | |
784 | + void __user *ubuffer,/* buffer with inode descriptions */ | |
785 | + inumbers_fmt_pf formatter) | |
760 | 786 | { |
761 | 787 | xfs_buf_t *agbp; |
762 | 788 | xfs_agino_t agino; |
763 | 789 | |
... | ... | @@ -835,12 +861,12 @@ |
835 | 861 | bufidx++; |
836 | 862 | left--; |
837 | 863 | if (bufidx == bcount) { |
838 | - if (copy_to_user(ubuffer, buffer, | |
839 | - bufidx * sizeof(*buffer))) { | |
864 | + long written; | |
865 | + if (formatter(ubuffer, buffer, bufidx, &written)) { | |
840 | 866 | error = XFS_ERROR(EFAULT); |
841 | 867 | break; |
842 | 868 | } |
843 | - ubuffer += bufidx; | |
869 | + ubuffer += written; | |
844 | 870 | *count += bufidx; |
845 | 871 | bufidx = 0; |
846 | 872 | } |
... | ... | @@ -862,8 +888,8 @@ |
862 | 888 | } |
863 | 889 | if (!error) { |
864 | 890 | if (bufidx) { |
865 | - if (copy_to_user(ubuffer, buffer, | |
866 | - bufidx * sizeof(*buffer))) | |
891 | + long written; | |
892 | + if (formatter(ubuffer, buffer, bufidx, &written)) | |
867 | 893 | error = XFS_ERROR(EFAULT); |
868 | 894 | else |
869 | 895 | *count += bufidx; |
fs/xfs/xfs_itable.h
... | ... | @@ -69,6 +69,10 @@ |
69 | 69 | char __user *buffer, |
70 | 70 | int *done); |
71 | 71 | |
72 | +typedef int (*bulkstat_one_fmt_pf)( /* used size in bytes or negative error */ | |
73 | + void __user *ubuffer, /* buffer to write to */ | |
74 | + const xfs_bstat_t *buffer); /* buffer to read from */ | |
75 | + | |
72 | 76 | int |
73 | 77 | xfs_bulkstat_one( |
74 | 78 | xfs_mount_t *mp, |
75 | 79 | |
... | ... | @@ -86,12 +90,26 @@ |
86 | 90 | xfs_mount_t *mp, |
87 | 91 | xfs_ino_t ino); |
88 | 92 | |
93 | +typedef int (*inumbers_fmt_pf)( | |
94 | + void __user *ubuffer, /* buffer to write to */ | |
95 | + const xfs_inogrp_t *buffer, /* buffer to read from */ | |
96 | + long count, /* # of elements to read */ | |
97 | + long *written); /* # of bytes written */ | |
98 | + | |
99 | +int | |
100 | +xfs_inumbers_fmt( | |
101 | + void __user *ubuffer, /* buffer to write to */ | |
102 | + const xfs_inogrp_t *buffer, /* buffer to read from */ | |
103 | + long count, /* # of elements to read */ | |
104 | + long *written); /* # of bytes written */ | |
105 | + | |
89 | 106 | int /* error status */ |
90 | 107 | xfs_inumbers( |
91 | 108 | xfs_mount_t *mp, /* mount point for filesystem */ |
92 | 109 | xfs_ino_t *last, /* last inode returned */ |
93 | 110 | int *count, /* size of buffer/count returned */ |
94 | - xfs_inogrp_t __user *buffer);/* buffer with inode info */ | |
111 | + void __user *buffer, /* buffer with inode info */ | |
112 | + inumbers_fmt_pf formatter); | |
95 | 113 | |
96 | 114 | #endif /* __XFS_ITABLE_H__ */ |