Commit | Line | Data |
---|---|---|
0b61f8a4 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1fb7e48d DC |
2 | /* |
3 | * Copyright (c) 2000-2006 Silicon Graphics, Inc. | |
4 | * Copyright (c) 2012-2013 Red Hat, Inc. | |
5 | * All rights reserved. | |
1fb7e48d DC |
6 | */ |
7 | #include "xfs.h" | |
8 | #include "xfs_fs.h" | |
9 | #include "xfs_format.h" | |
239880ef | 10 | #include "xfs_log_format.h" |
70a9883c | 11 | #include "xfs_shared.h" |
239880ef | 12 | #include "xfs_trans_resv.h" |
1fb7e48d DC |
13 | #include "xfs_mount.h" |
14 | #include "xfs_bmap_btree.h" | |
15 | #include "xfs_inode.h" | |
16 | #include "xfs_error.h" | |
17 | #include "xfs_trace.h" | |
18 | #include "xfs_symlink.h" | |
19 | #include "xfs_cksum.h" | |
239880ef | 20 | #include "xfs_trans.h" |
1fb7e48d | 21 | #include "xfs_buf_item.h" |
a45086e2 | 22 | #include "xfs_log.h" |
1fb7e48d DC |
23 | |
24 | ||
25 | /* | |
26 | * Each contiguous block has a header, so it is not just a simple pathlen | |
27 | * to FSB conversion. | |
28 | */ | |
29 | int | |
30 | xfs_symlink_blocks( | |
31 | struct xfs_mount *mp, | |
32 | int pathlen) | |
33 | { | |
34 | int buflen = XFS_SYMLINK_BUF_SPACE(mp, mp->m_sb.sb_blocksize); | |
35 | ||
36 | return (pathlen + buflen - 1) / buflen; | |
37 | } | |
38 | ||
39 | int | |
40 | xfs_symlink_hdr_set( | |
41 | struct xfs_mount *mp, | |
42 | xfs_ino_t ino, | |
43 | uint32_t offset, | |
44 | uint32_t size, | |
45 | struct xfs_buf *bp) | |
46 | { | |
47 | struct xfs_dsymlink_hdr *dsl = bp->b_addr; | |
48 | ||
49 | if (!xfs_sb_version_hascrc(&mp->m_sb)) | |
50 | return 0; | |
51 | ||
a45086e2 | 52 | memset(dsl, 0, sizeof(struct xfs_dsymlink_hdr)); |
1fb7e48d DC |
53 | dsl->sl_magic = cpu_to_be32(XFS_SYMLINK_MAGIC); |
54 | dsl->sl_offset = cpu_to_be32(offset); | |
55 | dsl->sl_bytes = cpu_to_be32(size); | |
ce748eaa | 56 | uuid_copy(&dsl->sl_uuid, &mp->m_sb.sb_meta_uuid); |
1fb7e48d DC |
57 | dsl->sl_owner = cpu_to_be64(ino); |
58 | dsl->sl_blkno = cpu_to_be64(bp->b_bn); | |
59 | bp->b_ops = &xfs_symlink_buf_ops; | |
60 | ||
61 | return sizeof(struct xfs_dsymlink_hdr); | |
62 | } | |
63 | ||
64 | /* | |
65 | * Checking of the symlink header is split into two parts. the verifier does | |
66 | * CRC, location and bounds checking, the unpacking function checks the path | |
67 | * parameters and owner. | |
68 | */ | |
69 | bool | |
70 | xfs_symlink_hdr_ok( | |
1fb7e48d DC |
71 | xfs_ino_t ino, |
72 | uint32_t offset, | |
73 | uint32_t size, | |
74 | struct xfs_buf *bp) | |
75 | { | |
76 | struct xfs_dsymlink_hdr *dsl = bp->b_addr; | |
77 | ||
78 | if (offset != be32_to_cpu(dsl->sl_offset)) | |
79 | return false; | |
80 | if (size != be32_to_cpu(dsl->sl_bytes)) | |
81 | return false; | |
82 | if (ino != be64_to_cpu(dsl->sl_owner)) | |
83 | return false; | |
84 | ||
85 | /* ok */ | |
86 | return true; | |
87 | } | |
88 | ||
a6a781a5 | 89 | static xfs_failaddr_t |
1fb7e48d DC |
90 | xfs_symlink_verify( |
91 | struct xfs_buf *bp) | |
92 | { | |
93 | struct xfs_mount *mp = bp->b_target->bt_mount; | |
94 | struct xfs_dsymlink_hdr *dsl = bp->b_addr; | |
95 | ||
96 | if (!xfs_sb_version_hascrc(&mp->m_sb)) | |
a6a781a5 | 97 | return __this_address; |
1fb7e48d | 98 | if (dsl->sl_magic != cpu_to_be32(XFS_SYMLINK_MAGIC)) |
a6a781a5 | 99 | return __this_address; |
ce748eaa | 100 | if (!uuid_equal(&dsl->sl_uuid, &mp->m_sb.sb_meta_uuid)) |
a6a781a5 | 101 | return __this_address; |
1fb7e48d | 102 | if (bp->b_bn != be64_to_cpu(dsl->sl_blkno)) |
a6a781a5 | 103 | return __this_address; |
1fb7e48d | 104 | if (be32_to_cpu(dsl->sl_offset) + |
6eb0b8df | 105 | be32_to_cpu(dsl->sl_bytes) >= XFS_SYMLINK_MAXLEN) |
a6a781a5 | 106 | return __this_address; |
1fb7e48d | 107 | if (dsl->sl_owner == 0) |
a6a781a5 | 108 | return __this_address; |
a45086e2 | 109 | if (!xfs_log_check_lsn(mp, be64_to_cpu(dsl->sl_lsn))) |
a6a781a5 | 110 | return __this_address; |
1fb7e48d | 111 | |
a6a781a5 | 112 | return NULL; |
1fb7e48d DC |
113 | } |
114 | ||
115 | static void | |
116 | xfs_symlink_read_verify( | |
117 | struct xfs_buf *bp) | |
118 | { | |
119 | struct xfs_mount *mp = bp->b_target->bt_mount; | |
bc1a09b8 | 120 | xfs_failaddr_t fa; |
1fb7e48d DC |
121 | |
122 | /* no verification of non-crc buffers */ | |
123 | if (!xfs_sb_version_hascrc(&mp->m_sb)) | |
124 | return; | |
125 | ||
ce5028cf | 126 | if (!xfs_buf_verify_cksum(bp, XFS_SYMLINK_CRC_OFF)) |
bc1a09b8 DW |
127 | xfs_verifier_error(bp, -EFSBADCRC, __this_address); |
128 | else { | |
129 | fa = xfs_symlink_verify(bp); | |
130 | if (fa) | |
131 | xfs_verifier_error(bp, -EFSCORRUPTED, fa); | |
132 | } | |
1fb7e48d DC |
133 | } |
134 | ||
135 | static void | |
136 | xfs_symlink_write_verify( | |
137 | struct xfs_buf *bp) | |
138 | { | |
139 | struct xfs_mount *mp = bp->b_target->bt_mount; | |
fb1755a6 | 140 | struct xfs_buf_log_item *bip = bp->b_log_item; |
bc1a09b8 | 141 | xfs_failaddr_t fa; |
1fb7e48d DC |
142 | |
143 | /* no verification of non-crc buffers */ | |
144 | if (!xfs_sb_version_hascrc(&mp->m_sb)) | |
145 | return; | |
146 | ||
bc1a09b8 DW |
147 | fa = xfs_symlink_verify(bp); |
148 | if (fa) { | |
149 | xfs_verifier_error(bp, -EFSCORRUPTED, fa); | |
1fb7e48d DC |
150 | return; |
151 | } | |
152 | ||
153 | if (bip) { | |
154 | struct xfs_dsymlink_hdr *dsl = bp->b_addr; | |
155 | dsl->sl_lsn = cpu_to_be64(bip->bli_item.li_lsn); | |
156 | } | |
f1dbcd7e | 157 | xfs_buf_update_cksum(bp, XFS_SYMLINK_CRC_OFF); |
1fb7e48d DC |
158 | } |
159 | ||
160 | const struct xfs_buf_ops xfs_symlink_buf_ops = { | |
233135b7 | 161 | .name = "xfs_symlink", |
1fb7e48d DC |
162 | .verify_read = xfs_symlink_read_verify, |
163 | .verify_write = xfs_symlink_write_verify, | |
b5572597 | 164 | .verify_struct = xfs_symlink_verify, |
1fb7e48d DC |
165 | }; |
166 | ||
167 | void | |
168 | xfs_symlink_local_to_remote( | |
169 | struct xfs_trans *tp, | |
170 | struct xfs_buf *bp, | |
171 | struct xfs_inode *ip, | |
172 | struct xfs_ifork *ifp) | |
173 | { | |
174 | struct xfs_mount *mp = ip->i_mount; | |
175 | char *buf; | |
176 | ||
fe22d552 DC |
177 | xfs_trans_buf_set_type(tp, bp, XFS_BLFT_SYMLINK_BUF); |
178 | ||
1fb7e48d DC |
179 | if (!xfs_sb_version_hascrc(&mp->m_sb)) { |
180 | bp->b_ops = NULL; | |
181 | memcpy(bp->b_addr, ifp->if_u1.if_data, ifp->if_bytes); | |
b7cdc66b | 182 | xfs_trans_log_buf(tp, bp, 0, ifp->if_bytes - 1); |
1fb7e48d DC |
183 | return; |
184 | } | |
185 | ||
186 | /* | |
187 | * As this symlink fits in an inode literal area, it must also fit in | |
188 | * the smallest buffer the filesystem supports. | |
189 | */ | |
190 | ASSERT(BBTOB(bp->b_length) >= | |
191 | ifp->if_bytes + sizeof(struct xfs_dsymlink_hdr)); | |
192 | ||
193 | bp->b_ops = &xfs_symlink_buf_ops; | |
194 | ||
195 | buf = bp->b_addr; | |
196 | buf += xfs_symlink_hdr_set(mp, ip->i_ino, 0, ifp->if_bytes, bp); | |
197 | memcpy(buf, ifp->if_u1.if_data, ifp->if_bytes); | |
b7cdc66b BF |
198 | xfs_trans_log_buf(tp, bp, 0, sizeof(struct xfs_dsymlink_hdr) + |
199 | ifp->if_bytes - 1); | |
1fb7e48d | 200 | } |
0795e004 DW |
201 | |
202 | /* Verify the consistency of an inline symlink. */ | |
203 | xfs_failaddr_t | |
204 | xfs_symlink_shortform_verify( | |
205 | struct xfs_inode *ip) | |
206 | { | |
207 | char *sfp; | |
208 | char *endp; | |
209 | struct xfs_ifork *ifp; | |
210 | int size; | |
211 | ||
212 | ASSERT(ip->i_d.di_format == XFS_DINODE_FMT_LOCAL); | |
213 | ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK); | |
214 | sfp = (char *)ifp->if_u1.if_data; | |
215 | size = ifp->if_bytes; | |
216 | endp = sfp + size; | |
217 | ||
218 | /* Zero length symlinks can exist while we're deleting a remote one. */ | |
219 | if (size == 0) | |
220 | return NULL; | |
221 | ||
222 | /* No negative sizes or overly long symlink targets. */ | |
223 | if (size < 0 || size > XFS_SYMLINK_MAXLEN) | |
224 | return __this_address; | |
225 | ||
226 | /* No NULLs in the target either. */ | |
227 | if (memchr(sfp, 0, size - 1)) | |
228 | return __this_address; | |
229 | ||
230 | /* We /did/ null-terminate the buffer, right? */ | |
231 | if (*endp != 0) | |
232 | return __this_address; | |
233 | return NULL; | |
234 | } |