update to 2.6.9-rc1
[linux-flexiantxendom0-3.2.10.git] / arch / um / drivers / ubd_user.c
index 400c2a0..93b6f3e 100644 (file)
 #include <signal.h>
 #include <string.h>
 #include <netinet/in.h>
+#include <sys/stat.h>
 #include <sys/time.h>
+#include <sys/fcntl.h>
 #include <sys/socket.h>
+#include <string.h>
 #include <sys/mman.h>
 #include <sys/param.h>
 #include "asm/types.h"
 #include "user.h"
 #include "ubd_user.h"
 #include "os.h"
-#include "cow.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
+
+#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, 
+                 unsigned long *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;
+       }
+
+       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;
+
+       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);
+}
 
 static int same_backing_files(char *from_cmdline, char *from_cow, char *cow)
 {
-       struct uml_stat buf1, buf2;
-       int err;
+       struct stat buf1, buf2;
 
        if(from_cmdline == NULL) return(1);
        if(!strcmp(from_cmdline, from_cow)) return(1);
 
-       err = os_stat_file(from_cmdline, &buf1);
-       if(err < 0){
-               printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err);
+       if(stat(from_cmdline, &buf1) < 0){
+               printk("Couldn't stat '%s', errno = %d\n", from_cmdline, 
+                      errno);
                return(1);
        }
-       err = os_stat_file(from_cow, &buf2);
-       if(err < 0){
-               printk("Couldn't stat '%s', err = %d\n", from_cow, -err);
+       if(stat(from_cow, &buf2) < 0){
+               printk("Couldn't stat '%s', errno = %d\n", from_cow, errno);
                return(1);
        }
-       if((buf1.ust_major == buf2.ust_major) && 
-          (buf1.ust_minor == buf2.ust_minor) && 
-          (buf1.ust_ino == buf2.ust_ino))
+       if((buf1.st_dev == buf2.st_dev) && (buf1.st_ino == buf2.st_ino))
                return(1);
 
        printk("Backing file mismatch - \"%s\" requested,\n"
@@ -57,21 +174,20 @@ static int same_backing_files(char *from_cmdline, char *from_cow, char *cow)
 
 static int backing_file_mismatch(char *file, __u64 size, time_t mtime)
 {
-       unsigned long modtime;
+       struct stat64 buf;
        long long actual;
        int err;
 
-       err = os_file_modtime(file, &modtime);
-       if(err < 0){
-               printk("Failed to get modification time of backing file "
-                      "\"%s\", err = %d\n", file, -err);
-               return(err);
+       if(stat64(file, &buf) < 0){
+               printk("Failed to stat backing file \"%s\", errno = %d\n",
+                      file, errno);
+               return(-errno);
        }
 
        err = os_file_size(file, &actual);
-       if(err < 0){
+       if(err){
                printk("Failed to get size of backing file \"%s\", "
-                      "err = %d\n", file, -err);
+                      "errno = %d\n", file, -err);
                return(err);
        }
 
@@ -80,9 +196,9 @@ static int backing_file_mismatch(char *file, __u64 size, time_t mtime)
                       "file\n", size, actual);
                return(-EINVAL);
        }
-       if(modtime != mtime){
+       if(buf.st_mtime != mtime){
                printk("mtime mismatch (%ld vs %ld) of COW header vs backing "
-                      "file\n", mtime, modtime);
+                      "file\n", mtime, buf.st_mtime);
                return(-EINVAL);
        }
        return(0);
@@ -93,16 +209,124 @@ int read_cow_bitmap(int fd, void *buf, int offset, int len)
        int err;
 
        err = os_seek_file(fd, offset);
-       if(err < 0) 
-               return(err);
+       if(err != 0) return(-errno);
+       err = read(fd, buf, len);
+       if(err < 0) return(-errno);
+       return(0);
+}
 
-       err = os_read_file(fd, buf, len);
-       if(err < 0) 
-               return(err);
+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);
 }
 
+static int write_cow_header(char *cow_file, int fd, char *backing_file, 
+                           int sectorsize, long long *size)
+{
+        struct cow_header_v2 *header;
+       struct stat64 buf;
+       int err;
+
+       err = os_seek_file(fd, 0);
+       if(err != 0){
+               printk("write_cow_header - lseek failed, errno = %d\n", errno);
+               return(-errno);
+       }
+
+       err = -ENOMEM;
+       header = um_kmalloc(sizeof(*header));
+       if(header == NULL){
+               printk("Failed to allocate COW V2 header\n");
+               goto out;
+       }
+       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){
+               printk("Stat of backing file '%s' failed, errno = %d\n",
+                      header->backing_file, errno);
+               err = -errno;
+               goto out_free;
+       }
+
+       err = os_file_size(header->backing_file, size);
+       if(err){
+               printk("Couldn't get size of backing file '%s', errno = %d\n",
+                      header->backing_file, -*size);
+               goto out_free;
+       }
+
+       header->mtime = htonl(buf.st_mtime);
+       header->size = htonll(*size);
+       header->sectorsize = htonl(sectorsize);
+
+       err = write(fd, header, sizeof(*header));
+       if(err != sizeof(*header)){
+               printk("Write of header to new COW file '%s' failed, "
+                      "errno = %d\n", cow_file, errno);
+               goto out_free;
+       }
+       err = 0;
+ out_free:
+       kfree(header);
+ out:
+       return(err);
+}
+
 int open_ubd_file(char *file, struct openflags *openflags, 
                  char **backing_file_out, int *bitmap_offset_out, 
                  unsigned long *bitmap_len_out, int *data_offset_out, 
@@ -110,36 +334,26 @@ int open_ubd_file(char *file, struct openflags *openflags,
 {
        time_t mtime;
        __u64 size;
-       __u32 version, align;
        char *backing_file;
-       int fd, err, sectorsize, same, mode = 0644;
+        int fd, err, sectorsize, magic, same, mode = 0644;
 
-       fd = os_open_file(file, *openflags, mode);
-       if(fd < 0){
+        if((fd = os_open_file(file, *openflags, mode)) < 0){
                if((fd == -ENOENT) && (create_cow_out != NULL))
                        *create_cow_out = 1;
                 if(!openflags->w ||
                    ((errno != EROFS) && (errno != EACCES))) return(-errno);
                openflags->w = 0;
-               fd = os_open_file(file, *openflags, mode); 
-               if(fd < 0) 
+                if((fd = os_open_file(file, *openflags, mode)) < 0) 
                        return(fd);
         }
-
-       err = os_lock_file(fd, openflags->w);
-       if(err < 0){
-               printk("Failed to lock '%s', err = %d\n", file, -err);
-               goto out_close;
-       }
-       
        if(backing_file_out == NULL) return(fd);
 
-       err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime,
-                             &size, &sectorsize, &align, bitmap_offset_out);
+       err = read_cow_header(fd, &magic, &backing_file, &mtime, &size, 
+                             &sectorsize, bitmap_offset_out);
        if(err && (*backing_file_out != NULL)){
                printk("Failed to read COW header from COW file \"%s\", "
-                      "errno = %d\n", file, -err);
-               goto out_close;
+                      "errno = %d\n", file, err);
+               goto error;
        }
        if(err) return(fd);
 
@@ -149,33 +363,36 @@ int open_ubd_file(char *file, struct openflags *openflags,
 
        if(!same && !backing_file_mismatch(*backing_file_out, size, mtime)){
                printk("Switching backing file to '%s'\n", *backing_file_out);
-               err = write_cow_header(file, fd, *backing_file_out,
-                                      sectorsize, align, &size);
+               err = write_cow_header(file, fd, *backing_file_out, 
+                                      sectorsize, &size);
                if(err){
-                       printk("Switch failed, errno = %d\n", -err);
+                       printk("Switch failed, errno = %d\n", err);
                        return(err);
                }
        }
        else {
                *backing_file_out = backing_file;
                err = backing_file_mismatch(*backing_file_out, size, mtime);
-               if(err) goto out_close;
+               if(err) goto error;
        }
 
-       cow_sizes(version, size, sectorsize, align, *bitmap_offset_out, 
-                 bitmap_len_out, data_offset_out);
+       sizes(size, sectorsize, *bitmap_offset_out, bitmap_len_out, 
+             data_offset_out);
 
         return(fd);
- out_close:
-       os_close_file(fd);
+ error:
+       close(fd);
        return(err);
 }
 
 int create_cow_file(char *cow_file, char *backing_file, struct openflags flags,
-                   int sectorsize, int alignment, int *bitmap_offset_out, 
+                   int sectorsize, int *bitmap_offset_out, 
                    unsigned long *bitmap_len_out, int *data_offset_out)
 {
-       int err, fd;
+       __u64 blocks;
+       long zero;
+       int err, fd, i;
+       long long size;
 
        flags.c = 1;
        fd = open_ubd_file(cow_file, &flags, NULL, NULL, NULL, NULL, NULL);
@@ -186,50 +403,57 @@ int create_cow_file(char *cow_file, char *backing_file, struct openflags flags,
                goto out;
        }
 
-       err = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment,
-                           bitmap_offset_out, bitmap_len_out, 
-                           data_offset_out);
-       if(!err)
-               return(fd);
+       err = write_cow_header(cow_file, fd, backing_file, sectorsize, &size);
+       if(err) goto out_close;
+
+       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)){
+                       printk("Write of bitmap to new COW file '%s' failed, "
+                              "errno = %d\n", cow_file, errno);
+                       goto out_close;
+               }
+       }
+
+       sizes(size, sectorsize, sizeof(struct cow_header_v2), 
+             bitmap_len_out, data_offset_out);
+       *bitmap_offset_out = sizeof(struct cow_header_v2);
+
+       return(fd);
 
-       os_close_file(fd);
+ out_close:
+       close(fd);
  out:
        return(err);
 }
 
-/* XXX Just trivial wrappers around os_read_file and os_write_file */
 int read_ubd_fs(int fd, void *buffer, int len)
 {
-       return(os_read_file(fd, buffer, len));
-}
+       int n;
 
-int write_ubd_fs(int fd, char *buffer, int len)
-{
-       return(os_write_file(fd, buffer, len));
+       n = read(fd, buffer, len);
+       if(n < 0) return(-errno);
+       else return(n);
 }
 
-static int update_bitmap(struct io_thread_req *req)
+int write_ubd_fs(int fd, char *buffer, int len)
 {
        int n;
 
-       if(req->cow_offset == -1)
-               return(0);
-
-       n = os_seek_file(req->fds[1], req->cow_offset);
-       if(n < 0){
-               printk("do_io - bitmap lseek failed : err = %d\n", -n);
-               return(1);
-       }
+       n = write(fd, buffer, len);
+       if(n < 0) return(-errno);
+       else return(n);
+}
 
-       n = os_write_file(req->fds[1], &req->bitmap_words,
-                         sizeof(req->bitmap_words));
-       if(n != sizeof(req->bitmap_words)){
-               printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
-                      req->fds[1]);
-               return(1);
-       }
+int ubd_is_dir(char *file)
+{
+       struct stat64 buf;
 
-       return(0);
+       if(stat64(file, &buf) < 0) return(0);
+       return(S_ISDIR(buf.st_mode));
 }
 
 void do_io(struct io_thread_req *req)
@@ -237,18 +461,8 @@ void do_io(struct io_thread_req *req)
        char *buf;
        unsigned long len;
        int n, nsectors, start, end, bit;
-       int err;
        __u64 off;
 
-       if(req->op == UBD_MMAP){
-               /* Touch the page to force the host to do any necessary IO to 
-                * get it into memory 
-                */
-               n = *((volatile int *) req->buffer);
-               req->error = update_bitmap(req);
-               return;
-       }
-
        nsectors = req->length / req->sectorsize;
        start = 0;
        do {
@@ -259,14 +473,15 @@ void do_io(struct io_thread_req *req)
                                    &req->sector_mask) == bit))
                        end++;
 
+               if(end != nsectors)
+                       printk("end != nsectors\n");
                off = req->offset + req->offsets[bit] + 
                        start * req->sectorsize;
                len = (end - start) * req->sectorsize;
                buf = &req->buffer[start * req->sectorsize];
 
-               err = os_seek_file(req->fds[bit], off);
-               if(err < 0){
-                       printk("do_io - lseek failed : err = %d\n", -err);
+               if(os_seek_file(req->fds[bit], off) != 0){
+                       printk("do_io - lseek failed : errno = %d\n", errno);
                        req->error = 1;
                        return;
                }
@@ -275,10 +490,11 @@ void do_io(struct io_thread_req *req)
                        do {
                                buf = &buf[n];
                                len -= n;
-                               n = os_read_file(req->fds[bit], buf, len);
+                               n = read(req->fds[bit], buf, len);
                                if (n < 0) {
-                                       printk("do_io - read failed, err = %d "
-                                              "fd = %d\n", -n, req->fds[bit]);
+                                       printk("do_io - read returned %d : "
+                                              "errno = %d fd = %d\n", n,
+                                              errno, req->fds[bit]);
                                        req->error = 1;
                                        return;
                                }
@@ -286,10 +502,11 @@ void do_io(struct io_thread_req *req)
                        if (n < len) memset(&buf[n], 0, len - n);
                }
                else {
-                       n = os_write_file(req->fds[bit], buf, len);
+                       n = write(req->fds[bit], buf, len);
                        if(n != len){
-                               printk("do_io - write failed err = %d "
-                                      "fd = %d\n", -n, req->fds[bit]);
+                               printk("do_io - write returned %d : "
+                                      "errno = %d fd = %d\n", n, 
+                                      errno, req->fds[bit]);
                                req->error = 1;
                                return;
                        }
@@ -298,7 +515,24 @@ void do_io(struct io_thread_req *req)
                start = end;
        } while(start < nsectors);
 
-       req->error = update_bitmap(req);
+       if(req->cow_offset != -1){
+               if(os_seek_file(req->fds[1], req->cow_offset) != 0){
+                       printk("do_io - bitmap lseek failed : errno = %d\n",
+                              errno);
+                       req->error = 1;
+                       return;
+               }
+               n = write(req->fds[1], &req->bitmap_words, 
+                         sizeof(req->bitmap_words));
+               if(n != sizeof(req->bitmap_words)){
+                       printk("do_io - bitmap update returned %d : "
+                              "errno = %d fd = %d\n", n, errno, req->fds[1]);
+                       req->error = 1;
+                       return;
+               }
+       }
+       req->error = 0;
+       return;
 }
 
 /* Changed in start_io_thread, which is serialized by being called only
@@ -316,23 +550,19 @@ int io_thread(void *arg)
 
        signal(SIGWINCH, SIG_IGN);
        while(1){
-               n = os_read_file(kernel_fd, &req, sizeof(req));
-               if(n != sizeof(req)){
-                       if(n < 0)
-                               printk("io_thread - read failed, fd = %d, "
-                                      "err = %d\n", kernel_fd, -n);
-                       else {
-                               printk("io_thread - short read, fd = %d, "
-                                      "length = %d\n", kernel_fd, n);
-                       }
+               n = read(kernel_fd, &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++;
                do_io(&req);
-               n = os_write_file(kernel_fd, &req, sizeof(req));
+               n = write(kernel_fd, &req, sizeof(req));
                if(n != sizeof(req))
-                       printk("io_thread - write failed, fd = %d, err = %d\n",
-                              kernel_fd, -n);
+                       printk("io_thread - write failed, errno = %d\n",
+                              errno);
        }
 }
 
@@ -341,11 +571,10 @@ int start_io_thread(unsigned long sp, int *fd_out)
        int pid, fds[2], err;
 
        err = os_pipe(fds, 1, 1);
-       if(err < 0){
-               printk("start_io_thread - os_pipe failed, err = %d\n", -err);
-               goto out;
+       if(err){
+               printk("start_io_thread - os_pipe failed, errno = %d\n", -err);
+               return(-1);
        }
-
        kernel_fd = fds[0];
        *fd_out = fds[1];
 
@@ -353,19 +582,32 @@ int start_io_thread(unsigned long sp, int *fd_out)
                    NULL);
        if(pid < 0){
                printk("start_io_thread - clone failed : errno = %d\n", errno);
-               goto out_close;
+               return(-errno);
        }
-
        return(pid);
+}
 
- out_close:
-       os_close_file(fds[0]);
-       os_close_file(fds[1]);
-       kernel_fd = -1;
-       *fd_out = -1;
- out:
-       return(err);
+#ifdef notdef
+int start_io_thread(unsigned long sp, int *fd_out)
+{
+       int pid;
+
+       if((kernel_fd = get_pty()) < 0) return(-1);
+       raw(kernel_fd, 0);
+       if((*fd_out = open(ptsname(kernel_fd), O_RDWR)) < 0){
+               printk("Couldn't open tty for IO\n");
+               return(-1);
+       }
+
+       pid = clone(io_thread, (void *) sp, CLONE_FILES | CLONE_VM | SIGCHLD,
+                   NULL);
+       if(pid < 0){
+               printk("start_io_thread - clone failed : errno = %d\n", errno);
+               return(-errno);
+       }
+       return(pid);
 }
+#endif
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.