mirror of
https://gitlab.com/cryptsetup/cryptsetup.git
synced 2025-12-06 00:10:04 +01:00
300 lines
6.9 KiB
C
300 lines
6.9 KiB
C
/*
|
|
* utils - miscellaneous I/O utilities for cryptsetup
|
|
*
|
|
* Copyright (C) 2004 Jana Saout <jana@saout.de>
|
|
* Copyright (C) 2004-2007 Clemens Fruhwirth <clemens@endorphin.org>
|
|
* Copyright (C) 2009-2021 Red Hat, Inc. All rights reserved.
|
|
* Copyright (C) 2009-2021 Milan Broz
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "utils_io.h"
|
|
|
|
static ssize_t _read_buffer(int fd, void *buf, size_t length, volatile int *quit)
|
|
{
|
|
size_t read_size = 0;
|
|
ssize_t r;
|
|
|
|
if (fd < 0 || !buf)
|
|
return -EINVAL;
|
|
|
|
do {
|
|
r = read(fd, buf, length - read_size);
|
|
if (r == -1 && errno != EINTR)
|
|
return r;
|
|
if (r > 0) {
|
|
read_size += (size_t)r;
|
|
buf = (uint8_t*)buf + r;
|
|
}
|
|
if (r == 0 || (quit && *quit))
|
|
return (ssize_t)read_size;
|
|
} while (read_size != length);
|
|
|
|
return (ssize_t)length;
|
|
}
|
|
|
|
ssize_t read_buffer(int fd, void *buf, size_t length)
|
|
{
|
|
return _read_buffer(fd, buf, length, NULL);
|
|
}
|
|
|
|
ssize_t read_buffer_intr(int fd, void *buf, size_t length, volatile int *quit)
|
|
{
|
|
return _read_buffer(fd, buf, length, quit);
|
|
}
|
|
|
|
static ssize_t _write_buffer(int fd, const void *buf, size_t length, volatile int *quit)
|
|
{
|
|
size_t write_size = 0;
|
|
ssize_t w;
|
|
|
|
if (fd < 0 || !buf || !length)
|
|
return -EINVAL;
|
|
|
|
do {
|
|
w = write(fd, buf, length - write_size);
|
|
if (w < 0 && errno != EINTR)
|
|
return w;
|
|
if (w > 0) {
|
|
write_size += (size_t) w;
|
|
buf = (const uint8_t*)buf + w;
|
|
}
|
|
if (w == 0 || (quit && *quit))
|
|
return (ssize_t)write_size;
|
|
} while (write_size != length);
|
|
|
|
return (ssize_t)write_size;
|
|
}
|
|
|
|
ssize_t write_buffer(int fd, const void *buf, size_t length)
|
|
{
|
|
return _write_buffer(fd, buf, length, NULL);
|
|
}
|
|
|
|
ssize_t write_buffer_intr(int fd, const void *buf, size_t length, volatile int *quit)
|
|
{
|
|
return _write_buffer(fd, buf, length, quit);
|
|
}
|
|
|
|
ssize_t write_blockwise(int fd, size_t bsize, size_t alignment,
|
|
void *orig_buf, size_t length)
|
|
{
|
|
void *hangover_buf = NULL, *buf = NULL;
|
|
size_t hangover, solid;
|
|
ssize_t r, ret = -1;
|
|
|
|
if (fd == -1 || !orig_buf || !bsize || !alignment)
|
|
return -1;
|
|
|
|
hangover = length % bsize;
|
|
solid = length - hangover;
|
|
|
|
if ((size_t)orig_buf & (alignment - 1)) {
|
|
if (posix_memalign(&buf, alignment, length))
|
|
return -1;
|
|
memcpy(buf, orig_buf, length);
|
|
} else
|
|
buf = orig_buf;
|
|
|
|
if (solid) {
|
|
r = write_buffer(fd, buf, solid);
|
|
if (r < 0 || r != (ssize_t)solid)
|
|
goto out;
|
|
}
|
|
|
|
if (hangover) {
|
|
if (posix_memalign(&hangover_buf, alignment, bsize))
|
|
goto out;
|
|
memset(hangover_buf, 0, bsize);
|
|
|
|
r = read_buffer(fd, hangover_buf, bsize);
|
|
if (r < 0)
|
|
goto out;
|
|
|
|
if (lseek(fd, -(off_t)r, SEEK_CUR) < 0)
|
|
goto out;
|
|
|
|
memcpy(hangover_buf, (char*)buf + solid, hangover);
|
|
|
|
r = write_buffer(fd, hangover_buf, bsize);
|
|
if (r < 0 || r < (ssize_t)hangover)
|
|
goto out;
|
|
}
|
|
ret = length;
|
|
out:
|
|
free(hangover_buf);
|
|
if (buf != orig_buf)
|
|
free(buf);
|
|
return ret;
|
|
}
|
|
|
|
ssize_t read_blockwise(int fd, size_t bsize, size_t alignment,
|
|
void *orig_buf, size_t length)
|
|
{
|
|
void *hangover_buf = NULL, *buf = NULL;
|
|
size_t hangover, solid;
|
|
ssize_t r, ret = -1;
|
|
|
|
if (fd == -1 || !orig_buf || !bsize || !alignment)
|
|
return -1;
|
|
|
|
hangover = length % bsize;
|
|
solid = length - hangover;
|
|
|
|
if ((size_t)orig_buf & (alignment - 1)) {
|
|
if (posix_memalign(&buf, alignment, length))
|
|
return -1;
|
|
} else
|
|
buf = orig_buf;
|
|
|
|
r = read_buffer(fd, buf, solid);
|
|
if (r < 0 || r != (ssize_t)solid)
|
|
goto out;
|
|
|
|
if (hangover) {
|
|
if (posix_memalign(&hangover_buf, alignment, bsize))
|
|
goto out;
|
|
r = read_buffer(fd, hangover_buf, bsize);
|
|
if (r < 0 || r < (ssize_t)hangover)
|
|
goto out;
|
|
|
|
memcpy((char *)buf + solid, hangover_buf, hangover);
|
|
}
|
|
ret = length;
|
|
out:
|
|
free(hangover_buf);
|
|
if (buf != orig_buf) {
|
|
if (ret != -1)
|
|
memcpy(orig_buf, buf, length);
|
|
free(buf);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Combines llseek with blockwise write. write_blockwise can already deal with short writes
|
|
* but we also need a function to deal with short writes at the start. But this information
|
|
* is implicitly included in the read/write offset, which can not be set to non-aligned
|
|
* boundaries. Hence, we combine llseek with write.
|
|
*/
|
|
ssize_t write_lseek_blockwise(int fd, size_t bsize, size_t alignment,
|
|
void *buf, size_t length, off_t offset)
|
|
{
|
|
void *frontPadBuf = NULL;
|
|
size_t frontHang, innerCount = 0;
|
|
ssize_t r, ret = -1;
|
|
|
|
if (fd == -1 || !buf || !bsize || !alignment)
|
|
return -1;
|
|
|
|
if (offset < 0)
|
|
offset = lseek(fd, offset, SEEK_END);
|
|
|
|
if (offset < 0)
|
|
return -1;
|
|
|
|
frontHang = offset % bsize;
|
|
|
|
if (lseek(fd, offset - frontHang, SEEK_SET) < 0)
|
|
return -1;
|
|
|
|
if (frontHang && length) {
|
|
if (posix_memalign(&frontPadBuf, alignment, bsize))
|
|
return -1;
|
|
|
|
innerCount = bsize - frontHang;
|
|
if (innerCount > length)
|
|
innerCount = length;
|
|
|
|
r = read_buffer(fd, frontPadBuf, bsize);
|
|
if (r < 0 || r < (ssize_t)(frontHang + innerCount))
|
|
goto out;
|
|
|
|
memcpy((char*)frontPadBuf + frontHang, buf, innerCount);
|
|
|
|
if (lseek(fd, offset - frontHang, SEEK_SET) < 0)
|
|
goto out;
|
|
|
|
r = write_buffer(fd, frontPadBuf, bsize);
|
|
if (r < 0 || r != (ssize_t)bsize)
|
|
goto out;
|
|
|
|
buf = (char*)buf + innerCount;
|
|
length -= innerCount;
|
|
}
|
|
|
|
ret = length ? write_blockwise(fd, bsize, alignment, buf, length) : 0;
|
|
if (ret >= 0)
|
|
ret += innerCount;
|
|
out:
|
|
free(frontPadBuf);
|
|
return ret;
|
|
}
|
|
|
|
ssize_t read_lseek_blockwise(int fd, size_t bsize, size_t alignment,
|
|
void *buf, size_t length, off_t offset)
|
|
{
|
|
void *frontPadBuf = NULL;
|
|
size_t frontHang, innerCount = 0;
|
|
ssize_t r, ret = -1;
|
|
|
|
if (fd == -1 || !buf || bsize <= 0)
|
|
return -1;
|
|
|
|
if (offset < 0)
|
|
offset = lseek(fd, offset, SEEK_END);
|
|
|
|
if (offset < 0)
|
|
return -1;
|
|
|
|
frontHang = offset % bsize;
|
|
|
|
if (lseek(fd, offset - frontHang, SEEK_SET) < 0)
|
|
return -1;
|
|
|
|
if (frontHang && length) {
|
|
if (posix_memalign(&frontPadBuf, alignment, bsize))
|
|
return -1;
|
|
|
|
innerCount = bsize - frontHang;
|
|
if (innerCount > length)
|
|
innerCount = length;
|
|
|
|
r = read_buffer(fd, frontPadBuf, bsize);
|
|
if (r < 0 || r < (ssize_t)(frontHang + innerCount))
|
|
goto out;
|
|
|
|
memcpy(buf, (char*)frontPadBuf + frontHang, innerCount);
|
|
|
|
buf = (char*)buf + innerCount;
|
|
length -= innerCount;
|
|
}
|
|
|
|
ret = read_blockwise(fd, bsize, alignment, buf, length);
|
|
if (ret >= 0)
|
|
ret += innerCount;
|
|
out:
|
|
free(frontPadBuf);
|
|
return ret;
|
|
}
|