blob: e2bf2a93d378197b9b0ba2b241c9730e8cb21196 [file] [log] [blame]
/*
* Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
* Copyright (C) 2001 Ridgerun,Inc (glonnon@ridgerun.com)
* Licensed under the GPL
*/
#include <stddef.h>
#include <unistd.h>
#include <errno.h>
#include <sched.h>
#include <signal.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/param.h>
#include "asm/types.h"
#include "user_util.h"
#include "kern_util.h"
#include "user.h"
#include "ubd_user.h"
#include <endian.h>
#include <byteswap.h>
#if __BYTE_ORDER == __BIG_ENDIAN
# define ntohll(x) (x)
# define htonll(x) (x)
#elif __BYTE_ORDER == __LITTLE_ENDIAN
# define ntohll(x) bswap_64(x)
# define htonll(x) bswap_64(x)
#else
#error "__BYTE_ORDER not defined"
#endif
extern void panic(char *fmt, ...);
#define PATH_LEN_V1 256
struct cow_header_v1 {
int magic;
int version;
char backing_file[PATH_LEN_V1];
time_t mtime;
__u64 size;
int sectorsize;
};
#define PATH_LEN_V2 MAXPATHLEN
struct cow_header_v2 {
unsigned long magic;
unsigned long version;
char backing_file[PATH_LEN_V2];
time_t mtime;
__u64 size;
int sectorsize;
};
union cow_header {
struct cow_header_v1 v1;
struct cow_header_v2 v2;
};
#define COW_MAGIC 0x4f4f4f4d /* MOOO */
#define COW_VERSION 2
static void sizes(__u64 size, int sectorsize, int bitmap_offset,
int *bitmap_len_out, int *data_offset_out)
{
*bitmap_len_out = (size + sectorsize - 1) / (8 * sectorsize);
*data_offset_out = bitmap_offset + *bitmap_len_out;
*data_offset_out = (*data_offset_out + sectorsize - 1) / sectorsize;
*data_offset_out *= sectorsize;
}
static int read_cow_header(int fd, int *magic_out, char **backing_file_out,
time_t *mtime_out, __u64 *size_out,
int *sectorsize_out, int *bitmap_offset_out)
{
union cow_header *header;
char *file;
int err, n;
unsigned long version, magic;
header = um_kmalloc(sizeof(*header));
if(header == NULL){
printk("read_cow_header - Failed to allocate header\n");
return(-ENOMEM);
}
err = -EINVAL;
n = read(fd, header, sizeof(*header));
if(n < offsetof(typeof(header->v1), backing_file)){
printk("read_cow_header - short header\n");
goto out;
}
err = 0;
magic = header->v1.magic;
if(magic == COW_MAGIC) {
version = header->v1.version;
}
else if(magic == ntohl(COW_MAGIC)){
version = ntohl(header->v1.version);
}
else goto out;
*magic_out = COW_MAGIC;
err = -EINVAL;
if(version == 1){
if(n < sizeof(header->v1)){
printk("read_cow_header - failed to read V1 header\n");
goto out;
}
*mtime_out = header->v1.mtime;
*size_out = header->v1.size;
*sectorsize_out = header->v1.sectorsize;
*bitmap_offset_out = sizeof(header->v1);
file = header->v1.backing_file;
}
else if(version == 2){
if(n < sizeof(header->v2)){
printk("read_cow_header - failed to read V2 header\n");
goto out;
}
*mtime_out = ntohl(header->v2.mtime);
*size_out = ntohll(header->v2.size);
*sectorsize_out = ntohl(header->v2.sectorsize);
*bitmap_offset_out = sizeof(header->v2);
file = header->v2.backing_file;
}
else {
printk("read_cow_header - invalid COW version\n");
goto out;
}
err = -ENOMEM;
*backing_file_out = uml_strdup(file);
if(*backing_file_out == NULL){
printk("read_cow_header - failed to allocate backing file\n");
goto out;
}
err = 0;
out:
kfree(header);
return(err);
}
int open_ubd_file(char *file, int *openflags, char **backing_file_out,
int *bitmap_offset_out, int *bitmap_len_out,
int *data_offset_out)
{
struct stat64 buf;
time_t mtime;
__u64 size;
int fd, err, sectorsize, magic, mode = 0644;
if(backing_file_out != NULL) *backing_file_out = NULL;
if((fd = open64(file, *openflags, mode)) < 0){
if(((*openflags & O_ACCMODE) != O_RDWR) ||
((errno != EROFS) && (errno != EACCES))) return(-errno);
*openflags &= ~O_ACCMODE;
*openflags |= O_RDONLY;
if((fd = open64(file, *openflags, mode)) < 0) return(-errno);
}
if(backing_file_out == NULL) return(fd);
err = read_cow_header(fd, &magic, backing_file_out, &mtime, &size,
&sectorsize, bitmap_offset_out);
if(err) goto error;
if(magic != COW_MAGIC){
*backing_file_out = NULL;
return(fd);
}
err = stat64(*backing_file_out, &buf);
if(err) goto error;
err = -EINVAL;
if(buf.st_size != size){
printk("Size mismatch (%ld vs %ld) of COW header vs backing "
"file\n", buf.st_size, size);
goto error;
}
if(buf.st_mtime != mtime){
printk("mtime mismatch (%ld vs %ld) of COW header vs backing "
"file\n", buf.st_mtime, mtime);
goto error;
}
sizes(size, sectorsize, *bitmap_offset_out, bitmap_len_out,
data_offset_out);
return(fd);
error:
close(fd);
return(err);
}
int read_cow_bitmap(int fd, void *buf, int offset, int len)
{
int err;
err = lseek64(fd, offset, SEEK_SET);
if(err != offset) return(-errno);
err = read(fd, buf, len);
if(err < 0) return(-errno);
return(0);
}
static int absolutize(char *to, int size, char *from)
{
char save_cwd[256], *slash;
int remaining;
if(getcwd(save_cwd, sizeof(save_cwd)) == NULL) {
printk("absolutize : unable to get cwd - errno = %d\n", errno);
return(-1);
}
slash = strrchr(from, '/');
if(slash != NULL){
*slash = '\0';
if(chdir(from)){
*slash = '/';
printk("absolutize : Can't cd to '%s' - errno = %d\n",
from, errno);
return(-1);
}
*slash = '/';
if(getcwd(to, size) == NULL){
printk("absolutize : unable to get cwd of '%s' - "
"errno = %d\n", from, errno);
return(-1);
}
remaining = size - strlen(to);
if(strlen(slash) + 1 > remaining){
printk("absolutize : unable to fit '%s' into %d "
"chars\n", from, size);
return(-1);
}
strcat(to, slash);
}
else {
if(strlen(save_cwd) + 1 + strlen(from) + 1 > size){
printk("absolutize : unable to fit '%s' into %d "
"chars\n", from, size);
return(-1);
}
strcpy(to, save_cwd);
strcat(to, "/");
strcat(to, from);
}
chdir(save_cwd);
return(0);
}
int create_cow_file(char *cow_file, char *backing_file, int sectorsize,
int *bitmap_offset_out, int *bitmap_len_out,
int *data_offset_out)
{
struct cow_header_v2 *header;
struct stat64 buf;
__u64 blocks;
long zero;
int err, fd, i, flags;
__u64 size;
flags = O_RDWR | O_CREAT;
fd = open_ubd_file(cow_file, &flags, NULL, NULL, NULL, NULL);
if(fd < 0) return(fd);
err = -ENOMEM;
header = um_kmalloc(sizeof(*header));
if(header == NULL){
printk("Failed to allocate COW V2 header\n");
goto out_close;
}
header->magic = htonl(COW_MAGIC);
header->version = htonl(COW_VERSION);
err = -EINVAL;
if(strlen(backing_file) > sizeof(header->backing_file) - 1){
printk("Backing file name \"%s\" is too long - names are "
"limited to %d characters\n", backing_file,
sizeof(header->backing_file) - 1);
goto out_free;
}
if(absolutize(header->backing_file, sizeof(header->backing_file),
backing_file))
goto out_free;
err = stat64(header->backing_file, &buf);
if(err < 0) goto out_free;
header->mtime = htonl(buf.st_mtime);
header->size = htonll(buf.st_size);
header->sectorsize = htonl(sectorsize);
size = buf.st_size;
err = write(fd, header, sizeof(*header));
if(err != sizeof(*header)) goto out_free;
blocks = (size + sectorsize - 1) / sectorsize;
blocks = (blocks + sizeof(long) * 8 - 1) / (sizeof(long) * 8);
zero = 0;
for(i = 0; i < blocks; i++){
err = write(fd, &zero, sizeof(zero));
if(err != sizeof(zero)) goto out_free;
}
sizes(size, sectorsize, sizeof(struct cow_header_v2),
bitmap_len_out, data_offset_out);
*bitmap_offset_out = sizeof(struct cow_header_v2);
kfree(header);
return(fd);
out_free:
kfree(header);
out_close:
close(fd);
return(err);
}
int read_ubd_fs(int fd, void *buffer, int len)
{
return(read(fd, buffer, len));
}
int write_ubd_fs(int fd, char *buffer, int len)
{
return(write(fd, buffer, len));
}
int ubd_is_dir(char *file)
{
struct stat64 buf;
if(stat64(file, &buf) < 0) return(0);
return(S_ISDIR(buf.st_mode));
}
__u64 last_offset = -1;
int do_io(struct io_thread_req *req)
{
char *buf;
unsigned long len;
int n, nsectors, start, end, bit;
__u64 off;
nsectors = req->length / req->sectorsize;
start = 0;
do {
bit = ubd_test_bit(start, &req->sector_mask);
end = start;
while((end < nsectors) &&
(ubd_test_bit(end, &req->sector_mask) == bit))
end++;
if(end != nsectors)
printk("end != nsectors\n");
off = req->offset + req->offsets[bit];
len = (end - start) * req->sectorsize;
buf = &req->buffer[start * req->sectorsize];
last_offset = off;
if(lseek64(req->fds[bit], off, SEEK_SET) != off){
printk("do_io - lseek failed : errno = %d\n",
errno);
return(-1);
}
if(req->read){
n = read(req->fds[bit], buf, len);
if(n <= 0){
printk("do_io - read returned %d : "
"errno = %d fd = %d\n", n,
errno, req->fds[bit]);
return(-1);
}
else if(n != len) memset(&buf[n], 0, len - n);
}
else {
n = write(req->fds[bit], buf, len);
if(n != len){
printk("do_io - write returned %d : "
"errno = %d fd = %d\n", n,
errno, req->fds[bit]);
return(-1);
}
}
start = end;
} while(start < nsectors);
if(req->bitmap_start != -1){
off = req->bitmap_start * sizeof(req->bitmap[0]);
off += req->bitmap_offset;
if(lseek64(req->fds[1], off, SEEK_SET) != off){
printk("do_io - bitmap lseek failed : errno = %d\n",
errno);
return(-1);
}
len = (req->bitmap_end - req->bitmap_start) *
sizeof(req->bitmap[0]);
n = write(req->fds[1], &req->bitmap[req->bitmap_start], len);
if(n != len){
printk("do_io - bitmap update returned %d : "
"errno = %d fd = %d\n", n, errno, req->fds[1]);
return(-1);
}
}
return(0);
}
int kernel_fds[2] = { -1, -1 };
int io_count = 0;
int io_thread(void *arg)
{
struct io_thread_req req;
int n;
while(1){
n = read(kernel_fds[0], &req, sizeof(req));
if(n < 0) printk("io_thread - read returned %d, errno = %d\n",
n, errno);
else if(n < sizeof(req)){
printk("io_thread - short read : length = %d\n", n);
continue;
}
io_count++;
if(do_io(&req)) req.error = 1;
if(write(kernel_fds[1], &req, sizeof(req)) != sizeof(req))
printk("io_thread - write failed, errno = %d\n",
errno);
}
}
int start_io_thread(unsigned long sp, int *fds_out)
{
int pid;
if((kernel_fds[0] = get_pty()) < 0) return(-1);
raw(kernel_fds[0], 1);
if((fds_out[0] = open(ptsname(kernel_fds[0]), O_RDWR)) < 0){
printk("Couldn't open tty for IO\n");
return(-1);
}
fds_out[1] = fds_out[0];
kernel_fds[1] = kernel_fds[0];
pid = clone(io_thread, (void *) sp, CLONE_VM | CLONE_FILES | SIGCHLD,
NULL);
if(pid < 0){
printk("start_io_thread - clone failed : errno = %d\n", errno);
return(-errno);
}
return(pid);
}
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
* c-file-style: "linux"
* End:
*/