Files
scoutfs/kmod/src/kvec.c
Zach Brown 967e90e5ef Fix kvec overlapping comparison
The comparisons were a bit wrong when comparing overlaping kvec
endpoints.  We want to compare the starts and ends with the ends and
starts, respectively.

Signed-off-by: Zach Brown <zab@versity.com>
2017-04-18 13:44:53 -07:00

186 lines
4.2 KiB
C

/*
* Copyright (C) 2016 Versity Software, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License v2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/magic.h>
#include <linux/random.h>
#include <linux/statfs.h>
#include "super.h"
#include "format.h"
#include "inode.h"
#include "dir.h"
#include "xattr.h"
#include "msg.h"
#include "block.h"
#include "counters.h"
#include "trans.h"
#include "buddy.h"
#include "kvec.h"
#include "scoutfs_trace.h"
/*
* Return the result of memcmp between the min of the two total lengths.
* If their shorter lengths are equal than the shorter length is considered
* smaller than the longer.
*/
int scoutfs_kvec_memcmp(struct kvec *a, struct kvec *b)
{
int b_off = 0;
int a_off = 0;
int len;
int ret;
while (a->iov_base && b->iov_base) {
len = min(a->iov_len - a_off, b->iov_len - b_off);
ret = memcmp(a->iov_base + a_off, b->iov_base + b_off, len);
if (ret)
return ret;
b_off += len;
if (b_off == b->iov_len) {
b++;
b_off = 0;
}
a_off += len;
if (a_off == a->iov_len) {
a++;
a_off = 0;
}
}
return a->iov_base ? 1 : b->iov_base ? -1 : 0;
}
/*
* Returns 0 if [a,b] overlaps with [c,d]. Returns -1 if a < c and
* 1 if b > d.
*/
int scoutfs_kvec_cmp_overlap(struct kvec *a, struct kvec *b,
struct kvec *c, struct kvec *d)
{
return scoutfs_kvec_memcmp(b, c) < 0 ? -1 :
scoutfs_kvec_memcmp(a, d) > 0 ? 1 : 0;
}
/*
* Set just the pointers and length fields in the dst vector to point to
* the source vector.
*/
void scoutfs_kvec_clone(struct kvec *dst, struct kvec *src)
{
int i;
for (i = 0; i < SCOUTFS_KVEC_NR; i++)
*(dst++) = *(src++);
}
/*
* Copy as much of src as fits in dst. Null base pointers termintae the
* copy. The number of bytes copied is returned. Only the buffers
* pointed to by dst are changed, the kvec elements are not changed.
*/
int scoutfs_kvec_memcpy(struct kvec *dst, struct kvec *src)
{
int src_off = 0;
int dst_off = 0;
int copied = 0;
int len;
while (dst->iov_base && src->iov_base) {
len = min(dst->iov_len - dst_off, src->iov_len - src_off);
memcpy(dst->iov_base + dst_off, src->iov_base + src_off, len);
copied += len;
src_off += len;
if (src_off == src->iov_len) {
src++;
src_off = 0;
}
dst_off += len;
if (dst_off == dst->iov_len) {
dst++;
dst_off = 0;
}
}
return copied;
}
/*
* Copy bytes in src into dst, stopping if dst is full. The number of copied
* bytes is returned and the lengths of dst are updated if the size changes.
* The pointers in dst are not changed.
*/
int scoutfs_kvec_memcpy_truncate(struct kvec *dst, struct kvec *src)
{
int copied = scoutfs_kvec_memcpy(dst, src);
size_t bytes;
int i;
if (copied < scoutfs_kvec_length(dst)) {
bytes = copied;
for (i = 0; i < SCOUTFS_KVEC_NR; i++) {
dst[i].iov_len = min(dst[i].iov_len, bytes);
bytes -= dst[i].iov_len;
}
}
return copied;
}
/*
* Copy the src key vector into one new allocation in the dst. The existing
* dst is clobbered. The source isn't changed.
*/
int scoutfs_kvec_dup_flatten(struct kvec *dst, struct kvec *src)
{
void *ptr;
size_t len = scoutfs_kvec_length(src);
ptr = kmalloc(len, GFP_NOFS);
if (!ptr)
return -ENOMEM;
scoutfs_kvec_init(dst, ptr, len);
scoutfs_kvec_memcpy(dst, src);
return 0;
}
/*
* Free all the set pointers in the kvec. The pointer values aren't modified
* if they're freed.
*/
void scoutfs_kvec_kfree(struct kvec *kvec)
{
while (kvec->iov_base)
kfree((kvec++)->iov_base);
}
void scoutfs_kvec_init_null(struct kvec *kvec)
{
memset(kvec, 0, SCOUTFS_KVEC_NR * sizeof(kvec[0]));
}
void scoutfs_kvec_swap(struct kvec *a, struct kvec *b)
{
SCOUTFS_DECLARE_KVEC(tmp);
memcpy(tmp, a, sizeof(tmp));
memcpy(a, b, sizeof(tmp));
memcpy(b, tmp, sizeof(tmp));
}