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 | |
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>
-rw-r--r-- | fs/ocfs2/file.c | 182 | ||||
-rw-r--r-- | fs/ocfs2/file.h | 3 | ||||
-rw-r--r-- | fs/ocfs2/ioctl.c | 15 | ||||
-rw-r--r-- | fs/ocfs2/ocfs2_fs.h | 26 | ||||
-rw-r--r-- | fs/ocfs2/super.c | 4 | ||||
-rw-r--r-- | fs/ocfs2/super.h | 2 |
6 files changed, 216 insertions, 16 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, |
diff --git a/fs/ocfs2/file.h b/fs/ocfs2/file.h index 79115c92dc30..36fe27f268ee 100644 --- a/fs/ocfs2/file.h +++ b/fs/ocfs2/file.h | |||
@@ -62,4 +62,7 @@ int ocfs2_should_update_atime(struct inode *inode, | |||
62 | int ocfs2_update_inode_atime(struct inode *inode, | 62 | int ocfs2_update_inode_atime(struct inode *inode, |
63 | struct buffer_head *bh); | 63 | struct buffer_head *bh); |
64 | 64 | ||
65 | int ocfs2_change_file_space(struct file *file, unsigned int cmd, | ||
66 | struct ocfs2_space_resv *sr); | ||
67 | |||
65 | #endif /* OCFS2_FILE_H */ | 68 | #endif /* OCFS2_FILE_H */ |
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c index f3ad21ad9aed..bd68c3f2afbe 100644 --- a/fs/ocfs2/ioctl.c +++ b/fs/ocfs2/ioctl.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include "ocfs2.h" | 14 | #include "ocfs2.h" |
15 | #include "alloc.h" | 15 | #include "alloc.h" |
16 | #include "dlmglue.h" | 16 | #include "dlmglue.h" |
17 | #include "file.h" | ||
17 | #include "inode.h" | 18 | #include "inode.h" |
18 | #include "journal.h" | 19 | #include "journal.h" |
19 | 20 | ||
@@ -115,6 +116,7 @@ int ocfs2_ioctl(struct inode * inode, struct file * filp, | |||
115 | { | 116 | { |
116 | unsigned int flags; | 117 | unsigned int flags; |
117 | int status; | 118 | int status; |
119 | struct ocfs2_space_resv sr; | ||
118 | 120 | ||
119 | switch (cmd) { | 121 | switch (cmd) { |
120 | case OCFS2_IOC_GETFLAGS: | 122 | case OCFS2_IOC_GETFLAGS: |
@@ -130,6 +132,14 @@ int ocfs2_ioctl(struct inode * inode, struct file * filp, | |||
130 | 132 | ||
131 | return ocfs2_set_inode_attr(inode, flags, | 133 | return ocfs2_set_inode_attr(inode, flags, |
132 | OCFS2_FL_MODIFIABLE); | 134 | OCFS2_FL_MODIFIABLE); |
135 | case OCFS2_IOC_RESVSP: | ||
136 | case OCFS2_IOC_RESVSP64: | ||
137 | case OCFS2_IOC_UNRESVSP: | ||
138 | case OCFS2_IOC_UNRESVSP64: | ||
139 | if (copy_from_user(&sr, (int __user *) arg, sizeof(sr))) | ||
140 | return -EFAULT; | ||
141 | |||
142 | return ocfs2_change_file_space(filp, cmd, &sr); | ||
133 | default: | 143 | default: |
134 | return -ENOTTY; | 144 | return -ENOTTY; |
135 | } | 145 | } |
@@ -148,6 +158,11 @@ long ocfs2_compat_ioctl(struct file *file, unsigned cmd, unsigned long arg) | |||
148 | case OCFS2_IOC32_SETFLAGS: | 158 | case OCFS2_IOC32_SETFLAGS: |
149 | cmd = OCFS2_IOC_SETFLAGS; | 159 | cmd = OCFS2_IOC_SETFLAGS; |
150 | break; | 160 | break; |
161 | case OCFS2_IOC_RESVSP: | ||
162 | case OCFS2_IOC_RESVSP64: | ||
163 | case OCFS2_IOC_UNRESVSP: | ||
164 | case OCFS2_IOC_UNRESVSP64: | ||
165 | break; | ||
151 | default: | 166 | default: |
152 | return -ENOIOCTLCMD; | 167 | return -ENOIOCTLCMD; |
153 | } | 168 | } |
diff --git a/fs/ocfs2/ocfs2_fs.h b/fs/ocfs2/ocfs2_fs.h index c20a74b99d87..82f8a75b207e 100644 --- a/fs/ocfs2/ocfs2_fs.h +++ b/fs/ocfs2/ocfs2_fs.h | |||
@@ -175,6 +175,32 @@ | |||
175 | #define OCFS2_IOC32_SETFLAGS _IOW('f', 2, int) | 175 | #define OCFS2_IOC32_SETFLAGS _IOW('f', 2, int) |
176 | 176 | ||
177 | /* | 177 | /* |
178 | * Space reservation / allocation / free ioctls and argument structure | ||
179 | * are designed to be compatible with XFS. | ||
180 | * | ||
181 | * ALLOCSP* and FREESP* are not and will never be supported, but are | ||
182 | * included here for completeness. | ||
183 | */ | ||
184 | struct ocfs2_space_resv { | ||
185 | __s16 l_type; | ||
186 | __s16 l_whence; | ||
187 | __s64 l_start; | ||
188 | __s64 l_len; /* len == 0 means until end of file */ | ||
189 | __s32 l_sysid; | ||
190 | __u32 l_pid; | ||
191 | __s32 l_pad[4]; /* reserve area */ | ||
192 | }; | ||
193 | |||
194 | #define OCFS2_IOC_ALLOCSP _IOW ('X', 10, struct ocfs2_space_resv) | ||
195 | #define OCFS2_IOC_FREESP _IOW ('X', 11, struct ocfs2_space_resv) | ||
196 | #define OCFS2_IOC_RESVSP _IOW ('X', 40, struct ocfs2_space_resv) | ||
197 | #define OCFS2_IOC_UNRESVSP _IOW ('X', 41, struct ocfs2_space_resv) | ||
198 | #define OCFS2_IOC_ALLOCSP64 _IOW ('X', 36, struct ocfs2_space_resv) | ||
199 | #define OCFS2_IOC_FREESP64 _IOW ('X', 37, struct ocfs2_space_resv) | ||
200 | #define OCFS2_IOC_RESVSP64 _IOW ('X', 42, struct ocfs2_space_resv) | ||
201 | #define OCFS2_IOC_UNRESVSP64 _IOW ('X', 43, struct ocfs2_space_resv) | ||
202 | |||
203 | /* | ||
178 | * Journal Flags (ocfs2_dinode.id1.journal1.i_flags) | 204 | * Journal Flags (ocfs2_dinode.id1.journal1.i_flags) |
179 | */ | 205 | */ |
180 | #define OCFS2_JOURNAL_DIRTY_FL (0x00000001) /* Journal needs recovery */ | 206 | #define OCFS2_JOURNAL_DIRTY_FL (0x00000001) /* Journal needs recovery */ |
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index f07718a7552b..3a5a1ed09ac9 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c | |||
@@ -115,8 +115,6 @@ static void ocfs2_write_super(struct super_block *sb); | |||
115 | static struct inode *ocfs2_alloc_inode(struct super_block *sb); | 115 | static struct inode *ocfs2_alloc_inode(struct super_block *sb); |
116 | static void ocfs2_destroy_inode(struct inode *inode); | 116 | static void ocfs2_destroy_inode(struct inode *inode); |
117 | 117 | ||
118 | static unsigned long long ocfs2_max_file_offset(unsigned int blockshift); | ||
119 | |||
120 | static const struct super_operations ocfs2_sops = { | 118 | static const struct super_operations ocfs2_sops = { |
121 | .statfs = ocfs2_statfs, | 119 | .statfs = ocfs2_statfs, |
122 | .alloc_inode = ocfs2_alloc_inode, | 120 | .alloc_inode = ocfs2_alloc_inode, |
@@ -321,7 +319,7 @@ static void ocfs2_destroy_inode(struct inode *inode) | |||
321 | /* From xfs_super.c:xfs_max_file_offset | 319 | /* From xfs_super.c:xfs_max_file_offset |
322 | * Copyright (c) 2000-2004 Silicon Graphics, Inc. | 320 | * Copyright (c) 2000-2004 Silicon Graphics, Inc. |
323 | */ | 321 | */ |
324 | static unsigned long long ocfs2_max_file_offset(unsigned int blockshift) | 322 | unsigned long long ocfs2_max_file_offset(unsigned int blockshift) |
325 | { | 323 | { |
326 | unsigned int pagefactor = 1; | 324 | unsigned int pagefactor = 1; |
327 | unsigned int bitshift = BITS_PER_LONG - 1; | 325 | unsigned int bitshift = BITS_PER_LONG - 1; |
diff --git a/fs/ocfs2/super.h b/fs/ocfs2/super.h index 783f5270f2a1..3b9cb3d0b008 100644 --- a/fs/ocfs2/super.h +++ b/fs/ocfs2/super.h | |||
@@ -45,4 +45,6 @@ void __ocfs2_abort(struct super_block *sb, | |||
45 | 45 | ||
46 | #define ocfs2_abort(sb, fmt, args...) __ocfs2_abort(sb, __PRETTY_FUNCTION__, fmt, ##args) | 46 | #define ocfs2_abort(sb, fmt, args...) __ocfs2_abort(sb, __PRETTY_FUNCTION__, fmt, ##args) |
47 | 47 | ||
48 | unsigned long long ocfs2_max_file_offset(unsigned int blockshift); | ||
49 | |||
48 | #endif /* OCFS2_SUPER_H */ | 50 | #endif /* OCFS2_SUPER_H */ |