diff options
Diffstat (limited to 'fs/locks.c')
-rw-r--r-- | fs/locks.c | 223 |
1 files changed, 140 insertions, 83 deletions
diff --git a/fs/locks.c b/fs/locks.c index 96b33989147d..3b0d05dcd7c1 100644 --- a/fs/locks.c +++ b/fs/locks.c | |||
@@ -133,6 +133,20 @@ | |||
133 | #define IS_FLOCK(fl) (fl->fl_flags & FL_FLOCK) | 133 | #define IS_FLOCK(fl) (fl->fl_flags & FL_FLOCK) |
134 | #define IS_LEASE(fl) (fl->fl_flags & FL_LEASE) | 134 | #define IS_LEASE(fl) (fl->fl_flags & FL_LEASE) |
135 | 135 | ||
136 | static bool lease_breaking(struct file_lock *fl) | ||
137 | { | ||
138 | return fl->fl_flags & (FL_UNLOCK_PENDING | FL_DOWNGRADE_PENDING); | ||
139 | } | ||
140 | |||
141 | static int target_leasetype(struct file_lock *fl) | ||
142 | { | ||
143 | if (fl->fl_flags & FL_UNLOCK_PENDING) | ||
144 | return F_UNLCK; | ||
145 | if (fl->fl_flags & FL_DOWNGRADE_PENDING) | ||
146 | return F_RDLCK; | ||
147 | return fl->fl_type; | ||
148 | } | ||
149 | |||
136 | int leases_enable = 1; | 150 | int leases_enable = 1; |
137 | int lease_break_time = 45; | 151 | int lease_break_time = 45; |
138 | 152 | ||
@@ -1119,6 +1133,17 @@ int locks_mandatory_area(int read_write, struct inode *inode, | |||
1119 | 1133 | ||
1120 | EXPORT_SYMBOL(locks_mandatory_area); | 1134 | EXPORT_SYMBOL(locks_mandatory_area); |
1121 | 1135 | ||
1136 | static void lease_clear_pending(struct file_lock *fl, int arg) | ||
1137 | { | ||
1138 | switch (arg) { | ||
1139 | case F_UNLCK: | ||
1140 | fl->fl_flags &= ~FL_UNLOCK_PENDING; | ||
1141 | /* fall through: */ | ||
1142 | case F_RDLCK: | ||
1143 | fl->fl_flags &= ~FL_DOWNGRADE_PENDING; | ||
1144 | } | ||
1145 | } | ||
1146 | |||
1122 | /* We already had a lease on this file; just change its type */ | 1147 | /* We already had a lease on this file; just change its type */ |
1123 | int lease_modify(struct file_lock **before, int arg) | 1148 | int lease_modify(struct file_lock **before, int arg) |
1124 | { | 1149 | { |
@@ -1127,6 +1152,7 @@ int lease_modify(struct file_lock **before, int arg) | |||
1127 | 1152 | ||
1128 | if (error) | 1153 | if (error) |
1129 | return error; | 1154 | return error; |
1155 | lease_clear_pending(fl, arg); | ||
1130 | locks_wake_up_blocks(fl); | 1156 | locks_wake_up_blocks(fl); |
1131 | if (arg == F_UNLCK) | 1157 | if (arg == F_UNLCK) |
1132 | locks_delete_lock(before); | 1158 | locks_delete_lock(before); |
@@ -1135,19 +1161,25 @@ int lease_modify(struct file_lock **before, int arg) | |||
1135 | 1161 | ||
1136 | EXPORT_SYMBOL(lease_modify); | 1162 | EXPORT_SYMBOL(lease_modify); |
1137 | 1163 | ||
1164 | static bool past_time(unsigned long then) | ||
1165 | { | ||
1166 | if (!then) | ||
1167 | /* 0 is a special value meaning "this never expires": */ | ||
1168 | return false; | ||
1169 | return time_after(jiffies, then); | ||
1170 | } | ||
1171 | |||
1138 | static void time_out_leases(struct inode *inode) | 1172 | static void time_out_leases(struct inode *inode) |
1139 | { | 1173 | { |
1140 | struct file_lock **before; | 1174 | struct file_lock **before; |
1141 | struct file_lock *fl; | 1175 | struct file_lock *fl; |
1142 | 1176 | ||
1143 | before = &inode->i_flock; | 1177 | before = &inode->i_flock; |
1144 | while ((fl = *before) && IS_LEASE(fl) && (fl->fl_type & F_INPROGRESS)) { | 1178 | while ((fl = *before) && IS_LEASE(fl) && lease_breaking(fl)) { |
1145 | if ((fl->fl_break_time == 0) | 1179 | if (past_time(fl->fl_downgrade_time)) |
1146 | || time_before(jiffies, fl->fl_break_time)) { | 1180 | lease_modify(before, F_RDLCK); |
1147 | before = &fl->fl_next; | 1181 | if (past_time(fl->fl_break_time)) |
1148 | continue; | 1182 | lease_modify(before, F_UNLCK); |
1149 | } | ||
1150 | lease_modify(before, fl->fl_type & ~F_INPROGRESS); | ||
1151 | if (fl == *before) /* lease_modify may have freed fl */ | 1183 | if (fl == *before) /* lease_modify may have freed fl */ |
1152 | before = &fl->fl_next; | 1184 | before = &fl->fl_next; |
1153 | } | 1185 | } |
@@ -1165,7 +1197,7 @@ static void time_out_leases(struct inode *inode) | |||
1165 | */ | 1197 | */ |
1166 | int __break_lease(struct inode *inode, unsigned int mode) | 1198 | int __break_lease(struct inode *inode, unsigned int mode) |
1167 | { | 1199 | { |
1168 | int error = 0, future; | 1200 | int error = 0; |
1169 | struct file_lock *new_fl, *flock; | 1201 | struct file_lock *new_fl, *flock; |
1170 | struct file_lock *fl; | 1202 | struct file_lock *fl; |
1171 | unsigned long break_time; | 1203 | unsigned long break_time; |
@@ -1182,24 +1214,13 @@ int __break_lease(struct inode *inode, unsigned int mode) | |||
1182 | if ((flock == NULL) || !IS_LEASE(flock)) | 1214 | if ((flock == NULL) || !IS_LEASE(flock)) |
1183 | goto out; | 1215 | goto out; |
1184 | 1216 | ||
1217 | if (!locks_conflict(flock, new_fl)) | ||
1218 | goto out; | ||
1219 | |||
1185 | for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) | 1220 | for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) |
1186 | if (fl->fl_owner == current->files) | 1221 | if (fl->fl_owner == current->files) |
1187 | i_have_this_lease = 1; | 1222 | i_have_this_lease = 1; |
1188 | 1223 | ||
1189 | if (want_write) { | ||
1190 | /* If we want write access, we have to revoke any lease. */ | ||
1191 | future = F_UNLCK | F_INPROGRESS; | ||
1192 | } else if (flock->fl_type & F_INPROGRESS) { | ||
1193 | /* If the lease is already being broken, we just leave it */ | ||
1194 | future = flock->fl_type; | ||
1195 | } else if (flock->fl_type & F_WRLCK) { | ||
1196 | /* Downgrade the exclusive lease to a read-only lease. */ | ||
1197 | future = F_RDLCK | F_INPROGRESS; | ||
1198 | } else { | ||
1199 | /* the existing lease was read-only, so we can read too. */ | ||
1200 | goto out; | ||
1201 | } | ||
1202 | |||
1203 | if (IS_ERR(new_fl) && !i_have_this_lease | 1224 | if (IS_ERR(new_fl) && !i_have_this_lease |
1204 | && ((mode & O_NONBLOCK) == 0)) { | 1225 | && ((mode & O_NONBLOCK) == 0)) { |
1205 | error = PTR_ERR(new_fl); | 1226 | error = PTR_ERR(new_fl); |
@@ -1214,12 +1235,18 @@ int __break_lease(struct inode *inode, unsigned int mode) | |||
1214 | } | 1235 | } |
1215 | 1236 | ||
1216 | for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) { | 1237 | for (fl = flock; fl && IS_LEASE(fl); fl = fl->fl_next) { |
1217 | if (fl->fl_type != future) { | 1238 | if (want_write) { |
1218 | fl->fl_type = future; | 1239 | if (fl->fl_flags & FL_UNLOCK_PENDING) |
1240 | continue; | ||
1241 | fl->fl_flags |= FL_UNLOCK_PENDING; | ||
1219 | fl->fl_break_time = break_time; | 1242 | fl->fl_break_time = break_time; |
1220 | /* lease must have lmops break callback */ | 1243 | } else { |
1221 | fl->fl_lmops->lm_break(fl); | 1244 | if (lease_breaking(flock)) |
1245 | continue; | ||
1246 | fl->fl_flags |= FL_DOWNGRADE_PENDING; | ||
1247 | fl->fl_downgrade_time = break_time; | ||
1222 | } | 1248 | } |
1249 | fl->fl_lmops->lm_break(fl); | ||
1223 | } | 1250 | } |
1224 | 1251 | ||
1225 | if (i_have_this_lease || (mode & O_NONBLOCK)) { | 1252 | if (i_have_this_lease || (mode & O_NONBLOCK)) { |
@@ -1243,10 +1270,13 @@ restart: | |||
1243 | if (error >= 0) { | 1270 | if (error >= 0) { |
1244 | if (error == 0) | 1271 | if (error == 0) |
1245 | time_out_leases(inode); | 1272 | time_out_leases(inode); |
1246 | /* Wait for the next lease that has not been broken yet */ | 1273 | /* |
1274 | * Wait for the next conflicting lease that has not been | ||
1275 | * broken yet | ||
1276 | */ | ||
1247 | for (flock = inode->i_flock; flock && IS_LEASE(flock); | 1277 | for (flock = inode->i_flock; flock && IS_LEASE(flock); |
1248 | flock = flock->fl_next) { | 1278 | flock = flock->fl_next) { |
1249 | if (flock->fl_type & F_INPROGRESS) | 1279 | if (locks_conflict(new_fl, flock)) |
1250 | goto restart; | 1280 | goto restart; |
1251 | } | 1281 | } |
1252 | error = 0; | 1282 | error = 0; |
@@ -1314,7 +1344,7 @@ int fcntl_getlease(struct file *filp) | |||
1314 | for (fl = filp->f_path.dentry->d_inode->i_flock; fl && IS_LEASE(fl); | 1344 | for (fl = filp->f_path.dentry->d_inode->i_flock; fl && IS_LEASE(fl); |
1315 | fl = fl->fl_next) { | 1345 | fl = fl->fl_next) { |
1316 | if (fl->fl_file == filp) { | 1346 | if (fl->fl_file == filp) { |
1317 | type = fl->fl_type & ~F_INPROGRESS; | 1347 | type = target_leasetype(fl); |
1318 | break; | 1348 | break; |
1319 | } | 1349 | } |
1320 | } | 1350 | } |
@@ -1322,50 +1352,23 @@ int fcntl_getlease(struct file *filp) | |||
1322 | return type; | 1352 | return type; |
1323 | } | 1353 | } |
1324 | 1354 | ||
1325 | /** | 1355 | int generic_add_lease(struct file *filp, long arg, struct file_lock **flp) |
1326 | * generic_setlease - sets a lease on an open file | ||
1327 | * @filp: file pointer | ||
1328 | * @arg: type of lease to obtain | ||
1329 | * @flp: input - file_lock to use, output - file_lock inserted | ||
1330 | * | ||
1331 | * The (input) flp->fl_lmops->lm_break function is required | ||
1332 | * by break_lease(). | ||
1333 | * | ||
1334 | * Called with file_lock_lock held. | ||
1335 | */ | ||
1336 | int generic_setlease(struct file *filp, long arg, struct file_lock **flp) | ||
1337 | { | 1356 | { |
1338 | struct file_lock *fl, **before, **my_before = NULL, *lease; | 1357 | struct file_lock *fl, **before, **my_before = NULL, *lease; |
1339 | struct dentry *dentry = filp->f_path.dentry; | 1358 | struct dentry *dentry = filp->f_path.dentry; |
1340 | struct inode *inode = dentry->d_inode; | 1359 | struct inode *inode = dentry->d_inode; |
1341 | int error, rdlease_count = 0, wrlease_count = 0; | 1360 | int error; |
1342 | 1361 | ||
1343 | lease = *flp; | 1362 | lease = *flp; |
1344 | 1363 | ||
1345 | error = -EACCES; | 1364 | error = -EAGAIN; |
1346 | if ((current_fsuid() != inode->i_uid) && !capable(CAP_LEASE)) | 1365 | if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0)) |
1347 | goto out; | ||
1348 | error = -EINVAL; | ||
1349 | if (!S_ISREG(inode->i_mode)) | ||
1350 | goto out; | 1366 | goto out; |
1351 | error = security_file_lock(filp, arg); | 1367 | if ((arg == F_WRLCK) |
1352 | if (error) | 1368 | && ((dentry->d_count > 1) |
1369 | || (atomic_read(&inode->i_count) > 1))) | ||
1353 | goto out; | 1370 | goto out; |
1354 | 1371 | ||
1355 | time_out_leases(inode); | ||
1356 | |||
1357 | BUG_ON(!(*flp)->fl_lmops->lm_break); | ||
1358 | |||
1359 | if (arg != F_UNLCK) { | ||
1360 | error = -EAGAIN; | ||
1361 | if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0)) | ||
1362 | goto out; | ||
1363 | if ((arg == F_WRLCK) | ||
1364 | && ((dentry->d_count > 1) | ||
1365 | || (atomic_read(&inode->i_count) > 1))) | ||
1366 | goto out; | ||
1367 | } | ||
1368 | |||
1369 | /* | 1372 | /* |
1370 | * At this point, we know that if there is an exclusive | 1373 | * At this point, we know that if there is an exclusive |
1371 | * lease on this file, then we hold it on this filp | 1374 | * lease on this file, then we hold it on this filp |
@@ -1374,27 +1377,28 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp) | |||
1374 | * then the file is not open by anyone (including us) | 1377 | * then the file is not open by anyone (including us) |
1375 | * except for this filp. | 1378 | * except for this filp. |
1376 | */ | 1379 | */ |
1380 | error = -EAGAIN; | ||
1377 | for (before = &inode->i_flock; | 1381 | for (before = &inode->i_flock; |
1378 | ((fl = *before) != NULL) && IS_LEASE(fl); | 1382 | ((fl = *before) != NULL) && IS_LEASE(fl); |
1379 | before = &fl->fl_next) { | 1383 | before = &fl->fl_next) { |
1380 | if (fl->fl_file == filp) | 1384 | if (fl->fl_file == filp) { |
1381 | my_before = before; | 1385 | my_before = before; |
1382 | else if (fl->fl_type == (F_INPROGRESS | F_UNLCK)) | 1386 | continue; |
1383 | /* | 1387 | } |
1384 | * Someone is in the process of opening this | 1388 | /* |
1385 | * file for writing so we may not take an | 1389 | * No exclusive leases if someone else has a lease on |
1386 | * exclusive lease on it. | 1390 | * this file: |
1387 | */ | 1391 | */ |
1388 | wrlease_count++; | 1392 | if (arg == F_WRLCK) |
1389 | else | 1393 | goto out; |
1390 | rdlease_count++; | 1394 | /* |
1395 | * Modifying our existing lease is OK, but no getting a | ||
1396 | * new lease if someone else is opening for write: | ||
1397 | */ | ||
1398 | if (fl->fl_flags & FL_UNLOCK_PENDING) | ||
1399 | goto out; | ||
1391 | } | 1400 | } |
1392 | 1401 | ||
1393 | error = -EAGAIN; | ||
1394 | if ((arg == F_RDLCK && (wrlease_count > 0)) || | ||
1395 | (arg == F_WRLCK && ((rdlease_count + wrlease_count) > 0))) | ||
1396 | goto out; | ||
1397 | |||
1398 | if (my_before != NULL) { | 1402 | if (my_before != NULL) { |
1399 | error = lease->fl_lmops->lm_change(my_before, arg); | 1403 | error = lease->fl_lmops->lm_change(my_before, arg); |
1400 | if (!error) | 1404 | if (!error) |
@@ -1402,9 +1406,6 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp) | |||
1402 | goto out; | 1406 | goto out; |
1403 | } | 1407 | } |
1404 | 1408 | ||
1405 | if (arg == F_UNLCK) | ||
1406 | goto out; | ||
1407 | |||
1408 | error = -EINVAL; | 1409 | error = -EINVAL; |
1409 | if (!leases_enable) | 1410 | if (!leases_enable) |
1410 | goto out; | 1411 | goto out; |
@@ -1415,6 +1416,62 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp) | |||
1415 | out: | 1416 | out: |
1416 | return error; | 1417 | return error; |
1417 | } | 1418 | } |
1419 | |||
1420 | int generic_delete_lease(struct file *filp, struct file_lock **flp) | ||
1421 | { | ||
1422 | struct file_lock *fl, **before; | ||
1423 | struct dentry *dentry = filp->f_path.dentry; | ||
1424 | struct inode *inode = dentry->d_inode; | ||
1425 | |||
1426 | for (before = &inode->i_flock; | ||
1427 | ((fl = *before) != NULL) && IS_LEASE(fl); | ||
1428 | before = &fl->fl_next) { | ||
1429 | if (fl->fl_file != filp) | ||
1430 | continue; | ||
1431 | return (*flp)->fl_lmops->lm_change(before, F_UNLCK); | ||
1432 | } | ||
1433 | return -EAGAIN; | ||
1434 | } | ||
1435 | |||
1436 | /** | ||
1437 | * generic_setlease - sets a lease on an open file | ||
1438 | * @filp: file pointer | ||
1439 | * @arg: type of lease to obtain | ||
1440 | * @flp: input - file_lock to use, output - file_lock inserted | ||
1441 | * | ||
1442 | * The (input) flp->fl_lmops->lm_break function is required | ||
1443 | * by break_lease(). | ||
1444 | * | ||
1445 | * Called with file_lock_lock held. | ||
1446 | */ | ||
1447 | int generic_setlease(struct file *filp, long arg, struct file_lock **flp) | ||
1448 | { | ||
1449 | struct dentry *dentry = filp->f_path.dentry; | ||
1450 | struct inode *inode = dentry->d_inode; | ||
1451 | int error; | ||
1452 | |||
1453 | if ((current_fsuid() != inode->i_uid) && !capable(CAP_LEASE)) | ||
1454 | return -EACCES; | ||
1455 | if (!S_ISREG(inode->i_mode)) | ||
1456 | return -EINVAL; | ||
1457 | error = security_file_lock(filp, arg); | ||
1458 | if (error) | ||
1459 | return error; | ||
1460 | |||
1461 | time_out_leases(inode); | ||
1462 | |||
1463 | BUG_ON(!(*flp)->fl_lmops->lm_break); | ||
1464 | |||
1465 | switch (arg) { | ||
1466 | case F_UNLCK: | ||
1467 | return generic_delete_lease(filp, flp); | ||
1468 | case F_RDLCK: | ||
1469 | case F_WRLCK: | ||
1470 | return generic_add_lease(filp, arg, flp); | ||
1471 | default: | ||
1472 | BUG(); | ||
1473 | } | ||
1474 | } | ||
1418 | EXPORT_SYMBOL(generic_setlease); | 1475 | EXPORT_SYMBOL(generic_setlease); |
1419 | 1476 | ||
1420 | static int __vfs_setlease(struct file *filp, long arg, struct file_lock **lease) | 1477 | static int __vfs_setlease(struct file *filp, long arg, struct file_lock **lease) |
@@ -2126,7 +2183,7 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl, | |||
2126 | } | 2183 | } |
2127 | } else if (IS_LEASE(fl)) { | 2184 | } else if (IS_LEASE(fl)) { |
2128 | seq_printf(f, "LEASE "); | 2185 | seq_printf(f, "LEASE "); |
2129 | if (fl->fl_type & F_INPROGRESS) | 2186 | if (lease_breaking(fl)) |
2130 | seq_printf(f, "BREAKING "); | 2187 | seq_printf(f, "BREAKING "); |
2131 | else if (fl->fl_file) | 2188 | else if (fl->fl_file) |
2132 | seq_printf(f, "ACTIVE "); | 2189 | seq_printf(f, "ACTIVE "); |
@@ -2142,7 +2199,7 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl, | |||
2142 | : (fl->fl_type & LOCK_WRITE) ? "WRITE" : "NONE "); | 2199 | : (fl->fl_type & LOCK_WRITE) ? "WRITE" : "NONE "); |
2143 | } else { | 2200 | } else { |
2144 | seq_printf(f, "%s ", | 2201 | seq_printf(f, "%s ", |
2145 | (fl->fl_type & F_INPROGRESS) | 2202 | (lease_breaking(fl)) |
2146 | ? (fl->fl_type & F_UNLCK) ? "UNLCK" : "READ " | 2203 | ? (fl->fl_type & F_UNLCK) ? "UNLCK" : "READ " |
2147 | : (fl->fl_type & F_WRLCK) ? "WRITE" : "READ "); | 2204 | : (fl->fl_type & F_WRLCK) ? "WRITE" : "READ "); |
2148 | } | 2205 | } |