/* * The base64url encoding used by fscrypt includes the '_' character, which may * cause problems in snapshot names (which can not start with '_'). Thus, we * used the base64 encoding defined for IMAP mailbox names (RFC 3501) instead, * which replaces '-' and '_' by '+' and ','.
*/ staticconstchar base64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
int ceph_base64_encode(const u8 *src, int srclen, char *dst)
{
u32 ac = 0; int bits = 0; int i; char *cp = dst;
for (i = 0; i < srclen; i++) {
ac = (ac << 8) | src[i];
bits += 8; do {
bits -= 6;
*cp++ = base64_table[(ac >> bits) & 0x3f];
} while (bits >= 6);
} if (bits)
*cp++ = base64_table[(ac << (6 - bits)) & 0x3f]; return cp - dst;
}
int ceph_base64_decode(constchar *src, int srclen, u8 *dst)
{
u32 ac = 0; int bits = 0; int i;
u8 *bp = dst;
for (i = 0; i < srclen; i++) { constchar *p = strchr(base64_table, src[i]);
/* * User-created snapshots can't start with '_'. Snapshots that start with this * character are special (hint: there aren't real snapshots) and use the * following format: * * _<SNAPSHOT-NAME>_<INODE-NUMBER> * * where: * - <SNAPSHOT-NAME> - the real snapshot name that may need to be decrypted, * - <INODE-NUMBER> - the inode number (in decimal) for the actual snapshot * * This function parses these snapshot names and returns the inode * <INODE-NUMBER>. 'name_len' will also bet set with the <SNAPSHOT-NAME> * length.
*/ staticstruct inode *parse_longname(conststruct inode *parent, constchar *name, int *name_len)
{ struct ceph_client *cl = ceph_inode_to_client(parent); struct inode *dir = NULL; struct ceph_vino vino = { .snap = CEPH_NOSNAP }; char *name_end, *inode_number; int ret = -EIO; /* NUL-terminate */ char *str __free(kfree) = kmemdup_nul(name, *name_len, GFP_KERNEL); if (!str) return ERR_PTR(-ENOMEM); /* Skip initial '_' */
str++;
name_end = strrchr(str, '_'); if (!name_end) {
doutc(cl, "failed to parse long snapshot name: %s\n", str); return ERR_PTR(-EIO);
}
*name_len = (name_end - str); if (*name_len <= 0) {
pr_err_client(cl, "failed to parse long snapshot name\n"); return ERR_PTR(-EIO);
}
/* Get the inode number */
inode_number = name_end + 1;
ret = kstrtou64(inode_number, 10, &vino.ino); if (ret) {
doutc(cl, "failed to parse inode number: %s\n", str); return ERR_PTR(ret);
}
/* And finally the inode */
dir = ceph_find_inode(parent->i_sb, vino); if (!dir) { /* This can happen if we're not mounting cephfs on the root */
dir = ceph_get_inode(parent->i_sb, vino, NULL); if (IS_ERR(dir))
doutc(cl, "can't find inode %s (%s)\n", inode_number, name);
} return dir;
}
int ceph_encode_encrypted_dname(struct inode *parent, char *buf, int elen)
{ struct ceph_client *cl = ceph_inode_to_client(parent); struct inode *dir = parent; char *p = buf;
u32 len; int name_len = elen; int ret;
u8 *cryptbuf = NULL;
/* Handle the special case of snapshot names that start with '_' */ if (ceph_snap(dir) == CEPH_SNAPDIR && *p == '_') {
dir = parse_longname(parent, p, &name_len); if (IS_ERR(dir)) return PTR_ERR(dir);
p++; /* skip initial '_' */
}
if (!fscrypt_has_encryption_key(dir)) goto out;
/* * Convert cleartext d_name to ciphertext. If result is longer than * CEPH_NOHASH_NAME_MAX, sha256 the remaining bytes * * See: fscrypt_setup_filename
*/ if (!fscrypt_fname_encrypted_size(dir, name_len, NAME_MAX, &len)) {
elen = -ENAMETOOLONG; goto out;
}
/* Allocate a buffer appropriate to hold the result */
cryptbuf = kmalloc(len > CEPH_NOHASH_NAME_MAX ? NAME_MAX : len,
GFP_KERNEL); if (!cryptbuf) {
elen = -ENOMEM; goto out;
}
ret = fscrypt_fname_encrypt(dir,
&(struct qstr)QSTR_INIT(p, name_len),
cryptbuf, len); if (ret) {
elen = ret; goto out;
}
/* hash the end if the name is long enough */ if (len > CEPH_NOHASH_NAME_MAX) {
u8 hash[SHA256_DIGEST_SIZE];
u8 *extra = cryptbuf + CEPH_NOHASH_NAME_MAX;
/* * hash the extra bytes and overwrite crypttext beyond that * point with it
*/
sha256(extra, len - CEPH_NOHASH_NAME_MAX, hash);
memcpy(extra, hash, SHA256_DIGEST_SIZE);
len = CEPH_NOHASH_NAME_MAX + SHA256_DIGEST_SIZE;
}
/* base64 encode the encrypted name */
elen = ceph_base64_encode(cryptbuf, len, p);
doutc(cl, "base64-encoded ciphertext name = %.*s\n", elen, p);
/* To understand the 240 limit, see CEPH_NOHASH_NAME_MAX comments */
WARN_ON(elen > 240); if (dir != parent) // leading _ is already there; append _<inum>
elen += 1 + sprintf(p + elen, "_%ld", dir->i_ino);
out:
kfree(cryptbuf); if (dir != parent) { if ((dir->i_state & I_NEW))
discard_new_inode(dir); else
iput(dir);
} return elen;
}
/** * ceph_fname_to_usr - convert a filename for userland presentation * @fname: ceph_fname to be converted * @tname: temporary name buffer to use for conversion (may be NULL) * @oname: where converted name should be placed * @is_nokey: set to true if key wasn't available during conversion (may be NULL) * * Given a filename (usually from the MDS), format it for presentation to * userland. If @parent is not encrypted, just pass it back as-is. * * Otherwise, base64 decode the string, and then ask fscrypt to format it * for userland presentation. * * Returns 0 on success or negative error code on error.
*/ int ceph_fname_to_usr(conststruct ceph_fname *fname, struct fscrypt_str *tname, struct fscrypt_str *oname, bool *is_nokey)
{ struct inode *dir = fname->dir; struct fscrypt_str _tname = FSTR_INIT(NULL, 0); struct fscrypt_str iname; char *name = fname->name; int name_len = fname->name_len; int ret;
/* Sanity check that the resulting name will fit in the buffer */ if (fname->name_len > NAME_MAX || fname->ctext_len > NAME_MAX) return -EIO;
/* Handle the special case of snapshot names that start with '_' */ if ((ceph_snap(dir) == CEPH_SNAPDIR) && (name_len > 0) &&
(name[0] == '_')) {
dir = parse_longname(dir, name, &name_len); if (IS_ERR(dir)) return PTR_ERR(dir);
name++; /* skip initial '_' */
}
if (!IS_ENCRYPTED(dir)) {
oname->name = fname->name;
oname->len = fname->name_len;
ret = 0; goto out_inode;
}
ret = ceph_fscrypt_prepare_readdir(dir); if (ret) goto out_inode;
/* * Use the raw dentry name as sent by the MDS instead of * generating a nokey name via fscrypt.
*/ if (!fscrypt_has_encryption_key(dir)) { if (fname->no_copy)
oname->name = fname->name; else
memcpy(oname->name, fname->name, fname->name_len);
oname->len = fname->name_len; if (is_nokey)
*is_nokey = true;
ret = 0; goto out_inode;
}
if (fname->ctext_len == 0) { int declen;
if (!tname) {
ret = fscrypt_fname_alloc_buffer(NAME_MAX, &_tname); if (ret) goto out_inode;
tname = &_tname;
}
out:
fscrypt_fname_free_buffer(&_tname);
out_inode: if (dir != fname->dir) { if ((dir->i_state & I_NEW))
discard_new_inode(dir); else
iput(dir);
} return ret;
}
/** * ceph_fscrypt_prepare_readdir - simple __fscrypt_prepare_readdir() wrapper * @dir: directory inode for readdir prep * * Simple wrapper around __fscrypt_prepare_readdir() that will mark directory as * non-complete if this call results in having the directory unlocked. * * Returns: * 1 - if directory was locked and key is now loaded (i.e. dir is unlocked) * 0 - if directory is still locked * < 0 - if __fscrypt_prepare_readdir() fails
*/ int ceph_fscrypt_prepare_readdir(struct inode *dir)
{ bool had_key = fscrypt_has_encryption_key(dir); int err;
if (!IS_ENCRYPTED(dir)) return 0;
err = __fscrypt_prepare_readdir(dir); if (err) return err; if (!had_key && fscrypt_has_encryption_key(dir)) { /* directory just got unlocked, mark it as not complete */
ceph_dir_clear_complete(dir); return 1;
} return 0;
}
/** * ceph_fscrypt_decrypt_pages - decrypt an array of pages * @inode: pointer to inode associated with these pages * @page: pointer to page array * @off: offset into the file that the read data starts * @len: max length to decrypt * * Decrypt an array of fscrypt'ed pages and return the amount of * data decrypted. Any data in the page prior to the start of the * first complete block in the read is ignored. Any incomplete * crypto blocks at the end of the array are ignored (and should * probably be zeroed by the caller). * * Returns the length of the decrypted data or a negative errno.
*/ int ceph_fscrypt_decrypt_pages(struct inode *inode, struct page **page,
u64 off, int len)
{ int i, num_blocks;
u64 baseblk = off >> CEPH_FSCRYPT_BLOCK_SHIFT; int ret = 0;
/* * We can't deal with partial blocks on an encrypted file, so mask off * the last bit.
*/
num_blocks = ceph_fscrypt_blocks(off, len & CEPH_FSCRYPT_BLOCK_MASK);
/* Decrypt each block */ for (i = 0; i < num_blocks; ++i) { int blkoff = i << CEPH_FSCRYPT_BLOCK_SHIFT; int pgidx = blkoff >> PAGE_SHIFT; unsignedint pgoffs = offset_in_page(blkoff); int fret;
fret = ceph_fscrypt_decrypt_block_inplace(inode, page[pgidx],
CEPH_FSCRYPT_BLOCK_SIZE, pgoffs,
baseblk + i); if (fret < 0) { if (ret == 0)
ret = fret; break;
}
ret += CEPH_FSCRYPT_BLOCK_SIZE;
} return ret;
}
/** * ceph_fscrypt_decrypt_extents: decrypt received extents in given buffer * @inode: inode associated with pages being decrypted * @page: pointer to page array * @off: offset into the file that the data in page[0] starts * @map: pointer to extent array * @ext_cnt: length of extent array * * Given an extent map and a page array, decrypt the received data in-place, * skipping holes. Returns the offset into buffer of end of last decrypted * block.
*/ int ceph_fscrypt_decrypt_extents(struct inode *inode, struct page **page,
u64 off, struct ceph_sparse_extent *map,
u32 ext_cnt)
{ struct ceph_client *cl = ceph_inode_to_client(inode); int i, ret = 0; struct ceph_inode_info *ci = ceph_inode(inode);
u64 objno, objoff;
u32 xlen;
/* Nothing to do for empty array */ if (ext_cnt == 0) {
doutc(cl, "%p %llx.%llx empty array, ret 0\n", inode,
ceph_vinop(inode)); return 0;
}
for (i = 0; i < ext_cnt; ++i) { struct ceph_sparse_extent *ext = &map[i]; int pgsoff = ext->off - objoff; int pgidx = pgsoff >> PAGE_SHIFT; int fret;
if ((ext->off | ext->len) & ~CEPH_FSCRYPT_BLOCK_MASK) {
pr_warn_client(cl, "%p %llx.%llx bad encrypted sparse extent " "idx %d off %llx len %llx\n",
inode, ceph_vinop(inode), i, ext->off,
ext->len); return -EIO;
}
fret = ceph_fscrypt_decrypt_pages(inode, &page[pgidx],
off + pgsoff, ext->len);
doutc(cl, "%p %llx.%llx [%d] 0x%llx~0x%llx fret %d\n", inode,
ceph_vinop(inode), i, ext->off, ext->len, fret); if (fret < 0) { if (ret == 0)
ret = fret; break;
}
ret = pgsoff + fret;
}
doutc(cl, "ret %d\n", ret); return ret;
}
/** * ceph_fscrypt_encrypt_pages - encrypt an array of pages * @inode: pointer to inode associated with these pages * @page: pointer to page array * @off: offset into the file that the data starts * @len: max length to encrypt * * Encrypt an array of cleartext pages and return the amount of * data encrypted. Any data in the page prior to the start of the * first complete block in the read is ignored. Any incomplete * crypto blocks at the end of the array are ignored. * * Returns the length of the encrypted data or a negative errno.
*/ int ceph_fscrypt_encrypt_pages(struct inode *inode, struct page **page, u64 off, int len)
{ int i, num_blocks;
u64 baseblk = off >> CEPH_FSCRYPT_BLOCK_SHIFT; int ret = 0;
/* * We can't deal with partial blocks on an encrypted file, so mask off * the last bit.
*/
num_blocks = ceph_fscrypt_blocks(off, len & CEPH_FSCRYPT_BLOCK_MASK);
/* Encrypt each block */ for (i = 0; i < num_blocks; ++i) { int blkoff = i << CEPH_FSCRYPT_BLOCK_SHIFT; int pgidx = blkoff >> PAGE_SHIFT; unsignedint pgoffs = offset_in_page(blkoff); int fret;
fret = ceph_fscrypt_encrypt_block_inplace(inode, page[pgidx],
CEPH_FSCRYPT_BLOCK_SIZE, pgoffs,
baseblk + i); if (fret < 0) { if (ret == 0)
ret = fret; break;
}
ret += CEPH_FSCRYPT_BLOCK_SIZE;
} return ret;
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.2 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.