diff options
author | Mark Fasheh <mark.fasheh@oracle.com> | 2007-03-09 19:53:21 -0500 |
---|---|---|
committer | Mark Fasheh <mark.fasheh@oracle.com> | 2007-07-10 20:32:09 -0400 |
commit | b25801038da5823bba1b5440a57ca68afc51b6bd (patch) | |
tree | 3a6f10f8ddb3a7552630d60aabbb790d1b5a7a77 /fs/ocfs2/file.c | |
parent | 063c4561f52a74de686fe0ff2f96f4f54c9fecd2 (diff) |
ocfs2: Support xfs style space reservation ioctls
We re-use the RESVSP/UNRESVSP ioctls from xfs which allow the user to
allocate and deallocate regions to a file without zeroing data or changing
i_size.
Though renamed, the structure passed in from user is identical to struct
xfs_flock64. The three fields that are actually used right now are l_whence,
l_start and l_len.
This should get ocfs2 immediate compatibility with userspace software using
the pre-existing xfs ioctls.
Signed-off-by: Mark Fasheh <mark.fasheh@oracle.com>
Diffstat (limited to 'fs/ocfs2/file.c')
-rw-r--r-- | fs/ocfs2/file.c | 182 |
1 files changed, 169 insertions, 13 deletions
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 11f7cf9f2511..f04c7aa834cb 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c | |||
@@ -1111,17 +1111,16 @@ out: | |||
1111 | return ret; | 1111 | return ret; |
1112 | } | 1112 | } |
1113 | 1113 | ||
1114 | static int ocfs2_write_remove_suid(struct inode *inode) | 1114 | static int __ocfs2_write_remove_suid(struct inode *inode, |
1115 | struct buffer_head *bh) | ||
1115 | { | 1116 | { |
1116 | int ret; | 1117 | int ret; |
1117 | struct buffer_head *bh = NULL; | ||
1118 | struct ocfs2_inode_info *oi = OCFS2_I(inode); | ||
1119 | handle_t *handle; | 1118 | handle_t *handle; |
1120 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | 1119 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); |
1121 | struct ocfs2_dinode *di; | 1120 | struct ocfs2_dinode *di; |
1122 | 1121 | ||
1123 | mlog_entry("(Inode %llu, mode 0%o)\n", | 1122 | mlog_entry("(Inode %llu, mode 0%o)\n", |
1124 | (unsigned long long)oi->ip_blkno, inode->i_mode); | 1123 | (unsigned long long)OCFS2_I(inode)->ip_blkno, inode->i_mode); |
1125 | 1124 | ||
1126 | handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS); | 1125 | handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS); |
1127 | if (handle == NULL) { | 1126 | if (handle == NULL) { |
@@ -1130,17 +1129,11 @@ static int ocfs2_write_remove_suid(struct inode *inode) | |||
1130 | goto out; | 1129 | goto out; |
1131 | } | 1130 | } |
1132 | 1131 | ||
1133 | ret = ocfs2_read_block(osb, oi->ip_blkno, &bh, OCFS2_BH_CACHED, inode); | ||
1134 | if (ret < 0) { | ||
1135 | mlog_errno(ret); | ||
1136 | goto out_trans; | ||
1137 | } | ||
1138 | |||
1139 | ret = ocfs2_journal_access(handle, inode, bh, | 1132 | ret = ocfs2_journal_access(handle, inode, bh, |
1140 | OCFS2_JOURNAL_ACCESS_WRITE); | 1133 | OCFS2_JOURNAL_ACCESS_WRITE); |
1141 | if (ret < 0) { | 1134 | if (ret < 0) { |
1142 | mlog_errno(ret); | 1135 | mlog_errno(ret); |
1143 | goto out_bh; | 1136 | goto out_trans; |
1144 | } | 1137 | } |
1145 | 1138 | ||
1146 | inode->i_mode &= ~S_ISUID; | 1139 | inode->i_mode &= ~S_ISUID; |
@@ -1153,8 +1146,7 @@ static int ocfs2_write_remove_suid(struct inode *inode) | |||
1153 | ret = ocfs2_journal_dirty(handle, bh); | 1146 | ret = ocfs2_journal_dirty(handle, bh); |
1154 | if (ret < 0) | 1147 | if (ret < 0) |
1155 | mlog_errno(ret); | 1148 | mlog_errno(ret); |
1156 | out_bh: | 1149 | |
1157 | brelse(bh); | ||
1158 | out_trans: | 1150 | out_trans: |
1159 | ocfs2_commit_trans(osb, handle); | 1151 | ocfs2_commit_trans(osb, handle); |
1160 | out: | 1152 | out: |
@@ -1200,6 +1192,25 @@ out: | |||
1200 | return ret; | 1192 | return ret; |
1201 | } | 1193 | } |
1202 | 1194 | ||
1195 | static int ocfs2_write_remove_suid(struct inode *inode) | ||
1196 | { | ||
1197 | int ret; | ||
1198 | struct buffer_head *bh = NULL; | ||
1199 | struct ocfs2_inode_info *oi = OCFS2_I(inode); | ||
1200 | |||
1201 | ret = ocfs2_read_block(OCFS2_SB(inode->i_sb), | ||
1202 | oi->ip_blkno, &bh, OCFS2_BH_CACHED, inode); | ||
1203 | if (ret < 0) { | ||
1204 | mlog_errno(ret); | ||
1205 | goto out; | ||
1206 | } | ||
1207 | |||
1208 | ret = __ocfs2_write_remove_suid(inode, bh); | ||
1209 | out: | ||
1210 | brelse(bh); | ||
1211 | return ret; | ||
1212 | } | ||
1213 | |||
1203 | /* | 1214 | /* |
1204 | * Allocate enough extents to cover the region starting at byte offset | 1215 | * Allocate enough extents to cover the region starting at byte offset |
1205 | * start for len bytes. Existing extents are skipped, any extents | 1216 | * start for len bytes. Existing extents are skipped, any extents |
@@ -1490,6 +1501,151 @@ out: | |||
1490 | return ret; | 1501 | return ret; |
1491 | } | 1502 | } |
1492 | 1503 | ||
1504 | /* | ||
1505 | * Parts of this function taken from xfs_change_file_space() | ||
1506 | */ | ||
1507 | int ocfs2_change_file_space(struct file *file, unsigned int cmd, | ||
1508 | struct ocfs2_space_resv *sr) | ||
1509 | { | ||
1510 | int ret; | ||
1511 | s64 llen; | ||
1512 | struct inode *inode = file->f_path.dentry->d_inode; | ||
1513 | struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); | ||
1514 | struct buffer_head *di_bh = NULL; | ||
1515 | handle_t *handle; | ||
1516 | unsigned long long max_off = ocfs2_max_file_offset(inode->i_sb->s_blocksize_bits); | ||
1517 | |||
1518 | if ((cmd == OCFS2_IOC_RESVSP || cmd == OCFS2_IOC_RESVSP64) && | ||
1519 | !ocfs2_writes_unwritten_extents(osb)) | ||
1520 | return -ENOTTY; | ||
1521 | else if ((cmd == OCFS2_IOC_UNRESVSP || cmd == OCFS2_IOC_UNRESVSP64) && | ||
1522 | !ocfs2_sparse_alloc(osb)) | ||
1523 | return -ENOTTY; | ||
1524 | |||
1525 | if (!S_ISREG(inode->i_mode)) | ||
1526 | return -EINVAL; | ||
1527 | |||
1528 | if (!(file->f_mode & FMODE_WRITE)) | ||
1529 | return -EBADF; | ||
1530 | |||
1531 | if (ocfs2_is_hard_readonly(osb) || ocfs2_is_soft_readonly(osb)) | ||
1532 | return -EROFS; | ||
1533 | |||
1534 | mutex_lock(&inode->i_mutex); | ||
1535 | |||
1536 | /* | ||
1537 | * This prevents concurrent writes on other nodes | ||
1538 | */ | ||
1539 | ret = ocfs2_rw_lock(inode, 1); | ||
1540 | if (ret) { | ||
1541 | mlog_errno(ret); | ||
1542 | goto out; | ||
1543 | } | ||
1544 | |||
1545 | ret = ocfs2_meta_lock(inode, &di_bh, 1); | ||
1546 | if (ret) { | ||
1547 | mlog_errno(ret); | ||
1548 | goto out_rw_unlock; | ||
1549 | } | ||
1550 | |||
1551 | if (inode->i_flags & (S_IMMUTABLE|S_APPEND)) { | ||
1552 | ret = -EPERM; | ||
1553 | goto out_meta_unlock; | ||
1554 | } | ||
1555 | |||
1556 | switch (sr->l_whence) { | ||
1557 | case 0: /*SEEK_SET*/ | ||
1558 | break; | ||
1559 | case 1: /*SEEK_CUR*/ | ||
1560 | sr->l_start += file->f_pos; | ||
1561 | break; | ||
1562 | case 2: /*SEEK_END*/ | ||
1563 | sr->l_start += i_size_read(inode); | ||
1564 | break; | ||
1565 | default: | ||
1566 | ret = -EINVAL; | ||
1567 | goto out_meta_unlock; | ||
1568 | } | ||
1569 | sr->l_whence = 0; | ||
1570 | |||
1571 | llen = sr->l_len > 0 ? sr->l_len - 1 : sr->l_len; | ||
1572 | |||
1573 | if (sr->l_start < 0 | ||
1574 | || sr->l_start > max_off | ||
1575 | || (sr->l_start + llen) < 0 | ||
1576 | || (sr->l_start + llen) > max_off) { | ||
1577 | ret = -EINVAL; | ||
1578 | goto out_meta_unlock; | ||
1579 | } | ||
1580 | |||
1581 | if (cmd == OCFS2_IOC_RESVSP || cmd == OCFS2_IOC_RESVSP64) { | ||
1582 | if (sr->l_len <= 0) { | ||
1583 | ret = -EINVAL; | ||
1584 | goto out_meta_unlock; | ||
1585 | } | ||
1586 | } | ||
1587 | |||
1588 | if (should_remove_suid(file->f_path.dentry)) { | ||
1589 | ret = __ocfs2_write_remove_suid(inode, di_bh); | ||
1590 | if (ret) { | ||
1591 | mlog_errno(ret); | ||
1592 | goto out_meta_unlock; | ||
1593 | } | ||
1594 | } | ||
1595 | |||
1596 | down_write(&OCFS2_I(inode)->ip_alloc_sem); | ||
1597 | switch (cmd) { | ||
1598 | case OCFS2_IOC_RESVSP: | ||
1599 | case OCFS2_IOC_RESVSP64: | ||
1600 | /* | ||
1601 | * This takes unsigned offsets, but the signed ones we | ||
1602 | * pass have been checked against overflow above. | ||
1603 | */ | ||
1604 | ret = ocfs2_allocate_unwritten_extents(inode, sr->l_start, | ||
1605 | sr->l_len); | ||
1606 | break; | ||
1607 | case OCFS2_IOC_UNRESVSP: | ||
1608 | case OCFS2_IOC_UNRESVSP64: | ||
1609 | ret = ocfs2_remove_inode_range(inode, di_bh, sr->l_start, | ||
1610 | sr->l_len); | ||
1611 | break; | ||
1612 | default: | ||
1613 | ret = -EINVAL; | ||
1614 | } | ||
1615 | up_write(&OCFS2_I(inode)->ip_alloc_sem); | ||
1616 | if (ret) { | ||
1617 | mlog_errno(ret); | ||
1618 | goto out_meta_unlock; | ||
1619 | } | ||
1620 | |||
1621 | /* | ||
1622 | * We update c/mtime for these changes | ||
1623 | */ | ||
1624 | handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS); | ||
1625 | if (IS_ERR(handle)) { | ||
1626 | ret = PTR_ERR(handle); | ||
1627 | mlog_errno(ret); | ||
1628 | goto out_meta_unlock; | ||
1629 | } | ||
1630 | |||
1631 | inode->i_ctime = inode->i_mtime = CURRENT_TIME; | ||
1632 | ret = ocfs2_mark_inode_dirty(handle, inode, di_bh); | ||
1633 | if (ret < 0) | ||
1634 | mlog_errno(ret); | ||
1635 | |||
1636 | ocfs2_commit_trans(osb, handle); | ||
1637 | |||
1638 | out_meta_unlock: | ||
1639 | brelse(di_bh); | ||
1640 | ocfs2_meta_unlock(inode, 1); | ||
1641 | out_rw_unlock: | ||
1642 | ocfs2_rw_unlock(inode, 1); | ||
1643 | |||
1644 | mutex_unlock(&inode->i_mutex); | ||
1645 | out: | ||
1646 | return ret; | ||
1647 | } | ||
1648 | |||
1493 | static int ocfs2_prepare_inode_for_write(struct dentry *dentry, | 1649 | static int ocfs2_prepare_inode_for_write(struct dentry *dentry, |
1494 | loff_t *ppos, | 1650 | loff_t *ppos, |
1495 | size_t count, | 1651 | size_t count, |