Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tyhicks/ecryptfs

Says Tyler:
 "Tim's logging message update will be really helpful to users when
  they're trying to locate a problematic file in the lower filesystem
  with filename encryption enabled.

  You'll recognize the fix from Li, as you commented on that.

  You should also be familiar with my setattr/truncate improvements,
  since you were the one that pointed them out to us (thanks again!).
  Andrew noted the /dev/ecryptfs write count sanitization needed to be
  improved, so I've got a fix in there for that along with some other
  less important cleanups of the /dev/ecryptfs read/write code."

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tyhicks/ecryptfs:
  eCryptfs: Fix oops when printing debug info in extent crypto functions
  eCryptfs: Remove unused ecryptfs_read()
  eCryptfs: Check inode changes in setattr
  eCryptfs: Make truncate path killable
  eCryptfs: Infinite loop due to overflow in ecryptfs_write()
  eCryptfs: Replace miscdev read/write magic numbers
  eCryptfs: Report errors in writes to /dev/ecryptfs
  eCryptfs: Sanitize write counts of /dev/ecryptfs
  ecryptfs: Remove unnecessary variable initialization
  ecryptfs: Improve metadata read failure logging
  MAINTAINERS: Update eCryptfs maintainer address
This commit is contained in:
Linus Torvalds 2012-01-25 15:03:04 -08:00
Родитель f8275f9694 58ded24f0f
Коммит 3074c0350b
7 изменённых файлов: 155 добавлений и 195 удалений

Просмотреть файл

@ -2402,7 +2402,7 @@ F: net/bridge/netfilter/ebt*.c
ECRYPT FILE SYSTEM
M: Tyler Hicks <tyhicks@canonical.com>
M: Dustin Kirkland <kirkland@canonical.com>
M: Dustin Kirkland <dustin.kirkland@gazzang.com>
L: ecryptfs@vger.kernel.org
W: https://launchpad.net/ecryptfs
S: Supported

Просмотреть файл

@ -417,17 +417,6 @@ static int ecryptfs_encrypt_extent(struct page *enc_extent_page,
(unsigned long long)(extent_base + extent_offset), rc);
goto out;
}
if (unlikely(ecryptfs_verbosity > 0)) {
ecryptfs_printk(KERN_DEBUG, "Encrypting extent "
"with iv:\n");
ecryptfs_dump_hex(extent_iv, crypt_stat->iv_bytes);
ecryptfs_printk(KERN_DEBUG, "First 8 bytes before "
"encryption:\n");
ecryptfs_dump_hex((char *)
(page_address(page)
+ (extent_offset * crypt_stat->extent_size)),
8);
}
rc = ecryptfs_encrypt_page_offset(crypt_stat, enc_extent_page, 0,
page, (extent_offset
* crypt_stat->extent_size),
@ -440,14 +429,6 @@ static int ecryptfs_encrypt_extent(struct page *enc_extent_page,
goto out;
}
rc = 0;
if (unlikely(ecryptfs_verbosity > 0)) {
ecryptfs_printk(KERN_DEBUG, "Encrypt extent [0x%.16llx]; "
"rc = [%d]\n",
(unsigned long long)(extent_base + extent_offset), rc);
ecryptfs_printk(KERN_DEBUG, "First 8 bytes after "
"encryption:\n");
ecryptfs_dump_hex((char *)(page_address(enc_extent_page)), 8);
}
out:
return rc;
}
@ -543,17 +524,6 @@ static int ecryptfs_decrypt_extent(struct page *page,
(unsigned long long)(extent_base + extent_offset), rc);
goto out;
}
if (unlikely(ecryptfs_verbosity > 0)) {
ecryptfs_printk(KERN_DEBUG, "Decrypting extent "
"with iv:\n");
ecryptfs_dump_hex(extent_iv, crypt_stat->iv_bytes);
ecryptfs_printk(KERN_DEBUG, "First 8 bytes before "
"decryption:\n");
ecryptfs_dump_hex((char *)
(page_address(enc_extent_page)
+ (extent_offset * crypt_stat->extent_size)),
8);
}
rc = ecryptfs_decrypt_page_offset(crypt_stat, page,
(extent_offset
* crypt_stat->extent_size),
@ -567,16 +537,6 @@ static int ecryptfs_decrypt_extent(struct page *page,
goto out;
}
rc = 0;
if (unlikely(ecryptfs_verbosity > 0)) {
ecryptfs_printk(KERN_DEBUG, "Decrypt extent [0x%.16llx]; "
"rc = [%d]\n",
(unsigned long long)(extent_base + extent_offset), rc);
ecryptfs_printk(KERN_DEBUG, "First 8 bytes after "
"decryption:\n");
ecryptfs_dump_hex((char *)(page_address(page)
+ (extent_offset
* crypt_stat->extent_size)), 8);
}
out:
return rc;
}
@ -1590,8 +1550,8 @@ int ecryptfs_read_and_validate_xattr_region(struct dentry *dentry,
*/
int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry)
{
int rc = 0;
char *page_virt = NULL;
int rc;
char *page_virt;
struct inode *ecryptfs_inode = ecryptfs_dentry->d_inode;
struct ecryptfs_crypt_stat *crypt_stat =
&ecryptfs_inode_to_private(ecryptfs_inode)->crypt_stat;
@ -1616,11 +1576,13 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry)
ecryptfs_dentry,
ECRYPTFS_VALIDATE_HEADER_SIZE);
if (rc) {
/* metadata is not in the file header, so try xattrs */
memset(page_virt, 0, PAGE_CACHE_SIZE);
rc = ecryptfs_read_xattr_region(page_virt, ecryptfs_inode);
if (rc) {
printk(KERN_DEBUG "Valid eCryptfs headers not found in "
"file header region or xattr region\n");
"file header region or xattr region, inode %lu\n",
ecryptfs_inode->i_ino);
rc = -EINVAL;
goto out;
}
@ -1629,7 +1591,8 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry)
ECRYPTFS_DONT_VALIDATE_HEADER_SIZE);
if (rc) {
printk(KERN_DEBUG "Valid eCryptfs headers not found in "
"file xattr region either\n");
"file xattr region either, inode %lu\n",
ecryptfs_inode->i_ino);
rc = -EINVAL;
}
if (crypt_stat->mount_crypt_stat->flags
@ -1640,7 +1603,8 @@ int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry)
"crypto metadata only in the extended attribute "
"region, but eCryptfs was mounted without "
"xattr support enabled. eCryptfs will not treat "
"this like an encrypted file.\n");
"this like an encrypted file, inode %lu\n",
ecryptfs_inode->i_ino);
rc = -EINVAL;
}
}

Просмотреть файл

@ -151,6 +151,11 @@ ecryptfs_get_key_payload_data(struct key *key)
* dentry name */
#define ECRYPTFS_TAG_73_PACKET_TYPE 0x49 /* FEK-encrypted filename as
* metadata */
#define ECRYPTFS_MIN_PKT_LEN_SIZE 1 /* Min size to specify packet length */
#define ECRYPTFS_MAX_PKT_LEN_SIZE 2 /* Pass at least this many bytes to
* ecryptfs_parse_packet_length() and
* ecryptfs_write_packet_length()
*/
/* Constraint: ECRYPTFS_FILENAME_MIN_RANDOM_PREPEND_BYTES >=
* ECRYPTFS_MAX_IV_BYTES */
#define ECRYPTFS_FILENAME_MIN_RANDOM_PREPEND_BYTES 16

Просмотреть файл

@ -822,18 +822,6 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia,
size_t num_zeros = (PAGE_CACHE_SIZE
- (ia->ia_size & ~PAGE_CACHE_MASK));
/*
* XXX(truncate) this should really happen at the begginning
* of ->setattr. But the code is too messy to that as part
* of a larger patch. ecryptfs is also totally missing out
* on the inode_change_ok check at the beginning of
* ->setattr while would include this.
*/
rc = inode_newsize_ok(inode, ia->ia_size);
if (rc)
goto out;
if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
truncate_setsize(inode, ia->ia_size);
lower_ia->ia_size = ia->ia_size;
@ -883,6 +871,28 @@ out:
return rc;
}
static int ecryptfs_inode_newsize_ok(struct inode *inode, loff_t offset)
{
struct ecryptfs_crypt_stat *crypt_stat;
loff_t lower_oldsize, lower_newsize;
crypt_stat = &ecryptfs_inode_to_private(inode)->crypt_stat;
lower_oldsize = upper_size_to_lower_size(crypt_stat,
i_size_read(inode));
lower_newsize = upper_size_to_lower_size(crypt_stat, offset);
if (lower_newsize > lower_oldsize) {
/*
* The eCryptfs inode and the new *lower* size are mixed here
* because we may not have the lower i_mutex held and/or it may
* not be appropriate to call inode_newsize_ok() with inodes
* from other filesystems.
*/
return inode_newsize_ok(inode, lower_newsize);
}
return 0;
}
/**
* ecryptfs_truncate
* @dentry: The ecryptfs layer dentry
@ -899,6 +909,10 @@ int ecryptfs_truncate(struct dentry *dentry, loff_t new_length)
struct iattr lower_ia = { .ia_valid = 0 };
int rc;
rc = ecryptfs_inode_newsize_ok(dentry->d_inode, new_length);
if (rc)
return rc;
rc = truncate_upper(dentry, &ia, &lower_ia);
if (!rc && lower_ia.ia_valid & ATTR_SIZE) {
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
@ -978,6 +992,16 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
}
}
mutex_unlock(&crypt_stat->cs_mutex);
rc = inode_change_ok(inode, ia);
if (rc)
goto out;
if (ia->ia_valid & ATTR_SIZE) {
rc = ecryptfs_inode_newsize_ok(inode, ia->ia_size);
if (rc)
goto out;
}
if (S_ISREG(inode->i_mode)) {
rc = filemap_write_and_wait(inode->i_mapping);
if (rc)

Просмотреть файл

@ -109,7 +109,7 @@ int ecryptfs_parse_packet_length(unsigned char *data, size_t *size,
(*size) += ((unsigned char)(data[1]) + 192);
(*length_size) = 2;
} else if (data[0] == 255) {
/* Five-byte length; we're not supposed to see this */
/* If support is added, adjust ECRYPTFS_MAX_PKT_LEN_SIZE */
ecryptfs_printk(KERN_ERR, "Five-byte packet length not "
"supported\n");
rc = -EINVAL;
@ -126,7 +126,7 @@ out:
/**
* ecryptfs_write_packet_length
* @dest: The byte array target into which to write the length. Must
* have at least 5 bytes allocated.
* have at least ECRYPTFS_MAX_PKT_LEN_SIZE bytes allocated.
* @size: The length to write.
* @packet_size_length: The number of bytes used to encode the packet
* length is written to this address.
@ -146,6 +146,7 @@ int ecryptfs_write_packet_length(char *dest, size_t size,
dest[1] = ((size - 192) % 256);
(*packet_size_length) = 2;
} else {
/* If support is added, adjust ECRYPTFS_MAX_PKT_LEN_SIZE */
rc = -EINVAL;
ecryptfs_printk(KERN_WARNING,
"Unsupported packet size: [%zd]\n", size);

Просмотреть файл

@ -218,6 +218,29 @@ out_unlock:
return rc;
}
/*
* miscdevfs packet format:
* Octet 0: Type
* Octets 1-4: network byte order msg_ctx->counter
* Octets 5-N0: Size of struct ecryptfs_message to follow
* Octets N0-N1: struct ecryptfs_message (including data)
*
* Octets 5-N1 not written if the packet type does not include a message
*/
#define PKT_TYPE_SIZE 1
#define PKT_CTR_SIZE 4
#define MIN_NON_MSG_PKT_SIZE (PKT_TYPE_SIZE + PKT_CTR_SIZE)
#define MIN_MSG_PKT_SIZE (PKT_TYPE_SIZE + PKT_CTR_SIZE \
+ ECRYPTFS_MIN_PKT_LEN_SIZE)
/* 4 + ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES comes from tag 65 packet format */
#define MAX_MSG_PKT_SIZE (PKT_TYPE_SIZE + PKT_CTR_SIZE \
+ ECRYPTFS_MAX_PKT_LEN_SIZE \
+ sizeof(struct ecryptfs_message) \
+ 4 + ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES)
#define PKT_TYPE_OFFSET 0
#define PKT_CTR_OFFSET PKT_TYPE_SIZE
#define PKT_LEN_OFFSET (PKT_TYPE_SIZE + PKT_CTR_SIZE)
/**
* ecryptfs_miscdev_read - format and send message from queue
* @file: fs/ecryptfs/euid miscdevfs handle (ignored)
@ -237,7 +260,7 @@ ecryptfs_miscdev_read(struct file *file, char __user *buf, size_t count,
struct ecryptfs_daemon *daemon;
struct ecryptfs_msg_ctx *msg_ctx;
size_t packet_length_size;
char packet_length[3];
char packet_length[ECRYPTFS_MAX_PKT_LEN_SIZE];
size_t i;
size_t total_length;
uid_t euid = current_euid();
@ -305,15 +328,8 @@ check_list:
packet_length_size = 0;
msg_ctx->msg_size = 0;
}
/* miscdevfs packet format:
* Octet 0: Type
* Octets 1-4: network byte order msg_ctx->counter
* Octets 5-N0: Size of struct ecryptfs_message to follow
* Octets N0-N1: struct ecryptfs_message (including data)
*
* Octets 5-N1 not written if the packet type does not
* include a message */
total_length = (1 + 4 + packet_length_size + msg_ctx->msg_size);
total_length = (PKT_TYPE_SIZE + PKT_CTR_SIZE + packet_length_size
+ msg_ctx->msg_size);
if (count < total_length) {
rc = 0;
printk(KERN_WARNING "%s: Only given user buffer of "
@ -324,9 +340,10 @@ check_list:
rc = -EFAULT;
if (put_user(msg_ctx->type, buf))
goto out_unlock_msg_ctx;
if (put_user(cpu_to_be32(msg_ctx->counter), (__be32 __user *)(buf + 1)))
if (put_user(cpu_to_be32(msg_ctx->counter),
(__be32 __user *)(&buf[PKT_CTR_OFFSET])))
goto out_unlock_msg_ctx;
i = 5;
i = PKT_TYPE_SIZE + PKT_CTR_SIZE;
if (msg_ctx->msg) {
if (copy_to_user(&buf[i], packet_length, packet_length_size))
goto out_unlock_msg_ctx;
@ -391,12 +408,6 @@ out:
* @count: Amount of data in @buf
* @ppos: Pointer to offset in file (ignored)
*
* miscdevfs packet format:
* Octet 0: Type
* Octets 1-4: network byte order msg_ctx->counter (0's for non-response)
* Octets 5-N0: Size of struct ecryptfs_message to follow
* Octets N0-N1: struct ecryptfs_message (including data)
*
* Returns the number of bytes read from @buf
*/
static ssize_t
@ -405,60 +416,78 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf,
{
__be32 counter_nbo;
u32 seq;
size_t packet_size, packet_size_length, i;
ssize_t sz = 0;
size_t packet_size, packet_size_length;
char *data;
uid_t euid = current_euid();
int rc;
unsigned char packet_size_peek[ECRYPTFS_MAX_PKT_LEN_SIZE];
ssize_t rc;
if (count == 0)
goto out;
if (count == 0) {
return 0;
} else if (count == MIN_NON_MSG_PKT_SIZE) {
/* Likely a harmless MSG_HELO or MSG_QUIT - no packet length */
goto memdup;
} else if (count < MIN_MSG_PKT_SIZE || count > MAX_MSG_PKT_SIZE) {
printk(KERN_WARNING "%s: Acceptable packet size range is "
"[%d-%lu], but amount of data written is [%zu].",
__func__, MIN_MSG_PKT_SIZE, MAX_MSG_PKT_SIZE, count);
return -EINVAL;
}
if (copy_from_user(packet_size_peek, &buf[PKT_LEN_OFFSET],
sizeof(packet_size_peek))) {
printk(KERN_WARNING "%s: Error while inspecting packet size\n",
__func__);
return -EFAULT;
}
rc = ecryptfs_parse_packet_length(packet_size_peek, &packet_size,
&packet_size_length);
if (rc) {
printk(KERN_WARNING "%s: Error parsing packet length; "
"rc = [%zd]\n", __func__, rc);
return rc;
}
if ((PKT_TYPE_SIZE + PKT_CTR_SIZE + packet_size_length + packet_size)
!= count) {
printk(KERN_WARNING "%s: Invalid packet size [%zu]\n", __func__,
packet_size);
return -EINVAL;
}
memdup:
data = memdup_user(buf, count);
if (IS_ERR(data)) {
printk(KERN_ERR "%s: memdup_user returned error [%ld]\n",
__func__, PTR_ERR(data));
goto out;
return PTR_ERR(data);
}
sz = count;
i = 0;
switch (data[i++]) {
switch (data[PKT_TYPE_OFFSET]) {
case ECRYPTFS_MSG_RESPONSE:
if (count < (1 + 4 + 1 + sizeof(struct ecryptfs_message))) {
if (count < (MIN_MSG_PKT_SIZE
+ sizeof(struct ecryptfs_message))) {
printk(KERN_WARNING "%s: Minimum acceptable packet "
"size is [%zd], but amount of data written is "
"only [%zd]. Discarding response packet.\n",
__func__,
(1 + 4 + 1 + sizeof(struct ecryptfs_message)),
count);
(MIN_MSG_PKT_SIZE
+ sizeof(struct ecryptfs_message)), count);
rc = -EINVAL;
goto out_free;
}
memcpy(&counter_nbo, &data[i], 4);
memcpy(&counter_nbo, &data[PKT_CTR_OFFSET], PKT_CTR_SIZE);
seq = be32_to_cpu(counter_nbo);
i += 4;
rc = ecryptfs_parse_packet_length(&data[i], &packet_size,
&packet_size_length);
rc = ecryptfs_miscdev_response(
&data[PKT_LEN_OFFSET + packet_size_length],
packet_size, euid, current_user_ns(),
task_pid(current), seq);
if (rc) {
printk(KERN_WARNING "%s: Error parsing packet length; "
"rc = [%d]\n", __func__, rc);
goto out_free;
}
i += packet_size_length;
if ((1 + 4 + packet_size_length + packet_size) != count) {
printk(KERN_WARNING "%s: (1 + packet_size_length([%zd])"
" + packet_size([%zd]))([%zd]) != "
"count([%zd]). Invalid packet format.\n",
__func__, packet_size_length, packet_size,
(1 + packet_size_length + packet_size), count);
goto out_free;
}
rc = ecryptfs_miscdev_response(&data[i], packet_size,
euid, current_user_ns(),
task_pid(current), seq);
if (rc)
printk(KERN_WARNING "%s: Failed to deliver miscdev "
"response to requesting operation; rc = [%d]\n",
"response to requesting operation; rc = [%zd]\n",
__func__, rc);
goto out_free;
}
break;
case ECRYPTFS_MSG_HELO:
case ECRYPTFS_MSG_QUIT:
@ -467,12 +496,13 @@ ecryptfs_miscdev_write(struct file *file, const char __user *buf,
ecryptfs_printk(KERN_WARNING, "Dropping miscdev "
"message of unrecognized type [%d]\n",
data[0]);
break;
rc = -EINVAL;
goto out_free;
}
rc = count;
out_free:
kfree(data);
out:
return sz;
return rc;
}

Просмотреть файл

@ -130,13 +130,18 @@ int ecryptfs_write(struct inode *ecryptfs_inode, char *data, loff_t offset,
pgoff_t ecryptfs_page_idx = (pos >> PAGE_CACHE_SHIFT);
size_t start_offset_in_page = (pos & ~PAGE_CACHE_MASK);
size_t num_bytes = (PAGE_CACHE_SIZE - start_offset_in_page);
size_t total_remaining_bytes = ((offset + size) - pos);
loff_t total_remaining_bytes = ((offset + size) - pos);
if (fatal_signal_pending(current)) {
rc = -EINTR;
break;
}
if (num_bytes > total_remaining_bytes)
num_bytes = total_remaining_bytes;
if (pos < offset) {
/* remaining zeros to write, up to destination offset */
size_t total_remaining_zeros = (offset - pos);
loff_t total_remaining_zeros = (offset - pos);
if (num_bytes > total_remaining_zeros)
num_bytes = total_remaining_zeros;
@ -193,15 +198,19 @@ int ecryptfs_write(struct inode *ecryptfs_inode, char *data, loff_t offset,
}
pos += num_bytes;
}
if ((offset + size) > ecryptfs_file_size) {
i_size_write(ecryptfs_inode, (offset + size));
if (pos > ecryptfs_file_size) {
i_size_write(ecryptfs_inode, pos);
if (crypt_stat->flags & ECRYPTFS_ENCRYPTED) {
rc = ecryptfs_write_inode_size_to_metadata(
int rc2;
rc2 = ecryptfs_write_inode_size_to_metadata(
ecryptfs_inode);
if (rc) {
if (rc2) {
printk(KERN_ERR "Problem with "
"ecryptfs_write_inode_size_to_metadata; "
"rc = [%d]\n", rc);
"rc = [%d]\n", rc2);
if (!rc)
rc = rc2;
goto out;
}
}
@ -273,76 +282,3 @@ int ecryptfs_read_lower_page_segment(struct page *page_for_ecryptfs,
flush_dcache_page(page_for_ecryptfs);
return rc;
}
#if 0
/**
* ecryptfs_read
* @data: The virtual address into which to write the data read (and
* possibly decrypted) from the lower file
* @offset: The offset in the decrypted view of the file from which to
* read into @data
* @size: The number of bytes to read into @data
* @ecryptfs_file: The eCryptfs file from which to read
*
* Read an arbitrary amount of data from an arbitrary location in the
* eCryptfs page cache. This is done on an extent-by-extent basis;
* individual extents are decrypted and read from the lower page
* cache (via VFS reads). This function takes care of all the
* address translation to locations in the lower filesystem.
*
* Returns zero on success; non-zero otherwise
*/
int ecryptfs_read(char *data, loff_t offset, size_t size,
struct file *ecryptfs_file)
{
struct inode *ecryptfs_inode = ecryptfs_file->f_dentry->d_inode;
struct page *ecryptfs_page;
char *ecryptfs_page_virt;
loff_t ecryptfs_file_size = i_size_read(ecryptfs_inode);
loff_t data_offset = 0;
loff_t pos;
int rc = 0;
if ((offset + size) > ecryptfs_file_size) {
rc = -EINVAL;
printk(KERN_ERR "%s: Attempt to read data past the end of the "
"file; offset = [%lld]; size = [%td]; "
"ecryptfs_file_size = [%lld]\n",
__func__, offset, size, ecryptfs_file_size);
goto out;
}
pos = offset;
while (pos < (offset + size)) {
pgoff_t ecryptfs_page_idx = (pos >> PAGE_CACHE_SHIFT);
size_t start_offset_in_page = (pos & ~PAGE_CACHE_MASK);
size_t num_bytes = (PAGE_CACHE_SIZE - start_offset_in_page);
size_t total_remaining_bytes = ((offset + size) - pos);
if (num_bytes > total_remaining_bytes)
num_bytes = total_remaining_bytes;
ecryptfs_page = ecryptfs_get_locked_page(ecryptfs_inode,
ecryptfs_page_idx);
if (IS_ERR(ecryptfs_page)) {
rc = PTR_ERR(ecryptfs_page);
printk(KERN_ERR "%s: Error getting page at "
"index [%ld] from eCryptfs inode "
"mapping; rc = [%d]\n", __func__,
ecryptfs_page_idx, rc);
goto out;
}
ecryptfs_page_virt = kmap_atomic(ecryptfs_page, KM_USER0);
memcpy((data + data_offset),
((char *)ecryptfs_page_virt + start_offset_in_page),
num_bytes);
kunmap_atomic(ecryptfs_page_virt, KM_USER0);
flush_dcache_page(ecryptfs_page);
SetPageUptodate(ecryptfs_page);
unlock_page(ecryptfs_page);
page_cache_release(ecryptfs_page);
pos += num_bytes;
data_offset += num_bytes;
}
out:
return rc;
}
#endif /* 0 */