]>
Commit | Line | Data |
---|---|---|
1fb7e48d DC |
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" | |
239880ef | 22 | #include "xfs_log_format.h" |
70a9883c | 23 | #include "xfs_shared.h" |
239880ef | 24 | #include "xfs_trans_resv.h" |
1fb7e48d DC |
25 | #include "xfs_mount.h" |
26 | #include "xfs_bmap_btree.h" | |
27 | #include "xfs_inode.h" | |
28 | #include "xfs_error.h" | |
29 | #include "xfs_trace.h" | |
30 | #include "xfs_symlink.h" | |
31 | #include "xfs_cksum.h" | |
239880ef | 32 | #include "xfs_trans.h" |
1fb7e48d | 33 | #include "xfs_buf_item.h" |
a45086e2 | 34 | #include "xfs_log.h" |
1fb7e48d DC |
35 | |
36 | ||
37 | /* | |
38 | * Each contiguous block has a header, so it is not just a simple pathlen | |
39 | * to FSB conversion. | |
40 | */ | |
41 | int | |
42 | xfs_symlink_blocks( | |
43 | struct xfs_mount *mp, | |
44 | int pathlen) | |
45 | { | |
46 | int buflen = XFS_SYMLINK_BUF_SPACE(mp, mp->m_sb.sb_blocksize); | |
47 | ||
48 | return (pathlen + buflen - 1) / buflen; | |
49 | } | |
50 | ||
51 | int | |
52 | xfs_symlink_hdr_set( | |
53 | struct xfs_mount *mp, | |
54 | xfs_ino_t ino, | |
55 | uint32_t offset, | |
56 | uint32_t size, | |
57 | struct xfs_buf *bp) | |
58 | { | |
59 | struct xfs_dsymlink_hdr *dsl = bp->b_addr; | |
60 | ||
61 | if (!xfs_sb_version_hascrc(&mp->m_sb)) | |
62 | return 0; | |
63 | ||
a45086e2 | 64 | memset(dsl, 0, sizeof(struct xfs_dsymlink_hdr)); |
1fb7e48d DC |
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); | |
ce748eaa | 68 | uuid_copy(&dsl->sl_uuid, &mp->m_sb.sb_meta_uuid); |
1fb7e48d DC |
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( | |
1fb7e48d DC |
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; | |
ce748eaa | 112 | if (!uuid_equal(&dsl->sl_uuid, &mp->m_sb.sb_meta_uuid)) |
1fb7e48d DC |
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; | |
a45086e2 BF |
121 | if (!xfs_log_check_lsn(mp, be64_to_cpu(dsl->sl_lsn))) |
122 | return false; | |
1fb7e48d DC |
123 | |
124 | return true; | |
125 | } | |
126 | ||
127 | static void | |
128 | xfs_symlink_read_verify( | |
129 | struct xfs_buf *bp) | |
130 | { | |
131 | struct xfs_mount *mp = bp->b_target->bt_mount; | |
132 | ||
133 | /* no verification of non-crc buffers */ | |
134 | if (!xfs_sb_version_hascrc(&mp->m_sb)) | |
135 | return; | |
136 | ||
ce5028cf | 137 | if (!xfs_buf_verify_cksum(bp, XFS_SYMLINK_CRC_OFF)) |
2451337d | 138 | xfs_buf_ioerror(bp, -EFSBADCRC); |
ce5028cf | 139 | else if (!xfs_symlink_verify(bp)) |
2451337d | 140 | xfs_buf_ioerror(bp, -EFSCORRUPTED); |
ce5028cf ES |
141 | |
142 | if (bp->b_error) | |
143 | xfs_verifier_error(bp); | |
1fb7e48d DC |
144 | } |
145 | ||
146 | static void | |
147 | xfs_symlink_write_verify( | |
148 | struct xfs_buf *bp) | |
149 | { | |
150 | struct xfs_mount *mp = bp->b_target->bt_mount; | |
151 | struct xfs_buf_log_item *bip = bp->b_fspriv; | |
152 | ||
153 | /* no verification of non-crc buffers */ | |
154 | if (!xfs_sb_version_hascrc(&mp->m_sb)) | |
155 | return; | |
156 | ||
157 | if (!xfs_symlink_verify(bp)) { | |
2451337d | 158 | xfs_buf_ioerror(bp, -EFSCORRUPTED); |
ce5028cf | 159 | xfs_verifier_error(bp); |
1fb7e48d DC |
160 | return; |
161 | } | |
162 | ||
163 | if (bip) { | |
164 | struct xfs_dsymlink_hdr *dsl = bp->b_addr; | |
165 | dsl->sl_lsn = cpu_to_be64(bip->bli_item.li_lsn); | |
166 | } | |
f1dbcd7e | 167 | xfs_buf_update_cksum(bp, XFS_SYMLINK_CRC_OFF); |
1fb7e48d DC |
168 | } |
169 | ||
170 | const struct xfs_buf_ops xfs_symlink_buf_ops = { | |
171 | .verify_read = xfs_symlink_read_verify, | |
172 | .verify_write = xfs_symlink_write_verify, | |
173 | }; | |
174 | ||
175 | void | |
176 | xfs_symlink_local_to_remote( | |
177 | struct xfs_trans *tp, | |
178 | struct xfs_buf *bp, | |
179 | struct xfs_inode *ip, | |
180 | struct xfs_ifork *ifp) | |
181 | { | |
182 | struct xfs_mount *mp = ip->i_mount; | |
183 | char *buf; | |
184 | ||
fe22d552 DC |
185 | xfs_trans_buf_set_type(tp, bp, XFS_BLFT_SYMLINK_BUF); |
186 | ||
1fb7e48d DC |
187 | if (!xfs_sb_version_hascrc(&mp->m_sb)) { |
188 | bp->b_ops = NULL; | |
189 | memcpy(bp->b_addr, ifp->if_u1.if_data, ifp->if_bytes); | |
b7cdc66b | 190 | xfs_trans_log_buf(tp, bp, 0, ifp->if_bytes - 1); |
1fb7e48d DC |
191 | return; |
192 | } | |
193 | ||
194 | /* | |
195 | * As this symlink fits in an inode literal area, it must also fit in | |
196 | * the smallest buffer the filesystem supports. | |
197 | */ | |
198 | ASSERT(BBTOB(bp->b_length) >= | |
199 | ifp->if_bytes + sizeof(struct xfs_dsymlink_hdr)); | |
200 | ||
201 | bp->b_ops = &xfs_symlink_buf_ops; | |
202 | ||
203 | buf = bp->b_addr; | |
204 | buf += xfs_symlink_hdr_set(mp, ip->i_ino, 0, ifp->if_bytes, bp); | |
205 | memcpy(buf, ifp->if_u1.if_data, ifp->if_bytes); | |
b7cdc66b BF |
206 | xfs_trans_log_buf(tp, bp, 0, sizeof(struct xfs_dsymlink_hdr) + |
207 | ifp->if_bytes - 1); | |
1fb7e48d | 208 | } |