1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * The base64 encode/decode code was copied from fscrypt: |
4 | * Copyright (C) 2015, Google, Inc. |
5 | * Copyright (C) 2015, Motorola Mobility |
6 | * Written by Uday Savagaonkar, 2014. |
7 | * Modified by Jaegeuk Kim, 2015. |
8 | */ |
9 | #include <linux/ceph/ceph_debug.h> |
10 | #include <linux/xattr.h> |
11 | #include <linux/fscrypt.h> |
12 | #include <linux/ceph/striper.h> |
13 | |
14 | #include "super.h" |
15 | #include "mds_client.h" |
16 | #include "crypto.h" |
17 | |
18 | /* |
19 | * The base64url encoding used by fscrypt includes the '_' character, which may |
20 | * cause problems in snapshot names (which can not start with '_'). Thus, we |
21 | * used the base64 encoding defined for IMAP mailbox names (RFC 3501) instead, |
22 | * which replaces '-' and '_' by '+' and ','. |
23 | */ |
24 | static const char base64_table[65] = |
25 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+," ; |
26 | |
27 | int ceph_base64_encode(const u8 *src, int srclen, char *dst) |
28 | { |
29 | u32 ac = 0; |
30 | int bits = 0; |
31 | int i; |
32 | char *cp = dst; |
33 | |
34 | for (i = 0; i < srclen; i++) { |
35 | ac = (ac << 8) | src[i]; |
36 | bits += 8; |
37 | do { |
38 | bits -= 6; |
39 | *cp++ = base64_table[(ac >> bits) & 0x3f]; |
40 | } while (bits >= 6); |
41 | } |
42 | if (bits) |
43 | *cp++ = base64_table[(ac << (6 - bits)) & 0x3f]; |
44 | return cp - dst; |
45 | } |
46 | |
47 | int ceph_base64_decode(const char *src, int srclen, u8 *dst) |
48 | { |
49 | u32 ac = 0; |
50 | int bits = 0; |
51 | int i; |
52 | u8 *bp = dst; |
53 | |
54 | for (i = 0; i < srclen; i++) { |
55 | const char *p = strchr(base64_table, src[i]); |
56 | |
57 | if (p == NULL || src[i] == 0) |
58 | return -1; |
59 | ac = (ac << 6) | (p - base64_table); |
60 | bits += 6; |
61 | if (bits >= 8) { |
62 | bits -= 8; |
63 | *bp++ = (u8)(ac >> bits); |
64 | } |
65 | } |
66 | if (ac & ((1 << bits) - 1)) |
67 | return -1; |
68 | return bp - dst; |
69 | } |
70 | |
71 | static int ceph_crypt_get_context(struct inode *inode, void *ctx, size_t len) |
72 | { |
73 | struct ceph_inode_info *ci = ceph_inode(inode); |
74 | struct ceph_fscrypt_auth *cfa = (struct ceph_fscrypt_auth *)ci->fscrypt_auth; |
75 | u32 ctxlen; |
76 | |
77 | /* Non existent or too short? */ |
78 | if (!cfa || (ci->fscrypt_auth_len < (offsetof(struct ceph_fscrypt_auth, cfa_blob) + 1))) |
79 | return -ENOBUFS; |
80 | |
81 | /* Some format we don't recognize? */ |
82 | if (le32_to_cpu(cfa->cfa_version) != CEPH_FSCRYPT_AUTH_VERSION) |
83 | return -ENOBUFS; |
84 | |
85 | ctxlen = le32_to_cpu(cfa->cfa_blob_len); |
86 | if (len < ctxlen) |
87 | return -ERANGE; |
88 | |
89 | memcpy(ctx, cfa->cfa_blob, ctxlen); |
90 | return ctxlen; |
91 | } |
92 | |
93 | static int ceph_crypt_set_context(struct inode *inode, const void *ctx, |
94 | size_t len, void *fs_data) |
95 | { |
96 | int ret; |
97 | struct iattr attr = { }; |
98 | struct ceph_iattr cia = { }; |
99 | struct ceph_fscrypt_auth *cfa; |
100 | |
101 | WARN_ON_ONCE(fs_data); |
102 | |
103 | if (len > FSCRYPT_SET_CONTEXT_MAX_SIZE) |
104 | return -EINVAL; |
105 | |
106 | cfa = kzalloc(size: sizeof(*cfa), GFP_KERNEL); |
107 | if (!cfa) |
108 | return -ENOMEM; |
109 | |
110 | cfa->cfa_version = cpu_to_le32(CEPH_FSCRYPT_AUTH_VERSION); |
111 | cfa->cfa_blob_len = cpu_to_le32(len); |
112 | memcpy(cfa->cfa_blob, ctx, len); |
113 | |
114 | cia.fscrypt_auth = cfa; |
115 | |
116 | ret = __ceph_setattr(idmap: &nop_mnt_idmap, inode, attr: &attr, cia: &cia); |
117 | if (ret == 0) |
118 | inode_set_flags(inode, S_ENCRYPTED, S_ENCRYPTED); |
119 | kfree(objp: cia.fscrypt_auth); |
120 | return ret; |
121 | } |
122 | |
123 | static bool ceph_crypt_empty_dir(struct inode *inode) |
124 | { |
125 | struct ceph_inode_info *ci = ceph_inode(inode); |
126 | |
127 | return ci->i_rsubdirs + ci->i_rfiles == 1; |
128 | } |
129 | |
130 | static const union fscrypt_policy *ceph_get_dummy_policy(struct super_block *sb) |
131 | { |
132 | return ceph_sb_to_fs_client(sb)->fsc_dummy_enc_policy.policy; |
133 | } |
134 | |
135 | static struct fscrypt_operations ceph_fscrypt_ops = { |
136 | .needs_bounce_pages = 1, |
137 | .get_context = ceph_crypt_get_context, |
138 | .set_context = ceph_crypt_set_context, |
139 | .get_dummy_policy = ceph_get_dummy_policy, |
140 | .empty_dir = ceph_crypt_empty_dir, |
141 | }; |
142 | |
143 | void ceph_fscrypt_set_ops(struct super_block *sb) |
144 | { |
145 | fscrypt_set_ops(sb, s_cop: &ceph_fscrypt_ops); |
146 | } |
147 | |
148 | void ceph_fscrypt_free_dummy_policy(struct ceph_fs_client *fsc) |
149 | { |
150 | fscrypt_free_dummy_policy(dummy_policy: &fsc->fsc_dummy_enc_policy); |
151 | } |
152 | |
153 | int ceph_fscrypt_prepare_context(struct inode *dir, struct inode *inode, |
154 | struct ceph_acl_sec_ctx *as) |
155 | { |
156 | int ret, ctxsize; |
157 | bool encrypted = false; |
158 | struct ceph_inode_info *ci = ceph_inode(inode); |
159 | |
160 | ret = fscrypt_prepare_new_inode(dir, inode, encrypt_ret: &encrypted); |
161 | if (ret) |
162 | return ret; |
163 | if (!encrypted) |
164 | return 0; |
165 | |
166 | as->fscrypt_auth = kzalloc(size: sizeof(*as->fscrypt_auth), GFP_KERNEL); |
167 | if (!as->fscrypt_auth) |
168 | return -ENOMEM; |
169 | |
170 | ctxsize = fscrypt_context_for_new_inode(ctx: as->fscrypt_auth->cfa_blob, |
171 | inode); |
172 | if (ctxsize < 0) |
173 | return ctxsize; |
174 | |
175 | as->fscrypt_auth->cfa_version = cpu_to_le32(CEPH_FSCRYPT_AUTH_VERSION); |
176 | as->fscrypt_auth->cfa_blob_len = cpu_to_le32(ctxsize); |
177 | |
178 | WARN_ON_ONCE(ci->fscrypt_auth); |
179 | kfree(objp: ci->fscrypt_auth); |
180 | ci->fscrypt_auth_len = ceph_fscrypt_auth_len(fa: as->fscrypt_auth); |
181 | ci->fscrypt_auth = kmemdup(p: as->fscrypt_auth, size: ci->fscrypt_auth_len, |
182 | GFP_KERNEL); |
183 | if (!ci->fscrypt_auth) |
184 | return -ENOMEM; |
185 | |
186 | inode->i_flags |= S_ENCRYPTED; |
187 | |
188 | return 0; |
189 | } |
190 | |
191 | void ceph_fscrypt_as_ctx_to_req(struct ceph_mds_request *req, |
192 | struct ceph_acl_sec_ctx *as) |
193 | { |
194 | swap(req->r_fscrypt_auth, as->fscrypt_auth); |
195 | } |
196 | |
197 | /* |
198 | * User-created snapshots can't start with '_'. Snapshots that start with this |
199 | * character are special (hint: there aren't real snapshots) and use the |
200 | * following format: |
201 | * |
202 | * _<SNAPSHOT-NAME>_<INODE-NUMBER> |
203 | * |
204 | * where: |
205 | * - <SNAPSHOT-NAME> - the real snapshot name that may need to be decrypted, |
206 | * - <INODE-NUMBER> - the inode number (in decimal) for the actual snapshot |
207 | * |
208 | * This function parses these snapshot names and returns the inode |
209 | * <INODE-NUMBER>. 'name_len' will also bet set with the <SNAPSHOT-NAME> |
210 | * length. |
211 | */ |
212 | static struct inode *parse_longname(const struct inode *parent, |
213 | const char *name, int *name_len) |
214 | { |
215 | struct ceph_client *cl = ceph_inode_to_client(inode: parent); |
216 | struct inode *dir = NULL; |
217 | struct ceph_vino vino = { .snap = CEPH_NOSNAP }; |
218 | char *inode_number; |
219 | char *name_end; |
220 | int orig_len = *name_len; |
221 | int ret = -EIO; |
222 | |
223 | /* Skip initial '_' */ |
224 | name++; |
225 | name_end = strrchr(name, '_'); |
226 | if (!name_end) { |
227 | doutc(cl, "failed to parse long snapshot name: %s\n" , name); |
228 | return ERR_PTR(error: -EIO); |
229 | } |
230 | *name_len = (name_end - name); |
231 | if (*name_len <= 0) { |
232 | pr_err_client(cl, "failed to parse long snapshot name\n" ); |
233 | return ERR_PTR(error: -EIO); |
234 | } |
235 | |
236 | /* Get the inode number */ |
237 | inode_number = kmemdup_nul(s: name_end + 1, |
238 | len: orig_len - *name_len - 2, |
239 | GFP_KERNEL); |
240 | if (!inode_number) |
241 | return ERR_PTR(error: -ENOMEM); |
242 | ret = kstrtou64(s: inode_number, base: 10, res: &vino.ino); |
243 | if (ret) { |
244 | doutc(cl, "failed to parse inode number: %s\n" , name); |
245 | dir = ERR_PTR(error: ret); |
246 | goto out; |
247 | } |
248 | |
249 | /* And finally the inode */ |
250 | dir = ceph_find_inode(sb: parent->i_sb, vino); |
251 | if (!dir) { |
252 | /* This can happen if we're not mounting cephfs on the root */ |
253 | dir = ceph_get_inode(sb: parent->i_sb, vino, NULL); |
254 | if (IS_ERR(ptr: dir)) |
255 | doutc(cl, "can't find inode %s (%s)\n" , inode_number, name); |
256 | } |
257 | |
258 | out: |
259 | kfree(objp: inode_number); |
260 | return dir; |
261 | } |
262 | |
263 | int ceph_encode_encrypted_dname(struct inode *parent, struct qstr *d_name, |
264 | char *buf) |
265 | { |
266 | struct ceph_client *cl = ceph_inode_to_client(inode: parent); |
267 | struct inode *dir = parent; |
268 | struct qstr iname; |
269 | u32 len; |
270 | int name_len; |
271 | int elen; |
272 | int ret; |
273 | u8 *cryptbuf = NULL; |
274 | |
275 | iname.name = d_name->name; |
276 | name_len = d_name->len; |
277 | |
278 | /* Handle the special case of snapshot names that start with '_' */ |
279 | if ((ceph_snap(inode: dir) == CEPH_SNAPDIR) && (name_len > 0) && |
280 | (iname.name[0] == '_')) { |
281 | dir = parse_longname(parent, name: iname.name, name_len: &name_len); |
282 | if (IS_ERR(ptr: dir)) |
283 | return PTR_ERR(ptr: dir); |
284 | iname.name++; /* skip initial '_' */ |
285 | } |
286 | iname.len = name_len; |
287 | |
288 | if (!fscrypt_has_encryption_key(inode: dir)) { |
289 | memcpy(buf, d_name->name, d_name->len); |
290 | elen = d_name->len; |
291 | goto out; |
292 | } |
293 | |
294 | /* |
295 | * Convert cleartext d_name to ciphertext. If result is longer than |
296 | * CEPH_NOHASH_NAME_MAX, sha256 the remaining bytes |
297 | * |
298 | * See: fscrypt_setup_filename |
299 | */ |
300 | if (!fscrypt_fname_encrypted_size(inode: dir, orig_len: iname.len, NAME_MAX, encrypted_len_ret: &len)) { |
301 | elen = -ENAMETOOLONG; |
302 | goto out; |
303 | } |
304 | |
305 | /* Allocate a buffer appropriate to hold the result */ |
306 | cryptbuf = kmalloc(size: len > CEPH_NOHASH_NAME_MAX ? NAME_MAX : len, |
307 | GFP_KERNEL); |
308 | if (!cryptbuf) { |
309 | elen = -ENOMEM; |
310 | goto out; |
311 | } |
312 | |
313 | ret = fscrypt_fname_encrypt(inode: dir, iname: &iname, out: cryptbuf, olen: len); |
314 | if (ret) { |
315 | elen = ret; |
316 | goto out; |
317 | } |
318 | |
319 | /* hash the end if the name is long enough */ |
320 | if (len > CEPH_NOHASH_NAME_MAX) { |
321 | u8 hash[SHA256_DIGEST_SIZE]; |
322 | u8 * = cryptbuf + CEPH_NOHASH_NAME_MAX; |
323 | |
324 | /* |
325 | * hash the extra bytes and overwrite crypttext beyond that |
326 | * point with it |
327 | */ |
328 | sha256(data: extra, len: len - CEPH_NOHASH_NAME_MAX, out: hash); |
329 | memcpy(extra, hash, SHA256_DIGEST_SIZE); |
330 | len = CEPH_NOHASH_NAME_MAX + SHA256_DIGEST_SIZE; |
331 | } |
332 | |
333 | /* base64 encode the encrypted name */ |
334 | elen = ceph_base64_encode(src: cryptbuf, srclen: len, dst: buf); |
335 | doutc(cl, "base64-encoded ciphertext name = %.*s\n" , elen, buf); |
336 | |
337 | /* To understand the 240 limit, see CEPH_NOHASH_NAME_MAX comments */ |
338 | WARN_ON(elen > 240); |
339 | if ((elen > 0) && (dir != parent)) { |
340 | char tmp_buf[NAME_MAX]; |
341 | |
342 | elen = snprintf(buf: tmp_buf, size: sizeof(tmp_buf), fmt: "_%.*s_%ld" , |
343 | elen, buf, dir->i_ino); |
344 | memcpy(buf, tmp_buf, elen); |
345 | } |
346 | |
347 | out: |
348 | kfree(objp: cryptbuf); |
349 | if (dir != parent) { |
350 | if ((dir->i_state & I_NEW)) |
351 | discard_new_inode(dir); |
352 | else |
353 | iput(dir); |
354 | } |
355 | return elen; |
356 | } |
357 | |
358 | int ceph_encode_encrypted_fname(struct inode *parent, struct dentry *dentry, |
359 | char *buf) |
360 | { |
361 | WARN_ON_ONCE(!fscrypt_has_encryption_key(parent)); |
362 | |
363 | return ceph_encode_encrypted_dname(parent, d_name: &dentry->d_name, buf); |
364 | } |
365 | |
366 | /** |
367 | * ceph_fname_to_usr - convert a filename for userland presentation |
368 | * @fname: ceph_fname to be converted |
369 | * @tname: temporary name buffer to use for conversion (may be NULL) |
370 | * @oname: where converted name should be placed |
371 | * @is_nokey: set to true if key wasn't available during conversion (may be NULL) |
372 | * |
373 | * Given a filename (usually from the MDS), format it for presentation to |
374 | * userland. If @parent is not encrypted, just pass it back as-is. |
375 | * |
376 | * Otherwise, base64 decode the string, and then ask fscrypt to format it |
377 | * for userland presentation. |
378 | * |
379 | * Returns 0 on success or negative error code on error. |
380 | */ |
381 | int ceph_fname_to_usr(const struct ceph_fname *fname, struct fscrypt_str *tname, |
382 | struct fscrypt_str *oname, bool *is_nokey) |
383 | { |
384 | struct inode *dir = fname->dir; |
385 | struct fscrypt_str _tname = FSTR_INIT(NULL, 0); |
386 | struct fscrypt_str iname; |
387 | char *name = fname->name; |
388 | int name_len = fname->name_len; |
389 | int ret; |
390 | |
391 | /* Sanity check that the resulting name will fit in the buffer */ |
392 | if (fname->name_len > NAME_MAX || fname->ctext_len > NAME_MAX) |
393 | return -EIO; |
394 | |
395 | /* Handle the special case of snapshot names that start with '_' */ |
396 | if ((ceph_snap(inode: dir) == CEPH_SNAPDIR) && (name_len > 0) && |
397 | (name[0] == '_')) { |
398 | dir = parse_longname(parent: dir, name, name_len: &name_len); |
399 | if (IS_ERR(ptr: dir)) |
400 | return PTR_ERR(ptr: dir); |
401 | name++; /* skip initial '_' */ |
402 | } |
403 | |
404 | if (!IS_ENCRYPTED(dir)) { |
405 | oname->name = fname->name; |
406 | oname->len = fname->name_len; |
407 | ret = 0; |
408 | goto out_inode; |
409 | } |
410 | |
411 | ret = ceph_fscrypt_prepare_readdir(dir); |
412 | if (ret) |
413 | goto out_inode; |
414 | |
415 | /* |
416 | * Use the raw dentry name as sent by the MDS instead of |
417 | * generating a nokey name via fscrypt. |
418 | */ |
419 | if (!fscrypt_has_encryption_key(inode: dir)) { |
420 | if (fname->no_copy) |
421 | oname->name = fname->name; |
422 | else |
423 | memcpy(oname->name, fname->name, fname->name_len); |
424 | oname->len = fname->name_len; |
425 | if (is_nokey) |
426 | *is_nokey = true; |
427 | ret = 0; |
428 | goto out_inode; |
429 | } |
430 | |
431 | if (fname->ctext_len == 0) { |
432 | int declen; |
433 | |
434 | if (!tname) { |
435 | ret = fscrypt_fname_alloc_buffer(NAME_MAX, crypto_str: &_tname); |
436 | if (ret) |
437 | goto out_inode; |
438 | tname = &_tname; |
439 | } |
440 | |
441 | declen = ceph_base64_decode(src: name, srclen: name_len, dst: tname->name); |
442 | if (declen <= 0) { |
443 | ret = -EIO; |
444 | goto out; |
445 | } |
446 | iname.name = tname->name; |
447 | iname.len = declen; |
448 | } else { |
449 | iname.name = fname->ctext; |
450 | iname.len = fname->ctext_len; |
451 | } |
452 | |
453 | ret = fscrypt_fname_disk_to_usr(inode: dir, hash: 0, minor_hash: 0, iname: &iname, oname); |
454 | if (!ret && (dir != fname->dir)) { |
455 | char tmp_buf[CEPH_BASE64_CHARS(NAME_MAX)]; |
456 | |
457 | name_len = snprintf(buf: tmp_buf, size: sizeof(tmp_buf), fmt: "_%.*s_%ld" , |
458 | oname->len, oname->name, dir->i_ino); |
459 | memcpy(oname->name, tmp_buf, name_len); |
460 | oname->len = name_len; |
461 | } |
462 | |
463 | out: |
464 | fscrypt_fname_free_buffer(crypto_str: &_tname); |
465 | out_inode: |
466 | if (dir != fname->dir) { |
467 | if ((dir->i_state & I_NEW)) |
468 | discard_new_inode(dir); |
469 | else |
470 | iput(dir); |
471 | } |
472 | return ret; |
473 | } |
474 | |
475 | /** |
476 | * ceph_fscrypt_prepare_readdir - simple __fscrypt_prepare_readdir() wrapper |
477 | * @dir: directory inode for readdir prep |
478 | * |
479 | * Simple wrapper around __fscrypt_prepare_readdir() that will mark directory as |
480 | * non-complete if this call results in having the directory unlocked. |
481 | * |
482 | * Returns: |
483 | * 1 - if directory was locked and key is now loaded (i.e. dir is unlocked) |
484 | * 0 - if directory is still locked |
485 | * < 0 - if __fscrypt_prepare_readdir() fails |
486 | */ |
487 | int ceph_fscrypt_prepare_readdir(struct inode *dir) |
488 | { |
489 | bool had_key = fscrypt_has_encryption_key(inode: dir); |
490 | int err; |
491 | |
492 | if (!IS_ENCRYPTED(dir)) |
493 | return 0; |
494 | |
495 | err = __fscrypt_prepare_readdir(dir); |
496 | if (err) |
497 | return err; |
498 | if (!had_key && fscrypt_has_encryption_key(inode: dir)) { |
499 | /* directory just got unlocked, mark it as not complete */ |
500 | ceph_dir_clear_complete(inode: dir); |
501 | return 1; |
502 | } |
503 | return 0; |
504 | } |
505 | |
506 | int ceph_fscrypt_decrypt_block_inplace(const struct inode *inode, |
507 | struct page *page, unsigned int len, |
508 | unsigned int offs, u64 lblk_num) |
509 | { |
510 | struct ceph_client *cl = ceph_inode_to_client(inode); |
511 | |
512 | doutc(cl, "%p %llx.%llx len %u offs %u blk %llu\n" , inode, |
513 | ceph_vinop(inode), len, offs, lblk_num); |
514 | return fscrypt_decrypt_block_inplace(inode, page, len, offs, lblk_num); |
515 | } |
516 | |
517 | int ceph_fscrypt_encrypt_block_inplace(const struct inode *inode, |
518 | struct page *page, unsigned int len, |
519 | unsigned int offs, u64 lblk_num, |
520 | gfp_t gfp_flags) |
521 | { |
522 | struct ceph_client *cl = ceph_inode_to_client(inode); |
523 | |
524 | doutc(cl, "%p %llx.%llx len %u offs %u blk %llu\n" , inode, |
525 | ceph_vinop(inode), len, offs, lblk_num); |
526 | return fscrypt_encrypt_block_inplace(inode, page, len, offs, lblk_num, |
527 | gfp_flags); |
528 | } |
529 | |
530 | /** |
531 | * ceph_fscrypt_decrypt_pages - decrypt an array of pages |
532 | * @inode: pointer to inode associated with these pages |
533 | * @page: pointer to page array |
534 | * @off: offset into the file that the read data starts |
535 | * @len: max length to decrypt |
536 | * |
537 | * Decrypt an array of fscrypt'ed pages and return the amount of |
538 | * data decrypted. Any data in the page prior to the start of the |
539 | * first complete block in the read is ignored. Any incomplete |
540 | * crypto blocks at the end of the array are ignored (and should |
541 | * probably be zeroed by the caller). |
542 | * |
543 | * Returns the length of the decrypted data or a negative errno. |
544 | */ |
545 | int ceph_fscrypt_decrypt_pages(struct inode *inode, struct page **page, |
546 | u64 off, int len) |
547 | { |
548 | int i, num_blocks; |
549 | u64 baseblk = off >> CEPH_FSCRYPT_BLOCK_SHIFT; |
550 | int ret = 0; |
551 | |
552 | /* |
553 | * We can't deal with partial blocks on an encrypted file, so mask off |
554 | * the last bit. |
555 | */ |
556 | num_blocks = ceph_fscrypt_blocks(off, len: len & CEPH_FSCRYPT_BLOCK_MASK); |
557 | |
558 | /* Decrypt each block */ |
559 | for (i = 0; i < num_blocks; ++i) { |
560 | int blkoff = i << CEPH_FSCRYPT_BLOCK_SHIFT; |
561 | int pgidx = blkoff >> PAGE_SHIFT; |
562 | unsigned int pgoffs = offset_in_page(blkoff); |
563 | int fret; |
564 | |
565 | fret = ceph_fscrypt_decrypt_block_inplace(inode, page: page[pgidx], |
566 | CEPH_FSCRYPT_BLOCK_SIZE, offs: pgoffs, |
567 | lblk_num: baseblk + i); |
568 | if (fret < 0) { |
569 | if (ret == 0) |
570 | ret = fret; |
571 | break; |
572 | } |
573 | ret += CEPH_FSCRYPT_BLOCK_SIZE; |
574 | } |
575 | return ret; |
576 | } |
577 | |
578 | /** |
579 | * ceph_fscrypt_decrypt_extents: decrypt received extents in given buffer |
580 | * @inode: inode associated with pages being decrypted |
581 | * @page: pointer to page array |
582 | * @off: offset into the file that the data in page[0] starts |
583 | * @map: pointer to extent array |
584 | * @ext_cnt: length of extent array |
585 | * |
586 | * Given an extent map and a page array, decrypt the received data in-place, |
587 | * skipping holes. Returns the offset into buffer of end of last decrypted |
588 | * block. |
589 | */ |
590 | int ceph_fscrypt_decrypt_extents(struct inode *inode, struct page **page, |
591 | u64 off, struct ceph_sparse_extent *map, |
592 | u32 ext_cnt) |
593 | { |
594 | struct ceph_client *cl = ceph_inode_to_client(inode); |
595 | int i, ret = 0; |
596 | struct ceph_inode_info *ci = ceph_inode(inode); |
597 | u64 objno, objoff; |
598 | u32 xlen; |
599 | |
600 | /* Nothing to do for empty array */ |
601 | if (ext_cnt == 0) { |
602 | doutc(cl, "%p %llx.%llx empty array, ret 0\n" , inode, |
603 | ceph_vinop(inode)); |
604 | return 0; |
605 | } |
606 | |
607 | ceph_calc_file_object_mapping(l: &ci->i_layout, off, len: map[0].len, |
608 | objno: &objno, objoff: &objoff, xlen: &xlen); |
609 | |
610 | for (i = 0; i < ext_cnt; ++i) { |
611 | struct ceph_sparse_extent *ext = &map[i]; |
612 | int pgsoff = ext->off - objoff; |
613 | int pgidx = pgsoff >> PAGE_SHIFT; |
614 | int fret; |
615 | |
616 | if ((ext->off | ext->len) & ~CEPH_FSCRYPT_BLOCK_MASK) { |
617 | pr_warn_client(cl, |
618 | "%p %llx.%llx bad encrypted sparse extent " |
619 | "idx %d off %llx len %llx\n" , |
620 | inode, ceph_vinop(inode), i, ext->off, |
621 | ext->len); |
622 | return -EIO; |
623 | } |
624 | fret = ceph_fscrypt_decrypt_pages(inode, page: &page[pgidx], |
625 | off: off + pgsoff, len: ext->len); |
626 | doutc(cl, "%p %llx.%llx [%d] 0x%llx~0x%llx fret %d\n" , inode, |
627 | ceph_vinop(inode), i, ext->off, ext->len, fret); |
628 | if (fret < 0) { |
629 | if (ret == 0) |
630 | ret = fret; |
631 | break; |
632 | } |
633 | ret = pgsoff + fret; |
634 | } |
635 | doutc(cl, "ret %d\n" , ret); |
636 | return ret; |
637 | } |
638 | |
639 | /** |
640 | * ceph_fscrypt_encrypt_pages - encrypt an array of pages |
641 | * @inode: pointer to inode associated with these pages |
642 | * @page: pointer to page array |
643 | * @off: offset into the file that the data starts |
644 | * @len: max length to encrypt |
645 | * @gfp: gfp flags to use for allocation |
646 | * |
647 | * Decrypt an array of cleartext pages and return the amount of |
648 | * data encrypted. Any data in the page prior to the start of the |
649 | * first complete block in the read is ignored. Any incomplete |
650 | * crypto blocks at the end of the array are ignored. |
651 | * |
652 | * Returns the length of the encrypted data or a negative errno. |
653 | */ |
654 | int ceph_fscrypt_encrypt_pages(struct inode *inode, struct page **page, u64 off, |
655 | int len, gfp_t gfp) |
656 | { |
657 | int i, num_blocks; |
658 | u64 baseblk = off >> CEPH_FSCRYPT_BLOCK_SHIFT; |
659 | int ret = 0; |
660 | |
661 | /* |
662 | * We can't deal with partial blocks on an encrypted file, so mask off |
663 | * the last bit. |
664 | */ |
665 | num_blocks = ceph_fscrypt_blocks(off, len: len & CEPH_FSCRYPT_BLOCK_MASK); |
666 | |
667 | /* Encrypt each block */ |
668 | for (i = 0; i < num_blocks; ++i) { |
669 | int blkoff = i << CEPH_FSCRYPT_BLOCK_SHIFT; |
670 | int pgidx = blkoff >> PAGE_SHIFT; |
671 | unsigned int pgoffs = offset_in_page(blkoff); |
672 | int fret; |
673 | |
674 | fret = ceph_fscrypt_encrypt_block_inplace(inode, page: page[pgidx], |
675 | CEPH_FSCRYPT_BLOCK_SIZE, offs: pgoffs, |
676 | lblk_num: baseblk + i, gfp_flags: gfp); |
677 | if (fret < 0) { |
678 | if (ret == 0) |
679 | ret = fret; |
680 | break; |
681 | } |
682 | ret += CEPH_FSCRYPT_BLOCK_SIZE; |
683 | } |
684 | return ret; |
685 | } |
686 | |