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