|
9 | 9 | #include <linux/ceph/ceph_debug.h> |
10 | 10 | #include <linux/xattr.h> |
11 | 11 | #include <linux/fscrypt.h> |
| 12 | +#include <linux/ceph/striper.h> |
12 | 13 |
|
13 | 14 | #include "super.h" |
14 | 15 | #include "mds_client.h" |
@@ -365,3 +366,173 @@ int ceph_fscrypt_prepare_readdir(struct inode *dir) |
365 | 366 | } |
366 | 367 | return 0; |
367 | 368 | } |
| 369 | + |
| 370 | +int ceph_fscrypt_decrypt_block_inplace(const struct inode *inode, |
| 371 | + struct page *page, unsigned int len, |
| 372 | + unsigned int offs, u64 lblk_num) |
| 373 | +{ |
| 374 | + dout("%s: len %u offs %u blk %llu\n", __func__, len, offs, lblk_num); |
| 375 | + return fscrypt_decrypt_block_inplace(inode, page, len, offs, lblk_num); |
| 376 | +} |
| 377 | + |
| 378 | +int ceph_fscrypt_encrypt_block_inplace(const struct inode *inode, |
| 379 | + struct page *page, unsigned int len, |
| 380 | + unsigned int offs, u64 lblk_num, |
| 381 | + gfp_t gfp_flags) |
| 382 | +{ |
| 383 | + dout("%s: len %u offs %u blk %llu\n", __func__, len, offs, lblk_num); |
| 384 | + return fscrypt_encrypt_block_inplace(inode, page, len, offs, lblk_num, |
| 385 | + gfp_flags); |
| 386 | +} |
| 387 | + |
| 388 | +/** |
| 389 | + * ceph_fscrypt_decrypt_pages - decrypt an array of pages |
| 390 | + * @inode: pointer to inode associated with these pages |
| 391 | + * @page: pointer to page array |
| 392 | + * @off: offset into the file that the read data starts |
| 393 | + * @len: max length to decrypt |
| 394 | + * |
| 395 | + * Decrypt an array of fscrypt'ed pages and return the amount of |
| 396 | + * data decrypted. Any data in the page prior to the start of the |
| 397 | + * first complete block in the read is ignored. Any incomplete |
| 398 | + * crypto blocks at the end of the array are ignored (and should |
| 399 | + * probably be zeroed by the caller). |
| 400 | + * |
| 401 | + * Returns the length of the decrypted data or a negative errno. |
| 402 | + */ |
| 403 | +int ceph_fscrypt_decrypt_pages(struct inode *inode, struct page **page, |
| 404 | + u64 off, int len) |
| 405 | +{ |
| 406 | + int i, num_blocks; |
| 407 | + u64 baseblk = off >> CEPH_FSCRYPT_BLOCK_SHIFT; |
| 408 | + int ret = 0; |
| 409 | + |
| 410 | + /* |
| 411 | + * We can't deal with partial blocks on an encrypted file, so mask off |
| 412 | + * the last bit. |
| 413 | + */ |
| 414 | + num_blocks = ceph_fscrypt_blocks(off, len & CEPH_FSCRYPT_BLOCK_MASK); |
| 415 | + |
| 416 | + /* Decrypt each block */ |
| 417 | + for (i = 0; i < num_blocks; ++i) { |
| 418 | + int blkoff = i << CEPH_FSCRYPT_BLOCK_SHIFT; |
| 419 | + int pgidx = blkoff >> PAGE_SHIFT; |
| 420 | + unsigned int pgoffs = offset_in_page(blkoff); |
| 421 | + int fret; |
| 422 | + |
| 423 | + fret = ceph_fscrypt_decrypt_block_inplace(inode, page[pgidx], |
| 424 | + CEPH_FSCRYPT_BLOCK_SIZE, pgoffs, |
| 425 | + baseblk + i); |
| 426 | + if (fret < 0) { |
| 427 | + if (ret == 0) |
| 428 | + ret = fret; |
| 429 | + break; |
| 430 | + } |
| 431 | + ret += CEPH_FSCRYPT_BLOCK_SIZE; |
| 432 | + } |
| 433 | + return ret; |
| 434 | +} |
| 435 | + |
| 436 | +/** |
| 437 | + * ceph_fscrypt_decrypt_extents: decrypt received extents in given buffer |
| 438 | + * @inode: inode associated with pages being decrypted |
| 439 | + * @page: pointer to page array |
| 440 | + * @off: offset into the file that the data in page[0] starts |
| 441 | + * @map: pointer to extent array |
| 442 | + * @ext_cnt: length of extent array |
| 443 | + * |
| 444 | + * Given an extent map and a page array, decrypt the received data in-place, |
| 445 | + * skipping holes. Returns the offset into buffer of end of last decrypted |
| 446 | + * block. |
| 447 | + */ |
| 448 | +int ceph_fscrypt_decrypt_extents(struct inode *inode, struct page **page, |
| 449 | + u64 off, struct ceph_sparse_extent *map, |
| 450 | + u32 ext_cnt) |
| 451 | +{ |
| 452 | + int i, ret = 0; |
| 453 | + struct ceph_inode_info *ci = ceph_inode(inode); |
| 454 | + u64 objno, objoff; |
| 455 | + u32 xlen; |
| 456 | + |
| 457 | + /* Nothing to do for empty array */ |
| 458 | + if (ext_cnt == 0) { |
| 459 | + dout("%s: empty array, ret 0\n", __func__); |
| 460 | + return 0; |
| 461 | + } |
| 462 | + |
| 463 | + ceph_calc_file_object_mapping(&ci->i_layout, off, map[0].len, |
| 464 | + &objno, &objoff, &xlen); |
| 465 | + |
| 466 | + for (i = 0; i < ext_cnt; ++i) { |
| 467 | + struct ceph_sparse_extent *ext = &map[i]; |
| 468 | + int pgsoff = ext->off - objoff; |
| 469 | + int pgidx = pgsoff >> PAGE_SHIFT; |
| 470 | + int fret; |
| 471 | + |
| 472 | + if ((ext->off | ext->len) & ~CEPH_FSCRYPT_BLOCK_MASK) { |
| 473 | + pr_warn("%s: bad encrypted sparse extent idx %d off %llx len %llx\n", |
| 474 | + __func__, i, ext->off, ext->len); |
| 475 | + return -EIO; |
| 476 | + } |
| 477 | + fret = ceph_fscrypt_decrypt_pages(inode, &page[pgidx], |
| 478 | + off + pgsoff, ext->len); |
| 479 | + dout("%s: [%d] 0x%llx~0x%llx fret %d\n", __func__, i, |
| 480 | + ext->off, ext->len, fret); |
| 481 | + if (fret < 0) { |
| 482 | + if (ret == 0) |
| 483 | + ret = fret; |
| 484 | + break; |
| 485 | + } |
| 486 | + ret = pgsoff + fret; |
| 487 | + } |
| 488 | + dout("%s: ret %d\n", __func__, ret); |
| 489 | + return ret; |
| 490 | +} |
| 491 | + |
| 492 | +/** |
| 493 | + * ceph_fscrypt_encrypt_pages - encrypt an array of pages |
| 494 | + * @inode: pointer to inode associated with these pages |
| 495 | + * @page: pointer to page array |
| 496 | + * @off: offset into the file that the data starts |
| 497 | + * @len: max length to encrypt |
| 498 | + * @gfp: gfp flags to use for allocation |
| 499 | + * |
| 500 | + * Decrypt an array of cleartext pages and return the amount of |
| 501 | + * data encrypted. Any data in the page prior to the start of the |
| 502 | + * first complete block in the read is ignored. Any incomplete |
| 503 | + * crypto blocks at the end of the array are ignored. |
| 504 | + * |
| 505 | + * Returns the length of the encrypted data or a negative errno. |
| 506 | + */ |
| 507 | +int ceph_fscrypt_encrypt_pages(struct inode *inode, struct page **page, u64 off, |
| 508 | + int len, gfp_t gfp) |
| 509 | +{ |
| 510 | + int i, num_blocks; |
| 511 | + u64 baseblk = off >> CEPH_FSCRYPT_BLOCK_SHIFT; |
| 512 | + int ret = 0; |
| 513 | + |
| 514 | + /* |
| 515 | + * We can't deal with partial blocks on an encrypted file, so mask off |
| 516 | + * the last bit. |
| 517 | + */ |
| 518 | + num_blocks = ceph_fscrypt_blocks(off, len & CEPH_FSCRYPT_BLOCK_MASK); |
| 519 | + |
| 520 | + /* Encrypt each block */ |
| 521 | + for (i = 0; i < num_blocks; ++i) { |
| 522 | + int blkoff = i << CEPH_FSCRYPT_BLOCK_SHIFT; |
| 523 | + int pgidx = blkoff >> PAGE_SHIFT; |
| 524 | + unsigned int pgoffs = offset_in_page(blkoff); |
| 525 | + int fret; |
| 526 | + |
| 527 | + fret = ceph_fscrypt_encrypt_block_inplace(inode, page[pgidx], |
| 528 | + CEPH_FSCRYPT_BLOCK_SIZE, pgoffs, |
| 529 | + baseblk + i, gfp); |
| 530 | + if (fret < 0) { |
| 531 | + if (ret == 0) |
| 532 | + ret = fret; |
| 533 | + break; |
| 534 | + } |
| 535 | + ret += CEPH_FSCRYPT_BLOCK_SIZE; |
| 536 | + } |
| 537 | + return ret; |
| 538 | +} |
0 commit comments