diff options
Diffstat (limited to 'fs/xfs/libxfs/xfs_symlink_remote.c')
-rw-r--r-- | fs/xfs/libxfs/xfs_symlink_remote.c | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/fs/xfs/libxfs/xfs_symlink_remote.c b/fs/xfs/libxfs/xfs_symlink_remote.c new file mode 100644 index 000000000000..23c2f2577c8d --- /dev/null +++ b/fs/xfs/libxfs/xfs_symlink_remote.c | |||
@@ -0,0 +1,201 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000-2006 Silicon Graphics, Inc. | ||
3 | * Copyright (c) 2012-2013 Red Hat, Inc. | ||
4 | * All rights reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it would be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write the Free Software Foundation, | ||
17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | */ | ||
19 | #include "xfs.h" | ||
20 | #include "xfs_fs.h" | ||
21 | #include "xfs_format.h" | ||
22 | #include "xfs_log_format.h" | ||
23 | #include "xfs_shared.h" | ||
24 | #include "xfs_trans_resv.h" | ||
25 | #include "xfs_ag.h" | ||
26 | #include "xfs_sb.h" | ||
27 | #include "xfs_mount.h" | ||
28 | #include "xfs_bmap_btree.h" | ||
29 | #include "xfs_inode.h" | ||
30 | #include "xfs_error.h" | ||
31 | #include "xfs_trace.h" | ||
32 | #include "xfs_symlink.h" | ||
33 | #include "xfs_cksum.h" | ||
34 | #include "xfs_trans.h" | ||
35 | #include "xfs_buf_item.h" | ||
36 | |||
37 | |||
38 | /* | ||
39 | * Each contiguous block has a header, so it is not just a simple pathlen | ||
40 | * to FSB conversion. | ||
41 | */ | ||
42 | int | ||
43 | xfs_symlink_blocks( | ||
44 | struct xfs_mount *mp, | ||
45 | int pathlen) | ||
46 | { | ||
47 | int buflen = XFS_SYMLINK_BUF_SPACE(mp, mp->m_sb.sb_blocksize); | ||
48 | |||
49 | return (pathlen + buflen - 1) / buflen; | ||
50 | } | ||
51 | |||
52 | int | ||
53 | xfs_symlink_hdr_set( | ||
54 | struct xfs_mount *mp, | ||
55 | xfs_ino_t ino, | ||
56 | uint32_t offset, | ||
57 | uint32_t size, | ||
58 | struct xfs_buf *bp) | ||
59 | { | ||
60 | struct xfs_dsymlink_hdr *dsl = bp->b_addr; | ||
61 | |||
62 | if (!xfs_sb_version_hascrc(&mp->m_sb)) | ||
63 | return 0; | ||
64 | |||
65 | dsl->sl_magic = cpu_to_be32(XFS_SYMLINK_MAGIC); | ||
66 | dsl->sl_offset = cpu_to_be32(offset); | ||
67 | dsl->sl_bytes = cpu_to_be32(size); | ||
68 | uuid_copy(&dsl->sl_uuid, &mp->m_sb.sb_uuid); | ||
69 | dsl->sl_owner = cpu_to_be64(ino); | ||
70 | dsl->sl_blkno = cpu_to_be64(bp->b_bn); | ||
71 | bp->b_ops = &xfs_symlink_buf_ops; | ||
72 | |||
73 | return sizeof(struct xfs_dsymlink_hdr); | ||
74 | } | ||
75 | |||
76 | /* | ||
77 | * Checking of the symlink header is split into two parts. the verifier does | ||
78 | * CRC, location and bounds checking, the unpacking function checks the path | ||
79 | * parameters and owner. | ||
80 | */ | ||
81 | bool | ||
82 | xfs_symlink_hdr_ok( | ||
83 | xfs_ino_t ino, | ||
84 | uint32_t offset, | ||
85 | uint32_t size, | ||
86 | struct xfs_buf *bp) | ||
87 | { | ||
88 | struct xfs_dsymlink_hdr *dsl = bp->b_addr; | ||
89 | |||
90 | if (offset != be32_to_cpu(dsl->sl_offset)) | ||
91 | return false; | ||
92 | if (size != be32_to_cpu(dsl->sl_bytes)) | ||
93 | return false; | ||
94 | if (ino != be64_to_cpu(dsl->sl_owner)) | ||
95 | return false; | ||
96 | |||
97 | /* ok */ | ||
98 | return true; | ||
99 | } | ||
100 | |||
101 | static bool | ||
102 | xfs_symlink_verify( | ||
103 | struct xfs_buf *bp) | ||
104 | { | ||
105 | struct xfs_mount *mp = bp->b_target->bt_mount; | ||
106 | struct xfs_dsymlink_hdr *dsl = bp->b_addr; | ||
107 | |||
108 | if (!xfs_sb_version_hascrc(&mp->m_sb)) | ||
109 | return false; | ||
110 | if (dsl->sl_magic != cpu_to_be32(XFS_SYMLINK_MAGIC)) | ||
111 | return false; | ||
112 | if (!uuid_equal(&dsl->sl_uuid, &mp->m_sb.sb_uuid)) | ||
113 | return false; | ||
114 | if (bp->b_bn != be64_to_cpu(dsl->sl_blkno)) | ||
115 | return false; | ||
116 | if (be32_to_cpu(dsl->sl_offset) + | ||
117 | be32_to_cpu(dsl->sl_bytes) >= MAXPATHLEN) | ||
118 | return false; | ||
119 | if (dsl->sl_owner == 0) | ||
120 | return false; | ||
121 | |||
122 | return true; | ||
123 | } | ||
124 | |||
125 | static void | ||
126 | xfs_symlink_read_verify( | ||
127 | struct xfs_buf *bp) | ||
128 | { | ||
129 | struct xfs_mount *mp = bp->b_target->bt_mount; | ||
130 | |||
131 | /* no verification of non-crc buffers */ | ||
132 | if (!xfs_sb_version_hascrc(&mp->m_sb)) | ||
133 | return; | ||
134 | |||
135 | if (!xfs_buf_verify_cksum(bp, XFS_SYMLINK_CRC_OFF)) | ||
136 | xfs_buf_ioerror(bp, EFSBADCRC); | ||
137 | else if (!xfs_symlink_verify(bp)) | ||
138 | xfs_buf_ioerror(bp, EFSCORRUPTED); | ||
139 | |||
140 | if (bp->b_error) | ||
141 | xfs_verifier_error(bp); | ||
142 | } | ||
143 | |||
144 | static void | ||
145 | xfs_symlink_write_verify( | ||
146 | struct xfs_buf *bp) | ||
147 | { | ||
148 | struct xfs_mount *mp = bp->b_target->bt_mount; | ||
149 | struct xfs_buf_log_item *bip = bp->b_fspriv; | ||
150 | |||
151 | /* no verification of non-crc buffers */ | ||
152 | if (!xfs_sb_version_hascrc(&mp->m_sb)) | ||
153 | return; | ||
154 | |||
155 | if (!xfs_symlink_verify(bp)) { | ||
156 | xfs_buf_ioerror(bp, EFSCORRUPTED); | ||
157 | xfs_verifier_error(bp); | ||
158 | return; | ||
159 | } | ||
160 | |||
161 | if (bip) { | ||
162 | struct xfs_dsymlink_hdr *dsl = bp->b_addr; | ||
163 | dsl->sl_lsn = cpu_to_be64(bip->bli_item.li_lsn); | ||
164 | } | ||
165 | xfs_buf_update_cksum(bp, XFS_SYMLINK_CRC_OFF); | ||
166 | } | ||
167 | |||
168 | const struct xfs_buf_ops xfs_symlink_buf_ops = { | ||
169 | .verify_read = xfs_symlink_read_verify, | ||
170 | .verify_write = xfs_symlink_write_verify, | ||
171 | }; | ||
172 | |||
173 | void | ||
174 | xfs_symlink_local_to_remote( | ||
175 | struct xfs_trans *tp, | ||
176 | struct xfs_buf *bp, | ||
177 | struct xfs_inode *ip, | ||
178 | struct xfs_ifork *ifp) | ||
179 | { | ||
180 | struct xfs_mount *mp = ip->i_mount; | ||
181 | char *buf; | ||
182 | |||
183 | if (!xfs_sb_version_hascrc(&mp->m_sb)) { | ||
184 | bp->b_ops = NULL; | ||
185 | memcpy(bp->b_addr, ifp->if_u1.if_data, ifp->if_bytes); | ||
186 | return; | ||
187 | } | ||
188 | |||
189 | /* | ||
190 | * As this symlink fits in an inode literal area, it must also fit in | ||
191 | * the smallest buffer the filesystem supports. | ||
192 | */ | ||
193 | ASSERT(BBTOB(bp->b_length) >= | ||
194 | ifp->if_bytes + sizeof(struct xfs_dsymlink_hdr)); | ||
195 | |||
196 | bp->b_ops = &xfs_symlink_buf_ops; | ||
197 | |||
198 | buf = bp->b_addr; | ||
199 | buf += xfs_symlink_hdr_set(mp, ip->i_ino, 0, ifp->if_bytes, bp); | ||
200 | memcpy(buf, ifp->if_u1.if_data, ifp->if_bytes); | ||
201 | } | ||