aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/pipe.c37
1 files changed, 21 insertions, 16 deletions
diff --git a/fs/pipe.c b/fs/pipe.c
index 01ca9fab95fa..bdd3f96054b9 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -1112,26 +1112,20 @@ SYSCALL_DEFINE1(pipe, int __user *, fildes)
1112 * Allocate a new array of pipe buffers and copy the info over. Returns the 1112 * Allocate a new array of pipe buffers and copy the info over. Returns the
1113 * pipe size if successful, or return -ERROR on error. 1113 * pipe size if successful, or return -ERROR on error.
1114 */ 1114 */
1115static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long arg) 1115static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long nr_pages)
1116{ 1116{
1117 struct pipe_buffer *bufs; 1117 struct pipe_buffer *bufs;
1118 1118
1119 /* 1119 /*
1120 * Must be a power-of-2 currently
1121 */
1122 if (!is_power_of_2(arg))
1123 return -EINVAL;
1124
1125 /*
1126 * We can shrink the pipe, if arg >= pipe->nrbufs. Since we don't 1120 * We can shrink the pipe, if arg >= pipe->nrbufs. Since we don't
1127 * expect a lot of shrink+grow operations, just free and allocate 1121 * expect a lot of shrink+grow operations, just free and allocate
1128 * again like we would do for growing. If the pipe currently 1122 * again like we would do for growing. If the pipe currently
1129 * contains more buffers than arg, then return busy. 1123 * contains more buffers than arg, then return busy.
1130 */ 1124 */
1131 if (arg < pipe->nrbufs) 1125 if (nr_pages < pipe->nrbufs)
1132 return -EBUSY; 1126 return -EBUSY;
1133 1127
1134 bufs = kcalloc(arg, sizeof(struct pipe_buffer), GFP_KERNEL); 1128 bufs = kcalloc(nr_pages, sizeof(struct pipe_buffer), GFP_KERNEL);
1135 if (unlikely(!bufs)) 1129 if (unlikely(!bufs))
1136 return -ENOMEM; 1130 return -ENOMEM;
1137 1131
@@ -1152,8 +1146,8 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long arg)
1152 pipe->curbuf = 0; 1146 pipe->curbuf = 0;
1153 kfree(pipe->bufs); 1147 kfree(pipe->bufs);
1154 pipe->bufs = bufs; 1148 pipe->bufs = bufs;
1155 pipe->buffers = arg; 1149 pipe->buffers = nr_pages;
1156 return arg; 1150 return nr_pages * PAGE_SIZE;
1157} 1151}
1158 1152
1159long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg) 1153long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg)
@@ -1168,19 +1162,30 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg)
1168 mutex_lock(&pipe->inode->i_mutex); 1162 mutex_lock(&pipe->inode->i_mutex);
1169 1163
1170 switch (cmd) { 1164 switch (cmd) {
1171 case F_SETPIPE_SZ: 1165 case F_SETPIPE_SZ: {
1172 if (!capable(CAP_SYS_ADMIN) && arg > pipe_max_pages) 1166 unsigned long nr_pages;
1167
1168 /*
1169 * Currently the array must be a power-of-2 size, so adjust
1170 * upwards if needed.
1171 */
1172 nr_pages = (arg + PAGE_SIZE - 1) >> PAGE_SHIFT;
1173 nr_pages = roundup_pow_of_two(nr_pages);
1174
1175 if (!capable(CAP_SYS_ADMIN) && nr_pages > pipe_max_pages)
1173 return -EPERM; 1176 return -EPERM;
1177
1174 /* 1178 /*
1175 * The pipe needs to be at least 2 pages large to 1179 * The pipe needs to be at least 2 pages large to
1176 * guarantee POSIX behaviour. 1180 * guarantee POSIX behaviour.
1177 */ 1181 */
1178 if (arg < 2) 1182 if (nr_pages < 2)
1179 return -EINVAL; 1183 return -EINVAL;
1180 ret = pipe_set_size(pipe, arg); 1184 ret = pipe_set_size(pipe, nr_pages);
1181 break; 1185 break;
1186 }
1182 case F_GETPIPE_SZ: 1187 case F_GETPIPE_SZ:
1183 ret = pipe->buffers; 1188 ret = pipe->buffers * PAGE_SIZE;
1184 break; 1189 break;
1185 default: 1190 default:
1186 ret = -EINVAL; 1191 ret = -EINVAL;