|
16 | 16 | #include "xfs_trans.h" |
17 | 17 | #include "xfs_buf_item.h" |
18 | 18 | #include "xfs_log.h" |
19 | | - |
| 19 | +#include "xfs_symlink_remote.h" |
| 20 | +#include "xfs_bit.h" |
| 21 | +#include "xfs_bmap.h" |
| 22 | +#include "xfs_health.h" |
20 | 23 |
|
21 | 24 | /* |
22 | 25 | * Each contiguous block has a header, so it is not just a simple pathlen |
@@ -227,3 +230,153 @@ xfs_symlink_shortform_verify( |
227 | 230 | return __this_address; |
228 | 231 | return NULL; |
229 | 232 | } |
| 233 | + |
| 234 | +/* Read a remote symlink target into the buffer. */ |
| 235 | +int |
| 236 | +xfs_symlink_remote_read( |
| 237 | + struct xfs_inode *ip, |
| 238 | + char *link) |
| 239 | +{ |
| 240 | + struct xfs_mount *mp = ip->i_mount; |
| 241 | + struct xfs_bmbt_irec mval[XFS_SYMLINK_MAPS]; |
| 242 | + struct xfs_buf *bp; |
| 243 | + xfs_daddr_t d; |
| 244 | + char *cur_chunk; |
| 245 | + int pathlen = ip->i_disk_size; |
| 246 | + int nmaps = XFS_SYMLINK_MAPS; |
| 247 | + int byte_cnt; |
| 248 | + int n; |
| 249 | + int error = 0; |
| 250 | + int fsblocks = 0; |
| 251 | + int offset; |
| 252 | + |
| 253 | + xfs_assert_ilocked(ip, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL); |
| 254 | + |
| 255 | + fsblocks = xfs_symlink_blocks(mp, pathlen); |
| 256 | + error = xfs_bmapi_read(ip, 0, fsblocks, mval, &nmaps, 0); |
| 257 | + if (error) |
| 258 | + goto out; |
| 259 | + |
| 260 | + offset = 0; |
| 261 | + for (n = 0; n < nmaps; n++) { |
| 262 | + d = XFS_FSB_TO_DADDR(mp, mval[n].br_startblock); |
| 263 | + byte_cnt = XFS_FSB_TO_B(mp, mval[n].br_blockcount); |
| 264 | + |
| 265 | + error = xfs_buf_read(mp->m_ddev_targp, d, BTOBB(byte_cnt), 0, |
| 266 | + &bp, &xfs_symlink_buf_ops); |
| 267 | + if (xfs_metadata_is_sick(error)) |
| 268 | + xfs_inode_mark_sick(ip, XFS_SICK_INO_SYMLINK); |
| 269 | + if (error) |
| 270 | + return error; |
| 271 | + byte_cnt = XFS_SYMLINK_BUF_SPACE(mp, byte_cnt); |
| 272 | + if (pathlen < byte_cnt) |
| 273 | + byte_cnt = pathlen; |
| 274 | + |
| 275 | + cur_chunk = bp->b_addr; |
| 276 | + if (xfs_has_crc(mp)) { |
| 277 | + if (!xfs_symlink_hdr_ok(ip->i_ino, offset, |
| 278 | + byte_cnt, bp)) { |
| 279 | + xfs_inode_mark_sick(ip, XFS_SICK_INO_SYMLINK); |
| 280 | + error = -EFSCORRUPTED; |
| 281 | + xfs_alert(mp, |
| 282 | +"symlink header does not match required off/len/owner (0x%x/0x%x,0x%llx)", |
| 283 | + offset, byte_cnt, ip->i_ino); |
| 284 | + xfs_buf_relse(bp); |
| 285 | + goto out; |
| 286 | + |
| 287 | + } |
| 288 | + |
| 289 | + cur_chunk += sizeof(struct xfs_dsymlink_hdr); |
| 290 | + } |
| 291 | + |
| 292 | + memcpy(link + offset, cur_chunk, byte_cnt); |
| 293 | + |
| 294 | + pathlen -= byte_cnt; |
| 295 | + offset += byte_cnt; |
| 296 | + |
| 297 | + xfs_buf_relse(bp); |
| 298 | + } |
| 299 | + ASSERT(pathlen == 0); |
| 300 | + |
| 301 | + link[ip->i_disk_size] = '\0'; |
| 302 | + error = 0; |
| 303 | + |
| 304 | + out: |
| 305 | + return error; |
| 306 | +} |
| 307 | + |
| 308 | +/* Write the symlink target into the inode. */ |
| 309 | +int |
| 310 | +xfs_symlink_write_target( |
| 311 | + struct xfs_trans *tp, |
| 312 | + struct xfs_inode *ip, |
| 313 | + const char *target_path, |
| 314 | + int pathlen, |
| 315 | + xfs_fsblock_t fs_blocks, |
| 316 | + uint resblks) |
| 317 | +{ |
| 318 | + struct xfs_bmbt_irec mval[XFS_SYMLINK_MAPS]; |
| 319 | + struct xfs_mount *mp = tp->t_mountp; |
| 320 | + const char *cur_chunk; |
| 321 | + struct xfs_buf *bp; |
| 322 | + xfs_daddr_t d; |
| 323 | + int byte_cnt; |
| 324 | + int nmaps; |
| 325 | + int offset = 0; |
| 326 | + int n; |
| 327 | + int error; |
| 328 | + |
| 329 | + /* |
| 330 | + * If the symlink will fit into the inode, write it inline. |
| 331 | + */ |
| 332 | + if (pathlen <= xfs_inode_data_fork_size(ip)) { |
| 333 | + xfs_init_local_fork(ip, XFS_DATA_FORK, target_path, pathlen); |
| 334 | + |
| 335 | + ip->i_disk_size = pathlen; |
| 336 | + ip->i_df.if_format = XFS_DINODE_FMT_LOCAL; |
| 337 | + xfs_trans_log_inode(tp, ip, XFS_ILOG_DDATA | XFS_ILOG_CORE); |
| 338 | + return 0; |
| 339 | + } |
| 340 | + |
| 341 | + nmaps = XFS_SYMLINK_MAPS; |
| 342 | + error = xfs_bmapi_write(tp, ip, 0, fs_blocks, XFS_BMAPI_METADATA, |
| 343 | + resblks, mval, &nmaps); |
| 344 | + if (error) |
| 345 | + return error; |
| 346 | + |
| 347 | + ip->i_disk_size = pathlen; |
| 348 | + xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); |
| 349 | + |
| 350 | + cur_chunk = target_path; |
| 351 | + offset = 0; |
| 352 | + for (n = 0; n < nmaps; n++) { |
| 353 | + char *buf; |
| 354 | + |
| 355 | + d = XFS_FSB_TO_DADDR(mp, mval[n].br_startblock); |
| 356 | + byte_cnt = XFS_FSB_TO_B(mp, mval[n].br_blockcount); |
| 357 | + error = xfs_trans_get_buf(tp, mp->m_ddev_targp, d, |
| 358 | + BTOBB(byte_cnt), 0, &bp); |
| 359 | + if (error) |
| 360 | + return error; |
| 361 | + bp->b_ops = &xfs_symlink_buf_ops; |
| 362 | + |
| 363 | + byte_cnt = XFS_SYMLINK_BUF_SPACE(mp, byte_cnt); |
| 364 | + byte_cnt = min(byte_cnt, pathlen); |
| 365 | + |
| 366 | + buf = bp->b_addr; |
| 367 | + buf += xfs_symlink_hdr_set(mp, ip->i_ino, offset, byte_cnt, |
| 368 | + bp); |
| 369 | + |
| 370 | + memcpy(buf, cur_chunk, byte_cnt); |
| 371 | + |
| 372 | + cur_chunk += byte_cnt; |
| 373 | + pathlen -= byte_cnt; |
| 374 | + offset += byte_cnt; |
| 375 | + |
| 376 | + xfs_trans_buf_set_type(tp, bp, XFS_BLFT_SYMLINK_BUF); |
| 377 | + xfs_trans_log_buf(tp, bp, 0, (buf + byte_cnt - 1) - |
| 378 | + (char *)bp->b_addr); |
| 379 | + } |
| 380 | + ASSERT(pathlen == 0); |
| 381 | + return 0; |
| 382 | +} |
0 commit comments