diff options
Diffstat (limited to 'mm')
-rw-r--r-- | mm/filemap.c | 82 |
1 files changed, 45 insertions, 37 deletions
diff --git a/mm/filemap.c b/mm/filemap.c index 5631d6b2a62d..3e49fe13d6ac 100644 --- a/mm/filemap.c +++ b/mm/filemap.c | |||
@@ -1110,6 +1110,45 @@ success: | |||
1110 | return size; | 1110 | return size; |
1111 | } | 1111 | } |
1112 | 1112 | ||
1113 | /* | ||
1114 | * Performs necessary checks before doing a write | ||
1115 | * @iov: io vector request | ||
1116 | * @nr_segs: number of segments in the iovec | ||
1117 | * @count: number of bytes to write | ||
1118 | * @access_flags: type of access: %VERIFY_READ or %VERIFY_WRITE | ||
1119 | * | ||
1120 | * Adjust number of segments and amount of bytes to write (nr_segs should be | ||
1121 | * properly initialized first). Returns appropriate error code that caller | ||
1122 | * should return or zero in case that write should be allowed. | ||
1123 | */ | ||
1124 | int generic_segment_checks(const struct iovec *iov, | ||
1125 | unsigned long *nr_segs, size_t *count, int access_flags) | ||
1126 | { | ||
1127 | unsigned long seg; | ||
1128 | size_t cnt = 0; | ||
1129 | for (seg = 0; seg < *nr_segs; seg++) { | ||
1130 | const struct iovec *iv = &iov[seg]; | ||
1131 | |||
1132 | /* | ||
1133 | * If any segment has a negative length, or the cumulative | ||
1134 | * length ever wraps negative then return -EINVAL. | ||
1135 | */ | ||
1136 | cnt += iv->iov_len; | ||
1137 | if (unlikely((ssize_t)(cnt|iv->iov_len) < 0)) | ||
1138 | return -EINVAL; | ||
1139 | if (access_ok(access_flags, iv->iov_base, iv->iov_len)) | ||
1140 | continue; | ||
1141 | if (seg == 0) | ||
1142 | return -EFAULT; | ||
1143 | *nr_segs = seg; | ||
1144 | cnt -= iv->iov_len; /* This segment is no good */ | ||
1145 | break; | ||
1146 | } | ||
1147 | *count = cnt; | ||
1148 | return 0; | ||
1149 | } | ||
1150 | EXPORT_SYMBOL(generic_segment_checks); | ||
1151 | |||
1113 | /** | 1152 | /** |
1114 | * generic_file_aio_read - generic filesystem read routine | 1153 | * generic_file_aio_read - generic filesystem read routine |
1115 | * @iocb: kernel I/O control block | 1154 | * @iocb: kernel I/O control block |
@@ -1131,24 +1170,9 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, | |||
1131 | loff_t *ppos = &iocb->ki_pos; | 1170 | loff_t *ppos = &iocb->ki_pos; |
1132 | 1171 | ||
1133 | count = 0; | 1172 | count = 0; |
1134 | for (seg = 0; seg < nr_segs; seg++) { | 1173 | retval = generic_segment_checks(iov, &nr_segs, &count, VERIFY_WRITE); |
1135 | const struct iovec *iv = &iov[seg]; | 1174 | if (retval) |
1136 | 1175 | return retval; | |
1137 | /* | ||
1138 | * If any segment has a negative length, or the cumulative | ||
1139 | * length ever wraps negative then return -EINVAL. | ||
1140 | */ | ||
1141 | count += iv->iov_len; | ||
1142 | if (unlikely((ssize_t)(count|iv->iov_len) < 0)) | ||
1143 | return -EINVAL; | ||
1144 | if (access_ok(VERIFY_WRITE, iv->iov_base, iv->iov_len)) | ||
1145 | continue; | ||
1146 | if (seg == 0) | ||
1147 | return -EFAULT; | ||
1148 | nr_segs = seg; | ||
1149 | count -= iv->iov_len; /* This segment is no good */ | ||
1150 | break; | ||
1151 | } | ||
1152 | 1176 | ||
1153 | /* coalesce the iovecs and go direct-to-BIO for O_DIRECT */ | 1177 | /* coalesce the iovecs and go direct-to-BIO for O_DIRECT */ |
1154 | if (filp->f_flags & O_DIRECT) { | 1178 | if (filp->f_flags & O_DIRECT) { |
@@ -2218,30 +2242,14 @@ __generic_file_aio_write_nolock(struct kiocb *iocb, const struct iovec *iov, | |||
2218 | size_t ocount; /* original count */ | 2242 | size_t ocount; /* original count */ |
2219 | size_t count; /* after file limit checks */ | 2243 | size_t count; /* after file limit checks */ |
2220 | struct inode *inode = mapping->host; | 2244 | struct inode *inode = mapping->host; |
2221 | unsigned long seg; | ||
2222 | loff_t pos; | 2245 | loff_t pos; |
2223 | ssize_t written; | 2246 | ssize_t written; |
2224 | ssize_t err; | 2247 | ssize_t err; |
2225 | 2248 | ||
2226 | ocount = 0; | 2249 | ocount = 0; |
2227 | for (seg = 0; seg < nr_segs; seg++) { | 2250 | err = generic_segment_checks(iov, &nr_segs, &ocount, VERIFY_READ); |
2228 | const struct iovec *iv = &iov[seg]; | 2251 | if (err) |
2229 | 2252 | return err; | |
2230 | /* | ||
2231 | * If any segment has a negative length, or the cumulative | ||
2232 | * length ever wraps negative then return -EINVAL. | ||
2233 | */ | ||
2234 | ocount += iv->iov_len; | ||
2235 | if (unlikely((ssize_t)(ocount|iv->iov_len) < 0)) | ||
2236 | return -EINVAL; | ||
2237 | if (access_ok(VERIFY_READ, iv->iov_base, iv->iov_len)) | ||
2238 | continue; | ||
2239 | if (seg == 0) | ||
2240 | return -EFAULT; | ||
2241 | nr_segs = seg; | ||
2242 | ocount -= iv->iov_len; /* This segment is no good */ | ||
2243 | break; | ||
2244 | } | ||
2245 | 2253 | ||
2246 | count = ocount; | 2254 | count = ocount; |
2247 | pos = *ppos; | 2255 | pos = *ppos; |