diff -Nur uml-2.4.20-5/arch/um/drivers/ubd_user.c work-2.4.20-5/arch/um/drivers/ubd_user.c --- uml-2.4.20-5/arch/um/drivers/ubd_user.c 2003-05-23 07:02:29.000000000 -0700 +++ work-2.4.20-5/arch/um/drivers/ubd_user.c 2003-05-26 17:02:22.000000000 -0700 @@ -24,6 +24,7 @@ #include "user.h" #include "ubd_user.h" #include "os.h" +#include "cow.h" #include #include @@ -37,412 +38,6 @@ #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 stat64 buf1, buf2; - - if(from_cmdline == NULL) return(1); - if(!strcmp(from_cmdline, from_cow)) return(1); - - if(stat64(from_cmdline, &buf1) < 0){ - printk("Couldn't stat '%s', errno = %d\n", from_cmdline, - errno); - return(1); - } - if(stat64(from_cow, &buf2) < 0){ - printk("Couldn't stat '%s', errno = %d\n", from_cow, errno); - return(1); - } - if((buf1.st_dev == buf2.st_dev) && (buf1.st_ino == buf2.st_ino)) - return(1); - - printk("Backing file mismatch - \"%s\" requested,\n" - "\"%s\" specified in COW header of \"%s\"\n", - from_cmdline, from_cow, cow); - return(0); -} - -static int backing_file_mismatch(char *file, __u64 size, time_t mtime) -{ - struct stat64 buf; - long long actual; - int 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){ - printk("Failed to get size of backing file \"%s\", " - "errno = %d\n", file, -err); - return(err); - } - - if(actual != size){ - printk("Size mismatch (%ld vs %ld) of COW header vs backing " - "file\n", size, actual); - return(-EINVAL); - } - if(buf.st_mtime != mtime){ - printk("mtime mismatch (%ld vs %ld) of COW header vs backing " - "file\n", mtime, buf.st_mtime); - return(-EINVAL); - } - return(0); -} - -int read_cow_bitmap(int fd, void *buf, int offset, int len) -{ - int err; - - err = os_seek_file(fd, offset); - if(err != 0) 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); -} - -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, - int *create_cow_out) -{ - time_t mtime; - __u64 size; - char *backing_file; - int fd, err, sectorsize, magic, same, mode = 0644; - - 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; - if((fd = os_open_file(file, *openflags, mode)) < 0) - return(fd); - } - - err = os_lock_file(fd, openflags->w); - if(err){ - printk("Failed to lock '%s', errno = %d\n", file, -err); - goto error; - } - - if(backing_file_out == NULL) return(fd); - - err = read_cow_header(fd, &magic, &backing_file, &mtime, &size, - §orsize, 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 error; - } - if(err) return(fd); - - if(backing_file_out == NULL) return(fd); - - same = same_backing_files(*backing_file_out, backing_file, file); - - 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, &size); - if(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 error; - } - - sizes(size, sectorsize, *bitmap_offset_out, bitmap_len_out, - data_offset_out); - - return(fd); - error: - os_close_file(fd); - return(err); -} - -int create_cow_file(char *cow_file, char *backing_file, struct openflags flags, - int sectorsize, int *bitmap_offset_out, - unsigned long *bitmap_len_out, int *data_offset_out) -{ - __u64 offset; - int err, fd; - long long size; - char zero = 0; - - flags.c = 1; - fd = open_ubd_file(cow_file, &flags, NULL, NULL, NULL, NULL, NULL); - if(fd < 0){ - err = fd; - printk("Open of COW file '%s' failed, errno = %d\n", cow_file, - -err); - goto out; - } - - err = write_cow_header(cow_file, fd, backing_file, sectorsize, &size); - if(err) 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); - - offset = *data_offset_out + size - sizeof(zero); - err = os_seek_file(fd, offset); - if(err != 0){ - printk("cow bitmap lseek failed : errno = %d\n", errno); - goto out_close; - } - - /* does not really matter how much we write it is just to set EOF - * this also sets the entire COW bitmap - * to zero without having to allocate it - */ - err = os_write_file(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; - } - - return(fd); - - out_close: - close(fd); - out: - return(err); -} - int read_ubd_fs(int fd, void *buffer, int len) { int n; @@ -463,77 +58,23 @@ void 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, (unsigned char *) &req->sector_mask); - end = start; - while((end < nsectors) && - (ubd_test_bit(end, (unsigned char *) - &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]; - - if(os_seek_file(req->fds[bit], off) != 0){ - printk("do_io - lseek failed : errno = %d\n", errno); - req->error = 1; - return; - } - if(req->op == UBD_READ){ - n = 0; - do { - buf = &buf[n]; - len -= n; - 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]); - req->error = 1; - return; - } - } while((n < len) && (n != 0)); - 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]); - req->error = 1; - return; - } - } - - start = end; - } while(start < nsectors); + int n; - 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; + if (req->op == UBD_READ) { + n = read_COW(req->info, req->buffer, req->length, req->offset); + if (n != req->length) { + printk("do_io - read_COW returned %d : errno = %d info " + "= %s\n", n, errno, req->info->name); + req->error = 1; + return; + } + } else if (req->op == UBD_WRITE) { + n = write_COW(req->info, req->buffer, req->length, req->offset); + if (n != req->length) { + printk("do_io - write_COW returned %d : errno = %d info " + "= %s\n", n, errno, req->info->name); + req->error = 1; + return; } } req->error = 0; diff -Nur uml-2.4.20-5/arch/um/drivers/ubd_kern.c work-2.4.20-5/arch/um/drivers/ubd_kern.c --- uml-2.4.20-5/arch/um/drivers/ubd_kern.c 2003-05-23 07:02:29.000000000 -0700 +++ work-2.4.20-5/arch/um/drivers/ubd_kern.c 2003-05-27 07:20:14.000000000 -0700 @@ -3,9 +3,11 @@ * Licensed under the GPL */ -/* 2001-09-28...2002-04-17 - * Partition stuff by James_McMechan@hotmail.com - * old style ubd by setting UBD_SHIFT to 0 +/* 2001-09-28... + * Partition stuff by James_McMechan at hotmail.com + * clean ups for ubd-many. + * Also the Stackable COW files and fake_major is working again + * James McMechan */ #define MAJOR_NR UBD_MAJOR @@ -37,40 +39,31 @@ #include "mconsole_kern.h" #include "init.h" #include "irq_user.h" +#include "irq_kern.h" #include "ubd_user.h" #include "2_5compat.h" #include "os.h" +#include "cow.h" static int ubd_open(struct inode * inode, struct file * filp); static int ubd_release(struct inode * inode, struct file * file); static int ubd_ioctl(struct inode * inode, struct file * file, - unsigned int cmd, unsigned long arg); + unsigned int cmd, unsigned long arg); static int ubd_revalidate(kdev_t rdev); static int ubd_revalidate1(kdev_t rdev); -#define MAX_DEV (8) -#define MAX_MINOR (MAX_DEV << UBD_SHIFT) - -/* Not modified by this driver */ -static int blk_sizes[MAX_MINOR] = { [ 0 ... MAX_MINOR - 1 ] = BLOCK_SIZE }; -static int hardsect_sizes[MAX_MINOR] = { [ 0 ... MAX_MINOR - 1 ] = 512 }; - -/* Protected by ubd_lock */ -static int sizes[MAX_MINOR] = { [ 0 ... MAX_MINOR - 1 ] = 0 }; +/* number of minors per major number */ +#define NM (256) +#define MAX_DEV (NM >> UBD_SHIFT) +#define MAX_FAKE MAX_DEV static struct block_device_operations ubd_blops = { - .open = ubd_open, - .release = ubd_release, - .ioctl = ubd_ioctl, - .revalidate = ubd_revalidate, + .open = ubd_open, + .release = ubd_release, + .ioctl = ubd_ioctl, + .revalidate = ubd_revalidate, }; -/* Protected by ubd_lock, except in prepare_request and ubd_ioctl because - * the block layer should ensure that the device is idle before closing it. - */ -static struct hd_struct ubd_part[MAX_MINOR] = - { [ 0 ... MAX_MINOR - 1 ] = { 0, 0, 0 } }; - /* Protected by io_request_lock */ static request_queue_t *ubd_queue; @@ -81,33 +74,27 @@ #define INIT_GENDISK(maj, name, parts, shift, bsizes, max, blops) \ { \ - .major = maj, \ - .major_name = name, \ - .minor_shift = shift, \ - .max_p = 1 << shift, \ - .part = parts, \ - .sizes = bsizes, \ - .nr_real = max, \ - .real_devices = NULL, \ - .next = NULL, \ - .fops = blops, \ - .de_arr = NULL, \ - .flags = 0 \ -} - -static struct gendisk ubd_gendisk = INIT_GENDISK(MAJOR_NR, "ubd", ubd_part, - UBD_SHIFT, sizes, MAX_DEV, - &ubd_blops); -static struct gendisk fake_gendisk = INIT_GENDISK(0, "ubd", ubd_part, - UBD_SHIFT, sizes, MAX_DEV, - &ubd_blops); + .major = maj, \ + .major_name = name, \ + .minor_shift = shift, \ + .max_p = 1 << shift, \ + .part = parts, \ + .sizes = bsizes, \ + .nr_real = max, \ + .real_devices = NULL, \ + .next = NULL, \ + .fops = blops, \ + .de_arr = NULL, \ + .flags = 0 \ +} + #ifdef CONFIG_BLK_DEV_UBD_SYNC #define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 1, .c = 0, \ - .cl = 1 }) + .cl = 1 }) #else #define OPEN_FLAGS ((struct openflags) { .r = 1, .w = 1, .s = 0, .c = 0, \ - .cl = 1 }) + .cl = 1 }) #endif /* Not protected - changed only in ubd_setup_common and then only to @@ -115,213 +102,152 @@ */ static struct openflags global_openflags = OPEN_FLAGS; -struct cow { - char *file; - int fd; - unsigned long *bitmap; - unsigned long bitmap_len; - int bitmap_offset; - int data_offset; -}; - struct ubd { char *file; - int count; - int fd; - __u64 size; + struct devinfo *info; + struct ubd_frame *fp; + int count, n; struct openflags boot_openflags; struct openflags openflags; devfs_handle_t devfs; - struct cow cow; }; -#define DEFAULT_COW { \ - .file = NULL, \ - .fd = -1, \ - .bitmap = NULL, \ - .bitmap_offset = 0, \ - .data_offset = 0, \ -} - #define DEFAULT_UBD { \ - .file = NULL, \ - .count = 0, \ - .fd = -1, \ - .size = -1, \ - .boot_openflags = OPEN_FLAGS, \ - .openflags = OPEN_FLAGS, \ - .devfs = NULL, \ - .cow = DEFAULT_COW, \ -} + .file = NULL, \ + .info = NULL, \ + .fp = NULL, \ + .count = 0, \ + .n = 0, \ + .boot_openflags = OPEN_FLAGS, \ + .openflags = OPEN_FLAGS, \ + .devfs = NULL, \ +} + +static struct ubd_frame { + struct ubd_frame *next_frame; + int base,blk_sizes[NM], hardsect_sizes[NM], sizes[NM]; + struct gendisk gendisk; + struct ubd dev[MAX_DEV]; + struct hd_struct part[NM]; + char name[9]; +} static_frame = { + .next_frame = NULL, + .base = 0, + .blk_sizes = { [0 ... (NM - 1)] = BLOCK_SIZE }, + .hardsect_sizes = { [0 ... (NM - 1)] = 512 }, + .sizes = { [0 ... (NM - 1)] = 0 }, + .gendisk = INIT_GENDISK(MAJOR_NR, "ubd", static_frame.part, UBD_SHIFT, static_frame.sizes, MAX_DEV, &ubd_blops), + .dev = { [0 ... (MAX_DEV - 1)] = DEFAULT_UBD }, + .part = { [0 ... (NM - 1)] = { 0, 0, 0 }}, +}; + +/* all that is needed for fake devices is the fake gendisk */ +static struct gendisk fakegendisk = INIT_GENDISK(0, "ubd_fake", static_frame.part, UBD_SHIFT, static_frame.sizes, MAX_FAKE, &ubd_blops); -struct ubd ubd_dev[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = DEFAULT_UBD }; +static void ubd_frame_init(struct ubd_frame *fp, int n); static int ubd0_init(void) { - struct ubd *dev = &ubd_dev[0]; + struct ubd *dev = &static_frame.dev[0]; - if(dev->file == NULL) + if (dev->file == NULL) dev->file = "root_fs"; return(0); } __initcall(ubd0_init); -/* Only changed by fake_ide_setup which is a setup */ -static int fake_ide = 0; -static struct proc_dir_entry *proc_ide_root = NULL; -static struct proc_dir_entry *proc_ide = NULL; - -static void make_proc_ide(void) -{ - proc_ide_root = proc_mkdir("ide", 0); - proc_ide = proc_mkdir("ide0", proc_ide_root); -} - -static int proc_ide_read_media(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - int len; - - strcpy(page, "disk\n"); - len = strlen("disk\n"); - len -= off; - if (len < count){ - *eof = 1; - if (len <= 0) return 0; - } - else len = count; - *start = page + off; - return len; -} - -static void make_ide_entries(char *dev_name) -{ - struct proc_dir_entry *dir, *ent; - char name[64]; - - if(!fake_ide) return; - - /* Without locking this could race if a UML was booted with no - * disks and then two mconsole requests which add disks came in - * at the same time. - */ - spin_lock(&ubd_lock); - if(proc_ide_root == NULL) make_proc_ide(); - spin_unlock(&ubd_lock); - - dir = proc_mkdir(dev_name, proc_ide); - if(!dir) return; - - ent = create_proc_entry("media", S_IFREG|S_IRUGO, dir); - if(!ent) return; - ent->nlink = 1; - ent->data = NULL; - ent->read_proc = proc_ide_read_media; - ent->write_proc = NULL; - sprintf(name,"ide0/%s", dev_name); - proc_symlink(dev_name, proc_ide_root, name); -} - -static int fake_ide_setup(char *str) -{ - fake_ide = 1; - return(1); -} - -__setup("fake_ide", fake_ide_setup); - -__uml_help(fake_ide_setup, -"fake_ide\n" -" Create ide0 entries that map onto ubd devices.\n\n" -); - -static int parse_unit(char **ptr) +static struct ubd *parse_unit(char **ptr) { + struct ubd_frame *fp = &static_frame; + struct ubd *dev; char *str = *ptr, *end; int n = -1; - if(isdigit(*str)) { + if (isdigit(*str)) { n = simple_strtoul(str, &end, 0); - if(end == str) - return(-1); + if (end == str) + n = -1; *ptr = end; - } - else if (('a' <= *str) && (*str <= 'h')) { + } else if (('a' <= *str) && (*str <= 'h')) { n = *str - 'a'; str++; *ptr = str; } - return(n); + + while (fp && !((fp->base <= n) && (n < fp->base + MAX_DEV))) { + fp = fp->next_frame; + } + if (!fp) { + fp = &static_frame; + while (fp->next_frame) { + fp = fp->next_frame; + } + fp->next_frame = kmalloc(sizeof(struct ubd_frame), GFP_KERNEL); + fp = fp->next_frame; + if (!fp) { + printk(KERN_ERR "failed to allocate memory for a new device\n"); + return NULL; + } + ubd_frame_init(fp, n); + } + if ((fp->base <= n) && (n < fp->base + MAX_DEV)) { + dev = &fp->dev[n - fp->base]; + dev->n = n; + dev->fp = fp; + } else dev = 0; + return (dev); } -static int ubd_setup_common(char *str, int *index_out) +static struct ubd *ubd_setup_common(char *str) { + struct ubd *dev = NULL; struct openflags flags = global_openflags; - struct ubd *dev; - char *backing_file; - int n, err; - if(index_out) *index_out = -1; - n = *str; - if(n == '='){ + if(*str == '='){ char *end; int major; str++; if(!strcmp(str, "sync")){ global_openflags.s = 1; - return(0); + return NULL; } major = simple_strtoul(str, &end, 0); - if((*end != '\0') || (end == str)){ - printk(KERN_ERR - "ubd_setup : didn't parse major number\n"); - return(1); + if ((*end != '\0') || (end == str)) { + printk(KERN_ERR "ubd_setup : didn't parse major number\n"); + return NULL; } - err = 1; spin_lock(&ubd_lock); - if(fake_major != MAJOR_NR){ + if (fake_major != MAJOR_NR) { printk(KERN_ERR "Can't assign a fake major twice\n"); goto out1; } - fake_gendisk.major = major; fake_major = major; - + printk(KERN_INFO "Setting extra ubd major number to %d\n", - major); - err = 0; - out1: + major); +out1: spin_unlock(&ubd_lock); - return(err); + return NULL; } - n = parse_unit(&str); - if(n < 0){ - printk(KERN_ERR "ubd_setup : couldn't parse unit number " - "'%s'\n", str); - return(1); - } - - if(n >= MAX_DEV){ - printk(KERN_ERR "ubd_setup : index %d out of range " - "(%d devices)\n", n, MAX_DEV); - return(1); + dev = parse_unit(&str); + if (dev == 0) { + printk(KERN_ERR "ubd_setup : index out of range %s\n", str); + return NULL; } - err = 1; spin_lock(&ubd_lock); - dev = &ubd_dev[n]; - if(dev->file != NULL){ + if (dev->file != NULL) { printk(KERN_ERR "ubd_setup : device already configured\n"); + dev = NULL; goto out2; } - if(index_out) *index_out = n; - if (*str == 'r'){ flags.w = 0; str++; @@ -335,23 +261,16 @@ goto out2; } - err = 0; - backing_file = strchr(str, ','); - if(backing_file){ - *backing_file = '\0'; - backing_file++; - } dev->file = str; - dev->cow.file = backing_file; dev->boot_openflags = flags; - out2: +out2: spin_unlock(&ubd_lock); - return(err); + return (dev); } static int ubd_setup(char *str) { - ubd_setup_common(str, NULL); + ubd_setup_common(str); return(1); } @@ -370,20 +289,6 @@ " to be written to disk on the host immediately.\n\n" ); -static int fakehd(char *str) -{ - printk(KERN_INFO - "fakehd : Changing ubd_gendisk.major_name to \"hd\".\n"); - ubd_gendisk.major_name = "hd"; - return(1); -} - -__setup("fakehd", fakehd); -__uml_help(fakehd, -"fakehd\n" -" Change the ubd device name to \"hd\".\n\n" -); - static void do_ubd_request(request_queue_t * q); /* Only changed by ubd_init, which is an initcall. */ @@ -420,16 +325,15 @@ intr_count++; n = read_ubd_fs(thread_fd, &req, sizeof(req)); if(n != sizeof(req)){ - printk(KERN_ERR "Pid %d - spurious interrupt in ubd_handler, " - "errno = %d\n", os_getpid(), -n); + printk(KERN_ERR "Pid %d - spurious interrupt in ubd_handler, errno = %d\n", os_getpid(), -n); spin_lock(&io_request_lock); end_request(0); spin_unlock(&io_request_lock); return; } - - if((req.offset != ((__u64) (CURRENT->sector)) << 9) || - (req.length != (CURRENT->current_nr_sectors) << 9)) + + if ((req.offset != ((__u64) (CURRENT->sector))) || + (req.length != (CURRENT->current_nr_sectors))) panic("I/O op mismatch"); spin_lock(&io_request_lock); @@ -449,7 +353,7 @@ void kill_io_thread(void) { - if(io_pid != -1) + if (io_pid != -1) os_kill_process(io_pid, 1); } @@ -458,57 +362,59 @@ /* Initialized in an initcall, and unchanged thereafter */ devfs_handle_t ubd_dir_handle; -static int ubd_add(int n) +static int ubd_add(struct ubd *dev) { - struct ubd *dev = &ubd_dev[n]; - char name[sizeof("nnnnnn\0")], dev_name[sizeof("ubd0x")]; + char name[sizeof("nnnnnn\0")]; int err = -EISDIR; + int n = dev->n; + struct ubd_frame *fp = dev->fp; + struct gendisk *disk = &fp->gendisk; if(dev->file == NULL) goto out; - err = ubd_revalidate1(MKDEV(MAJOR_NR, n << UBD_SHIFT)); - if(err) + err = ubd_revalidate1(MKDEV(disk->major, (n - fp->base) << UBD_SHIFT)); + if (err) goto out; - sprintf(name, "%d", n); - dev->devfs = devfs_register(ubd_dir_handle, name, DEVFS_FL_REMOVABLE, - MAJOR_NR, n << UBD_SHIFT, S_IFBLK | - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, - &ubd_blops, NULL); - - if(!strcmp(ubd_gendisk.major_name, "ubd")) - sprintf(dev_name, "%s%d", ubd_gendisk.major_name, n); - else sprintf(dev_name, "%s%c", ubd_gendisk.major_name, - n + 'a'); - - make_ide_entries(dev_name); - return(0); - - out: - return(err); + if (n < MAX_FAKE) { + sprintf(name, "%d", n); + dev->devfs = devfs_register(ubd_dir_handle, name, + DEVFS_FL_DEFAULT, disk->major, + (n - fp->base) << UBD_SHIFT, + S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP | + S_IWGRP, &ubd_blops, NULL); + } else { + char newname[21]; /* "../ubd65536/disc65536" */ + char symname[13]; /* "ubd/disc65536" */ + snprintf((char *) &newname, sizeof(newname), "../ubd%d/disc%d", n >> UBD_SHIFT, n - fp->base); + snprintf((char *) &symname, sizeof(symname), "ubd/disc%d", n); + devfs_mk_symlink(NULL, symname, DEVFS_FL_DEFAULT, newname, NULL, NULL); + } +out: + return (err); } static int ubd_config(char *str) { - int n, err; + int err; + struct ubd *dev; str = uml_strdup(str); if(str == NULL){ printk(KERN_ERR "ubd_config failed to strdup string\n"); return(1); } - err = ubd_setup_common(str, &n); - if(err){ + dev = ubd_setup_common(str); + if (dev == 0) { kfree(str); return(-1); } - if(n == -1) return(0); spin_lock(&ubd_lock); - err = ubd_add(n); + err = ubd_add(dev); if(err) - ubd_dev[n].file = NULL; + dev->file = NULL; spin_unlock(&ubd_lock); return(err); @@ -517,21 +423,14 @@ static int ubd_get_config(char *name, char *str, int size, char **error_out) { struct ubd *dev; - char *end; - int n, len = 0; + int len = 0; - n = simple_strtoul(name, &end, 0); - if((*end != '\0') || (end == name)){ - *error_out = "ubd_get_config : didn't parse device number"; - return(-1); - } - - if((n >= MAX_DEV) || (n < 0)){ + dev = parse_unit(&name); + if (dev == 0) { *error_out = "ubd_get_config : device number out of range"; return(-1); } - dev = &ubd_dev[n]; spin_lock(&ubd_lock); if(dev->file == NULL){ @@ -541,13 +440,13 @@ CONFIG_CHUNK(str, size, len, dev->file, 0); - if(dev->cow.file != NULL){ + if (dev->info->name != NULL) { CONFIG_CHUNK(str, size, len, ",", 0); - CONFIG_CHUNK(str, size, len, dev->cow.file, 1); + CONFIG_CHUNK(str, size, len, dev->info->name, 1); } else CONFIG_CHUNK(str, size, len, "", 1); - out: +out: spin_unlock(&ubd_lock); return(len); } @@ -555,39 +454,25 @@ static int ubd_remove(char *str) { struct ubd *dev; - int n, err = -ENODEV; - if(isdigit(*str)){ - char *end; - n = simple_strtoul(str, &end, 0); - if ((*end != '\0') || (end == str)) - return(err); - } - else if (('a' <= *str) && (*str <= 'h')) - n = *str - 'a'; - else - return(err); /* it should be a number 0-7/a-h */ + dev = parse_unit(&str); + if (dev == NULL) + return (-ENODEV); /* could not find device */ - if((n < 0) || (n >= MAX_DEV)) - return(err); + if (dev->file == NULL) + return (-ENODEV); - dev = &ubd_dev[n]; + if (dev->count > 0) + return (-EBUSY); /* you cannot remove a open disk */ spin_lock(&ubd_lock); - err = 0; - if(dev->file == NULL) - goto out; - err = -1; - if(dev->count > 0) - goto out; - if(dev->devfs != NULL) + + if (dev->devfs) devfs_unregister(dev->devfs); *dev = ((struct ubd) DEFAULT_UBD); - err = 0; - out: spin_unlock(&ubd_lock); - return(err); + return (0); } static struct mc_device ubd_mc = { @@ -600,279 +485,265 @@ static int ubd_mc_init(void) { mconsole_register_dev(&ubd_mc); - return(0); + return (0); } __initcall(ubd_mc_init); static request_queue_t *ubd_get_queue(kdev_t device) { - return(ubd_queue); + return (ubd_queue); +} + +static void ubd_frame_register(struct gendisk *disk, struct ubd_frame *fp) +{ + int err, major = disk->major; + + err = devfs_register_blkdev(major, disk->major_name, &ubd_blops); + if (major == 0) { + /* auto device number case */ + major = err; + if (err == 0) { + return; + } + } else if (err) { + /* not auto so normal error */ + printk(KERN_ERR "ubd: error %d getting major %d\n", err, major); + return; + } + disk->major = major; + disk->real_devices = fp; + + blk_dev[major].queue = ubd_get_queue; + blk_size[major] = fp->sizes; + blksize_size[major] = fp->blk_sizes; + INIT_HARDSECT(hardsect_size, major, fp->hardsect_sizes); + add_gendisk(disk); +} + +static void ubd_frame_init(struct ubd_frame *fp, int n) +{ + int i; + struct gendisk *disk = &fp->gendisk; + + memset(fp, 0, sizeof(struct ubd_frame)); + + fp->base = n & ~((1 << UBD_SHIFT) - 1); + + disk->major = 0; + disk->major_name = fp->name; + snprintf(fp->name, sizeof(fp->name), "ubd%d", n >> UBD_SHIFT); + disk->minor_shift = UBD_SHIFT; + disk->max_p = 1 << UBD_SHIFT; + disk->fops = &ubd_blops; + disk->sizes = fp->sizes; + disk->part = fp->part; + disk->nr_real = 0; + disk->real_devices = fp; + disk->flags = 0; + disk->next = NULL; + disk->de_arr = 0; + + for (i = 0; i < NM; i++) { + fp->blk_sizes[i] = BLOCK_SIZE; + fp->hardsect_sizes[i] = 512; + } + for (i = 0; i < MAX_DEV; i++) { + struct ubd *dev = &fp->dev[i]; + dev->n = i; + dev->fp = fp; + } + ubd_frame_register(disk, fp); } int ubd_init(void) { unsigned long stack; - int i, err; + int i, err; ubd_dir_handle = devfs_mk_dir (NULL, "ubd", NULL); - if (devfs_register_blkdev(MAJOR_NR, "ubd", &ubd_blops)) { - printk(KERN_ERR "ubd: unable to get major %d\n", MAJOR_NR); - return -1; - } - read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */ - blksize_size[MAJOR_NR] = blk_sizes; - blk_size[MAJOR_NR] = sizes; - INIT_HARDSECT(hardsect_size, MAJOR_NR, hardsect_sizes); ubd_queue = BLK_DEFAULT_QUEUE(MAJOR_NR); blk_init_queue(ubd_queue, DEVICE_REQUEST); INIT_ELV(ubd_queue, &ubd_queue->elevator); + ubd_frame_register(&static_frame.gendisk, &static_frame); - add_gendisk(&ubd_gendisk); - if (fake_major != MAJOR_NR){ - /* major number 0 is used to auto select */ - err = devfs_register_blkdev(fake_major, "fake", &ubd_blops); - if(fake_major == 0){ - /* auto device number case */ - fake_major = err; - if(err == 0) - return(-ENODEV); - } - else if (err){ - /* not auto so normal error */ - printk(KERN_ERR "ubd: error %d getting major %d\n", - err, fake_major); - return(-ENODEV); - } - - blk_dev[fake_major].queue = ubd_get_queue; - read_ahead[fake_major] = 8; /* 8 sector (4kB) read-ahead */ - blksize_size[fake_major] = blk_sizes; - blk_size[fake_major] = sizes; - INIT_HARDSECT(hardsect_size, fake_major, hardsect_sizes); - add_gendisk(&fake_gendisk); + if (fake_major != MAJOR_NR) { + fakegendisk.major = fake_major; + fakegendisk.major_name = "ubd_fake"; + ubd_frame_register(&fakegendisk, &static_frame); } - for(i=0;in = i; + dev->fp = &static_frame; + ubd_add(dev); + } if(global_openflags.s){ printk(KERN_INFO "ubd : Synchronous mode\n"); return(0); } stack = alloc_stack(0, 0); - io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *), - &thread_fd); - if(io_pid < 0){ - printk(KERN_ERR - "ubd : Failed to start I/O thread (errno = %d) - " - "falling back to synchronous I/O\n", -io_pid); + io_pid = start_io_thread(stack + PAGE_SIZE - sizeof(void *), + &thread_fd); + if (io_pid < 0) { + printk(KERN_ERR "ubd : Failed to start I/O thread (errno = %d) - falling back to synchronous I/O\n", -io_pid); return(0); } - err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, ubd_intr, - SA_INTERRUPT, "ubd", ubd_dev); - if(err != 0) printk(KERN_ERR - "um_request_irq failed - errno = %d\n", -err); + err = um_request_irq(UBD_IRQ, thread_fd, IRQ_READ, &ubd_intr, + SA_INTERRUPT, "ubd", &static_frame); + if(err != 0) printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err); return(err); } __initcall(ubd_init); -static void ubd_close(struct ubd *dev) +static void ubd_close_dev(struct ubd *dev) { - os_close_file(dev->fd); - if(dev->cow.file != NULL) { - os_close_file(dev->cow.fd); - vfree(dev->cow.bitmap); - dev->cow.bitmap = NULL; + if (--dev->count == 0) { + if (dev->info) { + close_COW(dev->info); + dev->info = NULL; + } else { + printk(KERN_ERR"Closing a ubd device that was not open\n"); + } } } static int ubd_open_dev(struct ubd *dev) { - struct openflags flags; - int err, create_cow, *create_ptr; + int mode; dev->openflags = dev->boot_openflags; - create_cow = 0; - create_ptr = (dev->cow.file != NULL) ? &create_cow : NULL; - dev->fd = open_ubd_file(dev->file, &dev->openflags, &dev->cow.file, - &dev->cow.bitmap_offset, &dev->cow.bitmap_len, - &dev->cow.data_offset, create_ptr); - - if((dev->fd == -ENOENT) && create_cow){ - dev->fd = create_cow_file(dev->file, dev->cow.file, - dev->openflags, 1 << 9, - &dev->cow.bitmap_offset, - &dev->cow.bitmap_len, - &dev->cow.data_offset); - if(dev->fd >= 0){ - printk(KERN_INFO "Creating \"%s\" as COW file for " - "\"%s\"\n", dev->file, dev->cow.file); - } - } - if(dev->fd < 0) return(dev->fd); + if (dev->openflags.w) + mode = COW_CREATE; + else + mode = COW_RDONLY; - if(dev->cow.file != NULL){ - err = -ENOMEM; - dev->cow.bitmap = (void *) vmalloc(dev->cow.bitmap_len); - if(dev->cow.bitmap == NULL) goto error; - flush_tlb_kernel_vm(); - - err = read_cow_bitmap(dev->fd, dev->cow.bitmap, - dev->cow.bitmap_offset, - dev->cow.bitmap_len); - if(err) goto error; + if (!dev->info) + dev->info = open_COW(dev->file, mode); - flags = dev->openflags; - flags.w = 0; - err = open_ubd_file(dev->cow.file, &flags, NULL, NULL, NULL, - NULL, NULL); - if(err < 0) goto error; - dev->cow.fd = err; - } - return(0); - error: - os_close_file(dev->fd); - return(err); -} + if (dev->info == 0) + return(-ENODEV); -static int ubd_file_size(struct ubd *dev, __u64 *size_out) -{ - char *file; + dev->count++; - file = dev->cow.file ? dev->cow.file : dev->file; - return(os_file_size(file, size_out)); + return (0); } static int ubd_open(struct inode *inode, struct file *filp) { + struct gendisk *disk; + struct ubd_frame *fp; struct ubd *dev; int n, offset, err = 0; + disk = get_gendisk(inode->i_rdev); + if (!disk) { + printk(KERN_ERR "ubd_open got a unknown device\n"); + return (-ENODEV); + } + fp = disk->real_devices; + if (!fp) { + printk(KERN_ERR "ubd_open device had unkown frame\n"); + return (-ENODEV); + } n = DEVICE_NR(inode->i_rdev); - dev = &ubd_dev[n]; - if(n >= MAX_DEV) - return -ENODEV; + dev = &fp->dev[n]; spin_lock(&ubd_lock); offset = n << UBD_SHIFT; - if(dev->count == 0){ + if (dev->count == 0) { + dev->openflags = dev->boot_openflags; + err = ubd_open_dev(dev); - if(err){ - printk(KERN_ERR "ubd%d: Can't open \"%s\": " - "errno = %d\n", n, dev->file, -err); + if (err) { + printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n", + disk->major_name, dev->file, -err); goto out; } - err = ubd_file_size(dev, &dev->size); - if(err) - goto out; - sizes[offset] = dev->size / BLOCK_SIZE; - ubd_part[offset].nr_sects = dev->size / hardsect_sizes[offset]; - } - dev->count++; - if((filp->f_mode & FMODE_WRITE) && !dev->openflags.w){ - if(--dev->count == 0) ubd_close(dev); - err = -EROFS; + fp->sizes[offset] = dev->info->data_length / BLOCK_SIZE; + fp->part[offset].nr_sects = dev->info->data_length / fp->hardsect_sizes[offset]; + } else dev->count++; + if ((filp->f_mode & FMODE_WRITE) && !dev->openflags.w) { + ubd_close_dev(dev); + err = -EROFS; } - out: +out: spin_unlock(&ubd_lock); - return(err); + return (err); } static int ubd_release(struct inode * inode, struct file * file) { - int n, offset; + struct gendisk *disk; + struct ubd_frame *fp; + struct ubd *dev; + int n, offset; - n = DEVICE_NR(inode->i_rdev); + disk = get_gendisk(inode->i_rdev); + if (!disk) { + printk(KERN_ERR "ubd_open got a unknown device\n"); + return (-ENODEV); + } + fp = disk->real_devices; + if (!fp) { + printk(KERN_ERR "ubd_open device had unkown frame\n"); + return (-ENODEV); + } + n = DEVICE_NR(inode->i_rdev); offset = n << UBD_SHIFT; - if(n >= MAX_DEV) - return -ENODEV; + dev = &fp->dev[n]; spin_lock(&ubd_lock); - if(--ubd_dev[n].count == 0) - ubd_close(&ubd_dev[n]); + ubd_close_dev(dev); spin_unlock(&ubd_lock); return(0); } -void cowify_req(struct io_thread_req *req, struct ubd *dev) -{ - int i, update_bitmap, sector = req->offset >> 9; - - if(req->length > (sizeof(req->sector_mask) * 8) << 9) - panic("Operation too long"); - if(req->op == UBD_READ) { - for(i = 0; i < req->length >> 9; i++){ - if(ubd_test_bit(sector + i, (unsigned char *) - dev->cow.bitmap)){ - ubd_set_bit(i, (unsigned char *) - &req->sector_mask); - } - } - } - else { - update_bitmap = 0; - for(i = 0; i < req->length >> 9; i++){ - ubd_set_bit(i, (unsigned char *) - &req->sector_mask); - if(!ubd_test_bit(sector + i, (unsigned char *) - dev->cow.bitmap)) - update_bitmap = 1; - ubd_set_bit(sector + i, (unsigned char *) - dev->cow.bitmap); - } - if(update_bitmap){ - req->cow_offset = sector / (sizeof(unsigned long) * 8); - req->bitmap_words[0] = - dev->cow.bitmap[req->cow_offset]; - req->bitmap_words[1] = - dev->cow.bitmap[req->cow_offset + 1]; - req->cow_offset *= sizeof(unsigned long); - req->cow_offset += dev->cow.bitmap_offset; - } - } -} - static int prepare_request(struct request *req, struct io_thread_req *io_req) { + struct gendisk *disk; + struct ubd_frame *fp; struct ubd *dev; - __u64 block; - int nsect, minor, n; + int minor, n; if(req->rq_status == RQ_INACTIVE) return(1); + disk = get_gendisk(req->rq_dev); + if (!disk) { + printk(KERN_ERR "ubd_open got a unknown device\n"); + return (-ENODEV); + } + fp = disk->real_devices; + if (!fp) { + printk(KERN_ERR "ubd_open device had unkown frame\n"); + return (-ENODEV); + } minor = MINOR(req->rq_dev); n = minor >> UBD_SHIFT; - dev = &ubd_dev[n]; + dev = &fp->dev[n]; if(IS_WRITE(req) && !dev->openflags.w){ printk("Write attempted on readonly ubd device %d\n", n); end_request(0); return(1); } - req->sector += ubd_part[minor].start_sect; - block = req->sector; - nsect = req->current_nr_sectors; + req->sector += fp->part[minor].start_sect; io_req->op = (req->cmd == READ) ? UBD_READ : UBD_WRITE; - io_req->fds[0] = (dev->cow.file != NULL) ? dev->cow.fd : dev->fd; - io_req->fds[1] = dev->fd; - io_req->offsets[0] = 0; - io_req->offsets[1] = dev->cow.data_offset; - io_req->offset = ((__u64) block) << 9; - io_req->length = nsect << 9; + io_req->info = dev->info;; + io_req->offset = req->sector; + io_req->length = req->current_nr_sectors; io_req->buffer = req->buffer; io_req->sectorsize = 1 << 9; - io_req->sector_mask = 0; - io_req->cow_offset = -1; io_req->error = 0; - if(dev->cow.file != NULL) cowify_req(io_req, dev); return(0); } @@ -882,57 +753,66 @@ struct request *req; int err, n; - if(thread_fd == -1){ + if (thread_fd == -1) { while(!list_empty(&q->queue_head)){ req = blkdev_entry_next_request(&q->queue_head); err = prepare_request(req, &io_req); - if(!err){ + if (!err) { do_io(&io_req); ubd_finish(io_req.error); } } - } - else { - if(DEVICE_INTR || list_empty(&q->queue_head)) return; + } else { + if (DEVICE_INTR || list_empty(&q->queue_head)) + return; + req = blkdev_entry_next_request(&q->queue_head); err = prepare_request(req, &io_req); - if(!err){ + if (!err) { SET_INTR(ubd_handler); - n = write_ubd_fs(thread_fd, (char *) &io_req, - sizeof(io_req)); - if(n != sizeof(io_req)) + n = write_ubd_fs(thread_fd, (char *) &io_req, + sizeof(io_req)); + if (n != sizeof(io_req)) printk("write to io thread failed, " - "errno = %d\n", -n); + "errno = %d\n", -n); } } } static int ubd_ioctl(struct inode * inode, struct file * file, - unsigned int cmd, unsigned long arg) + unsigned int cmd, unsigned long arg) { - struct hd_geometry *loc = (struct hd_geometry *) arg; - struct ubd *dev; + struct gendisk *disk; + struct ubd_frame *fp; + struct ubd *dev; int n, minor, err; - struct hd_driveid ubd_id = { - .cyls = 0, - .heads = 128, - .sectors = 32, - }; + struct hd_geometry *loc = (struct hd_geometry *) arg; + struct hd_geometry g = {}; + struct hd_driveid ubd_id = {}; + struct cdrom_volctrl volume; - if(!inode) return(-EINVAL); + if (!inode) + return(-EINVAL); + + disk = get_gendisk(inode->i_rdev); + if (!disk) { + printk(KERN_ERR "ubd_open got a unknown device\n"); + return (-ENODEV); + } + fp = disk->real_devices; + if (!fp) { + printk(KERN_ERR "ubd_open device had unkown frame\n"); + return (-ENODEV); + } minor = MINOR(inode->i_rdev); n = minor >> UBD_SHIFT; - if(n >= MAX_DEV) - return(-EINVAL); - dev = &ubd_dev[n]; + dev = &fp->dev[n]; switch (cmd) { - struct hd_geometry g; - struct cdrom_volctrl volume; case HDIO_GETGEO: if(!loc) return(-EINVAL); - g.heads = 128; - g.sectors = 32; - g.cylinders = dev->size / (128 * 32 * hardsect_sizes[minor]); + g.heads = dev->info->heads; + g.sectors = dev->info->sectors; + g.cylinders = dev->info->cylinders; g.start = 2; return(copy_to_user(loc, &g, sizeof(g)) ? -EFAULT : 0); case BLKGETSIZE: /* Return device size */ @@ -940,7 +820,7 @@ err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long)); if(err) return(err); - put_user(ubd_part[minor].nr_sects, (long *) arg); + put_user(fp->part[minor].nr_sects, (long *) arg); return(0); case BLKRRPART: /* Re-read partition tables */ return(ubd_revalidate(inode->i_rdev)); @@ -970,9 +850,11 @@ return(0); case HDIO_GET_IDENTITY: - ubd_id.cyls = dev->size / (128 * 32 * hardsect_sizes[minor]); - if(copy_to_user((char *) arg, (char *) &ubd_id, - sizeof(ubd_id))) + ubd_id.cyls = dev->info->cylinders; + ubd_id.heads = dev->info->heads; + ubd_id.sectors = dev->info->sectors; + if(copy_to_user((char *) arg, (char *) &ubd_id, + sizeof(ubd_id))) return(-EFAULT); return(0); @@ -994,15 +876,27 @@ static int ubd_revalidate1(kdev_t rdev) { - int i, n, offset, err = 0, pcount = 1 << UBD_SHIFT; + struct gendisk *disk; + struct ubd_frame *fp; struct ubd *dev; struct hd_struct *part; + int i, n, offset, err = 0, pcount = 1 << UBD_SHIFT; + disk = get_gendisk(rdev); + if (!disk) { + printk(KERN_ERR "ubd_open got a unknown device\n"); + return (-ENODEV); + } + fp = disk->real_devices; + if (!fp) { + printk(KERN_ERR "ubd_open device had unkown frame\n"); + return (-ENODEV); + } n = DEVICE_NR(rdev); offset = n << UBD_SHIFT; - dev = &ubd_dev[n]; + dev = &fp->dev[n]; - part = &ubd_part[offset]; + part = &fp->part[offset]; /* clear all old partition counts */ for(i = 1; i < pcount; i++) { @@ -1010,38 +904,33 @@ part[i].nr_sects = 0; } - /* If it already has been opened we can check the partitions + /* If it already has been opened we can check the partitions * directly */ - if(dev->count){ + if (dev->count) { part->start_sect = 0; - register_disk(&ubd_gendisk, MKDEV(MAJOR_NR, offset), pcount, - &ubd_blops, part->nr_sects); - } - else if(dev->file){ + register_disk(disk, MKDEV(disk->major, offset), pcount, + &ubd_blops, part->nr_sects); + } else if (dev->file) { err = ubd_open_dev(dev); - if(err){ + if (err) { printk(KERN_ERR "unable to open %s for validation\n", - dev->file); + dev->file); goto out; } /* have to recompute sizes since we opened it */ - err = ubd_file_size(dev, &dev->size); - if(err) { - ubd_close(dev); - goto out; - } part->start_sect = 0; - part->nr_sects = dev->size / hardsect_sizes[offset]; - register_disk(&ubd_gendisk, MKDEV(MAJOR_NR, offset), pcount, - &ubd_blops, part->nr_sects); + part->nr_sects = dev->info->data_length / + fp->hardsect_sizes[offset]; + register_disk(disk, MKDEV(disk->major, offset), pcount, + &ubd_blops, part->nr_sects); /* we are done so close it */ - ubd_close(dev); + ubd_close_dev(dev); } else err = -ENODEV; - out: +out: return(err); } diff -Nur uml-2.4.20-5/arch/um/drivers/cow_user.c work-2.4.20-5/arch/um/drivers/cow_user.c --- uml-2.4.20-5/arch/um/drivers/cow_user.c 1969-12-31 16:00:00.000000000 -0800 +++ work-2.4.20-5/arch/um/drivers/cow_user.c 2003-05-26 23:48:28.000000000 -0700 @@ -0,0 +1,800 @@ +/* + * common cow file funcitons to be used by both uml_moo and ubd_user + * we really want 64 bit math on almost everything since otherwise + * we overflow at 2/4G or at 2^31 sectors ~2TiB which is starting to get + * to be easy to generate for real storage. For sparse files it has been + * possibile for some time. + * LICENSE GPLv2 + * James_McMechan at hotmail.com + * This includes the version 3 header I am working on + */ +#include /* stat... */ +#ifndef __USE_XOPEN +#define __USE_XOPEN +#endif +#ifndef __USE_LARGEFILE64 +#define __USE_LARGEFILE64 +#endif +#include /* stat... */ +#include /* pread/pwrite */ +#include /* MAXPATHLEN */ +#include /* __u32... */ +#include +#include +#include /* O_RDONLY etc for open */ +#include /* ntohl... */ +#include +#include +#include "cow.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 + +#ifdef TEST +#define TEST +#define printk printf +#define ERROR(format,f...) printf(format,##f) +#define DEBUG(format,f...) +static void *ubd_alloc(int size) { + void *memory = (void *) malloc(size); + DEBUG("allocated memory %p\n", memory); + return memory; +} + +static void ubd_free(void *memory) { + DEBUG("freeing memory %p\n", memory); + free(memory); +} +#else +#include "user.h" +#ifndef KERN_ERR +#define KERN_ERR "<3>" +#define KERN_INFO "<6>" +#endif +#define ERROR(format,f...) printk(KERN_ERR format,##f) +#define DEBUG(format,f...) +static void *ubd_alloc(int size) +{ + void *memory = (void *) um_kmalloc(size); + DEBUG("allocated memory %p\n", memory); + return memory; +} + +static void ubd_free(void *memory) +{ + DEBUG("freeing memory %p\n", memory); + kfree(memory); +} +#endif + +#define PATH_LEN_V1 256 +#define PATH_LEN_V2 MAXPATHLEN +#define PATH_LEN_V3 4096 + +#define VERSION_1 1 +#define VERSION_2 2 +#define VERSION_3 3 +#define CURRENT_VERSION 2 +#define COW_MAGIC 0x4f4f4f4d /* mooo */ + +#define SECTOR_SIZE 512 +#define MAP_SIZE 4096 +#define PAD_SIZE 512 +union cow_header { +#ifdef VERSION_1 + struct cow_header_v1 { + int magic, version; + char backing_file[PATH_LEN_V1]; + time_t mtime; + __u64 size; + int sectorsize; + } v1; +#define fillsize1 ((sizeof(struct cow_header_v1)+PAD_SIZE-1) & ~(PAD_SIZE-1)) + char space_v1[fillsize1]; +#endif +#ifdef VERSION_2 + struct cow_header_v2 { + int magic, version; + char backing_file[PATH_LEN_V2]; + time_t mtime; + __u64 size; + int sectorsize; + } v2; +#define fillsize2 ((sizeof(struct cow_header_v2)+PAD_SIZE-1) & ~(PAD_SIZE-1)) + char space_v2[fillsize2]; +#endif +#ifdef VERSION_3 + struct cow_header_v3 { + char magic[4]; + __u32 version; + __u64 cow_length, cow_offset; + __u64 data_length, data_offset; + __u64 name_length, name_offset; + __u64 mtime; + __u32 cylinders, heads, sectors, sector_size; + } v3; +#define fillsize3 ((sizeof(struct cow_header_v3)+PAD_SIZE-1) & ~(PAD_SIZE-1)) + char space_v3[fillsize3]; +#endif +}; + +char zero[SECTOR_SIZE] = {}; + +/* + * a bit shift of 3 is the log base 2 of 8 + * and is used to avoid havving to use the + * 64 bit math libraries also the function + * is half the size of the div/mod version + * also this really needs to use char * so + * that it will work with the same file data + * on both big and little endian machines. + */ +/* log2(sizeof(data)) unset to use div3/mod3 */ +#define bit_shift 3 +#ifdef bit_shift +#define divide8(x) ((x) >> bit_shift) +#else +#define divide8(x) ((x)/8) +#endif +static int ubd_test_bit(off64_t bit, unsigned char *data) +{ + __u64 index; + unsigned char off; + +#ifdef bit_shift + index = bit >> bit_shift; /* fast version of div3 */ + off = bit & ((1 << bit_shift)-1); /* bitmask with all const math */ + DEBUG("bit %llx data[%llx] = %02x & 1 << %d:%x\n",bit,index,data[index],off,1<> bit_shift; /* fast version of div3 */ + off = bit & ((1 << bit_shift)-1); /* bitmask with all const math */ + DEBUG("set %llx data[%llx] = %02x & 1 << %d:%x\n",bit,index,data[index],off,1< remaining) { + ERROR("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) { + ERROR("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); +} + +ssize_t close_COW(struct devinfo *dev) +{ + if (dev->flags & COW_DIRTY) { + ssize_t tmp; + DEBUG("closing %s fd %d buf %p[%d]:%x\n", dev->name, dev->fd, + dev->cow_bitmap, MAP_SIZE, dev->cow_offset + + divide8(dev->cow_current)); + tmp = pwrite64(dev->fd, dev->cow_bitmap, MAP_SIZE, + dev->cow_offset + divide8(dev->cow_current)); + if (tmp < MAP_SIZE) { + ERROR("Eek, short write closing %lu of %lu\n", tmp, + MAP_SIZE); + } + } + if (dev->next_cow_dev) + close_COW(dev->next_cow_dev); + close(dev->fd); + ubd_free(dev); +} + +struct devinfo *open_COW(char *cow_file, int in_flags) +{ + union cow_header *workspace; + struct devinfo tmp = {}, *result; + char *name, *cow_name = NULL; + int n, flags, version, mode; + struct stat64 buf; + off64_t count, offset, length; + + if (cow_file == NULL) { + ERROR("no file to open\n"); + return NULL; + } + + /* save passed in flags */ + tmp.flags = in_flags; + + name = strchr(cow_file,','); + if (name) { + *name = '\0'; /* this splits the string at the ',' */ + name++; + /* if it ends with a comma just dump it */ + if (*name == '\0') name = NULL; + } + /* we need a backing file to creat a COW file */ + if (!name) { + in_flags &= ~COW_CREATE; + } + + /* default mode for open command */ + mode = S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH; + if (in_flags & COW_RDONLY) { + flags = O_RDONLY; + } else { + flags = O_RDWR; + if (in_flags & COW_CREATE) { + flags |= O_CREAT; + } + } + + tmp.fd = open64(cow_file, flags, mode); + if (tmp.fd < 0) { + ERROR("unable to open %s\n",cow_file); + return NULL; + } + workspace = ubd_alloc(sizeof(union cow_header)); + if (!workspace) { + ERROR("unable to allocate space for reading header\n"); + return NULL; + } + n = read(tmp.fd,workspace,sizeof(union cow_header)); + if (n < sizeof(union cow_header)) { + memset(workspace, 0, sizeof(union cow_header)); + if (!(in_flags & COW_CREATE)) { + ERROR("short read on %s only got %d wanted %d error %s\n", cow_file, n, sizeof(union cow_header), strerror(errno)); + goto close1; + } + } + + version = 0; +#ifdef VERSION_1 + if (workspace->v1.magic == COW_MAGIC) { + if (workspace->v1.version == 1) { + version = 1; + cow_name = workspace->v1.backing_file; + tmp.data_length = workspace->v1.size; + tmp.mtime = workspace->v1.mtime; + tmp.sector_size = workspace->v1.sectorsize; + tmp.cow_offset = sizeof(struct cow_header_v1); + tmp.heads = 128; + tmp.sectors = 32; + tmp.cylinders = tmp.data_length / (tmp.heads * tmp.sectors * tmp.sector_size); + tmp.cow_length = (tmp.data_length + tmp.sector_size - 1) / tmp.sector_size; + tmp.data_offset = tmp.cow_offset + divide8(tmp.cow_length); + tmp.data_offset = (tmp.data_offset + tmp.sector_size - 1) / tmp.sector_size; + tmp.data_offset *= tmp.sector_size; + } + } +#endif +#ifdef VERSION_2 + if (ntohl(workspace->v2.magic) == COW_MAGIC) { + if (ntohl(workspace->v2.version) == 2) { + version = ntohl(workspace->v2.version); + cow_name = workspace->v2.backing_file; + tmp.data_length = ntohll(workspace->v2.size); + tmp.mtime = ntohl(workspace->v2.mtime); + tmp.sector_size = ntohl(workspace->v2.sectorsize); + tmp.cow_offset = sizeof(struct cow_header_v2); + tmp.heads = 128; + tmp.sectors = 32; + tmp.cylinders = tmp.data_length / (tmp.heads * tmp.sectors * tmp.sector_size); + tmp.cow_length = (tmp.data_length + tmp.sector_size - 1) / tmp.sector_size; + tmp.data_offset = tmp.cow_offset + divide8(tmp.cow_length); + tmp.data_offset = (tmp.data_offset + tmp.sector_size - 1) / tmp.sector_size; + tmp.data_offset *= tmp.sector_size; + } + } + #endif +#ifdef VERSION_3 + char magic[] = {'M','O','O','O'}; + if (memcmp(&workspace->v3.magic, &magic, sizeof(magic)) == 0) { + if (ntohl(workspace->v3.version) == 3) { + version = ntohl(workspace->v3.version); + tmp.data_offset = ntohll(workspace->v3.data_offset); + tmp.data_length = ntohll(workspace->v3.data_length); + tmp.mtime = ntohll(workspace->v3.mtime); + tmp.cow_offset = ntohll(workspace->v3.cow_offset); + tmp.cow_length = ntohll(workspace->v3.cow_length); + tmp.cylinders = ntohl(workspace->v3.cylinders); + tmp.heads = ntohl(workspace->v3.heads); + tmp.sectors = ntohl(workspace->v3.sectors); + tmp.sector_size = ntohl(workspace->v3.sector_size); + offset = ntohll(workspace->v3.name_offset); + length = ntohll(workspace->v3.name_length); + if (offset + length < sizeof(union cow_header)) { + cow_name = (char *) workspace; + cow_name += offset; + } else { + count = sizeof(struct cow_header_v3) + length; + ubd_free(workspace); + workspace = ubd_alloc(count); + n = pread64(tmp.fd, workspace, count, (off64_t) 0); + if (n < count) { + ERROR("unable to read name from COW file %s\n", cow_file); + return NULL; + } + cow_name = (char *) workspace; + cow_name += offset; + } + } + } +#endif + + /* if no COW file header */ + if (version == 0) { + /* if create a COW file */ + if (in_flags & COW_CREATE || !name) { + /* if no backing file this is the disk image file */ + if (name == NULL) { + tmp.sector_size = SECTOR_SIZE; + tmp.cow_length = 0; + tmp.cow_offset = 0; + if (fstat64(tmp.fd, &buf) < 0) { + ERROR("problem stating file %s %s\n", + cow_file, strerror(errno)); + goto close1; + } + tmp.data_length = buf.st_size; + tmp.mtime = buf.st_mtime; + tmp.data_offset = 0; + tmp.heads = 128; + tmp.sectors = 32; + tmp.cylinders = tmp.data_length / (tmp.heads * tmp.sectors * tmp.sector_size); + tmp.next_cow_dev = NULL; + } else { + /* + * we have a backing file to try + * here we should check backing_file + */ + in_flags &= ~COW_CREATE; + in_flags |= COW_RDONLY; + result = open_COW(name,in_flags); + if (result) { + char magic[] = {'M','O','O','O'}; + struct cow_header_v3 *v3 = &workspace->v3; + /* copy in the MAGIC "MOOO" */ + memcpy(&v3->magic, &magic, sizeof(magic)); + /* a version 3 header */ + version = 3; + v3->version = htonl(version); + + /* cylinders for CHS */ + count = result->cylinders; + v3->cylinders = htonl(count); + tmp.cylinders = count; + + /* heads for CHS */ + count = result->heads; + v3->heads = htonl(count); + tmp.heads = count; + + /* sectors for CHS */ + count = result->sectors; + v3->sectors = htonl(count); + tmp.sectors = count; + + /* sector size for all calcs */ + count = result->sector_size; + v3->sector_size = htonl(count); + tmp.sector_size = count; + + /* backing file length */ + count = result->data_length; + v3->data_length = htonll(count); + tmp.data_length = count; + + /* convert to sector count */ + count += result->sector_size-1; + count /= result->sector_size; + + /* save offsets and length */ + v3->cow_offset = htonll(fillsize3); + v3->cow_length = htonll(count); + tmp.cow_offset = fillsize3; + tmp.cow_length = count; + + /* convert to byte count */ + count += 8-1; + count = divide8(count); + + /* now add in the PAD_SIZE */ + count += (PAD_SIZE-1); + count &= ~(PAD_SIZE-1); + + /* add in length of the header */ + count += fillsize3; + v3->data_offset = htonll(count); + tmp.data_offset = count; + + /* save backing file name */ + cow_name = (char *) v3; + cow_name += sizeof(struct cow_header_v3); + offset = cow_name - (char *) v3; + v3->name_offset = htonll(offset); + count = sizeof(union cow_header) - offset; + absolutize(cow_name, count, name); + length = strlen(cow_name); + v3->name_length = htonll(length); + if (offset + length > sizeof(union cow_header)) { + ERROR("you have a name %s that will not fit in the default space of %d\n", name, sizeof(union cow_header)); + return NULL; + } + + /* save the backing file time */ + count = result->mtime; + v3->mtime = htonll(count); + tmp.mtime = count; + + tmp.next_cow_dev = result; + /* if we are creating a plain file */ + if (in_flags & COW_NOMAP) { + tmp.data_offset = 0; + tmp.cow_offset = 0; + tmp.cow_length = 0; + } else { + /* its a COW file */ + /* so write header */ + count = offset + length; + DEBUG("header %s fd %d buf " "%p[%d]\n", cow_file, tmp.fd, workspace, count); + pwrite64(tmp.fd, &workspace->v3, count, (off64_t) 0); + } + count = tmp.data_offset; + count += tmp.data_length; + count -= sizeof(zero); + DEBUG("seek %s fd %d buf %p[%d]:%x\n", + cow_file, tmp.fd, zero, + sizeof(zero), count); + pwrite64(tmp.fd, &zero, sizeof(zero), (off64_t) count); + } else { + ERROR("failed to open backing file %s not creating COW file %s\n", name, cow_file); + goto close1; + } + } + } else { + /* dont create so error instead */ + ERROR("unable to find a COW file %s for %s\n", + cow_file, name); + goto close1; + } + } + + /* if any version cow file do the checks */ + if (version) { + /* first recuse down the chain */ + if (!tmp.next_cow_dev) { + if (!name) { + name = cow_name; + } + if (strcmp(cow_name, name)) { + ERROR("filename %s is not the backing file of %s\n" , name, cow_name); + } + /* if we have data the lower layer is RO */ + /* else without data a lower layer is RW */ + if (tmp.data_length) { + in_flags &= ~COW_CREATE; + in_flags |= COW_RDONLY; + } + tmp.next_cow_dev = open_COW(name, in_flags); + + /* if we did not open the specified file ? */ + if (!tmp.next_cow_dev) { + ERROR("failed to open %s as backing for %s\n", name, cow_file); + goto close1; + } + } + /* if we dont have data don't worry about matching */ + /* e.g. a COW file without data can just set C/H/S */ + if (tmp.data_length) { + if (name && strcmp(cow_name, name)) { + ERROR("filename %s is not the backing file of %s\n" , name, cow_name); + /* this error is not fatal */ + } + if (tmp.mtime != tmp.next_cow_dev->mtime) { + ERROR("bad mtimes %llu != %llu\n" , tmp.mtime, tmp.next_cow_dev->mtime); + goto close1; + } + if (tmp.data_length != tmp.next_cow_dev->data_length) { + ERROR("bad data length %llu != %llu\n", tmp.data_length, tmp.next_cow_dev->data_length); + goto close1; + } + } + } + + count = sizeof(tmp); + count += strlen(cow_file)+1; + if (tmp.cow_length) count += MAP_SIZE; + result = ubd_alloc(count); + + if (!result) { + ERROR("failed to allocate device structure for %s\n",cow_file); + goto close1; + } + + memcpy(result, &tmp, sizeof(tmp)); + result->name = (char *)&result[1]; + strcpy(result->name, cow_file); + if (result->cow_length) { + /* put the map page just after the dev struct */ + result->cow_bitmap = &result->name[strlen(cow_file)+1]; + result->cow_current = 0; + DEBUG("setup %s fd %d buf %p[%d]:%x\n", result->name, + result->fd, result->cow_bitmap, MAP_SIZE, + result->cow_offset + divide8(result->cow_current)); + n = pread64(result->fd, result->cow_bitmap, MAP_SIZE, + result->cow_offset + divide8(result->cow_current)); + if (n < MAP_SIZE) { + ERROR("failed to read COW data %d of %d error %s\n", + n, MAP_SIZE, strerror(errno)); + goto free; + } + } + return result; +free: + if (result != NULL) { + ubd_free(result); + } +close1: + if (workspace) ubd_free(workspace); + if (tmp.fd >= 0) { + if (tmp.next_cow_dev) { + close_COW(tmp.next_cow_dev); + } + close(tmp.fd); + } + return NULL; +} + +/* compute map index */ +static int remap_COW(struct devinfo *dev, off64_t offset) +{ + int result; + off64_t index, offset2; + + if (offset >= dev->cow_length) { + ERROR("access %llu past end of COW %llu\n", offset, + dev->cow_length); + return -1; /* error no data */ + } + + index = offset - dev->cow_current; + + if (index < 0 || (MAP_SIZE * 8) <= index) { + if (dev->flags & COW_DIRTY) { + offset2 = dev->cow_offset + divide8(dev->cow_current); + + DEBUG("dirty %s fd %d buf %p[%d]:%x\n", dev->name, + dev->fd, dev->cow_bitmap, MAP_SIZE, + offset2); + result = pwrite64(dev->fd, dev->cow_bitmap, MAP_SIZE, + offset2); + if (result < MAP_SIZE) { + ERROR("Eek, short write in COW %lu of %lu\n", + result, MAP_SIZE); + return -1; + } + } + dev->flags &= ~COW_DIRTY; + + dev->cow_current = offset & ~((MAP_SIZE * 8)-1); + offset2 = dev->cow_offset + divide8(dev->cow_current); + + DEBUG("mapping %s fd %d buf %p[%d]:%x\n", dev->name, dev->fd, + dev->cow_bitmap, MAP_SIZE, offset2); + result = pread64(dev->fd, dev->cow_bitmap, MAP_SIZE, offset2); + if (result < MAP_SIZE) { + ERROR("failed to read COW data %d of %d error %s\n", + result, MAP_SIZE, strerror(errno)); + return -1; /* um we died */ + } + /* recompute map offset */ + index = offset - dev->cow_current; + } + return index; +} + +ssize_t read_COW(struct devinfo *dev, void *buf, size_t length, __u64 offset) +{ + off64_t work = dev->data_offset + (offset * dev->sector_size); + ssize_t count, result = 0; + struct devinfo *back_dev = dev->next_cow_dev; + + while (length) { + if (dev->cow_length) { + /* compute map index */ + int index = remap_COW(dev,offset); + + if (index < 0 ) { + return result; + } + + if (ubd_test_bit(index, dev->cow_bitmap)) { + DEBUG("backed %s fd %d buf %p[%d]:%x\n", + dev->name, + dev->fd, buf, + dev->sector_size, work); + count = pread64(dev->fd, buf, dev->sector_size, + work); + goto one_done; + } + } + /* recurse down the chain to the backing files */ + if (back_dev) { + count = read_COW(back_dev, buf, 1, offset); + count *= dev->sector_size; + } else { + DEBUG("reading %s fd %d buf %p[%d]:%x\n", + dev->name, + dev->fd, buf, + dev->sector_size, offset); + count = pread64(dev->fd, buf, dev->sector_size, work); + } +one_done: + if (count < dev->sector_size) { + ERROR("short read on %d:%d only got %d wanted %d " + "error %s\n", dev->fd, back_dev->fd, + count, dev->sector_size, strerror(errno)); + return result; + } + buf += count; + work += count; + result++; + length--; + offset++; + } + return result; +} + +ssize_t write_COW(struct devinfo *dev, void *buf, size_t length, __u64 offset) +{ + off64_t work = dev->data_offset + (offset * dev->sector_size); + ssize_t count, result = 0; + + while (length) { + if (dev->cow_length) { + /* compute map index */ + int index = remap_COW(dev,offset); + + if (index < 0 ) { + return result; + } + + ubd_set_bit(index, dev->cow_bitmap); + /* write out to the data section */ + DEBUG("dataout %s fd %d buf %p[%d]:%x\n", dev->name, dev->fd, buf, dev->sector_size, work); + count = pwrite64(dev->fd, buf, dev->sector_size, work); + dev->flags |= COW_DIRTY; + goto one_done; + } + /* ok this is the sparse calculation it is used to merge the + * COW file with a backing file. + * First we check the SPARSE flag should be set only when + * running as uml_moo. + * 2nd we check if the beffer to be writen is all zeros + * if it was all zeros then read the backing file to find if + * the base file is also all zeros also this will handle the + * case where zeros are over writen in the COW file on top + * of a non-zero backing file sector. + */ + if ((dev->flags & COW_SPARSE) && (memcmp(buf, zero, dev->sector_size) == 0)) { + count = read_COW(dev->next_cow_dev, buf, 1, offset); + count *= dev->sector_size; + if (count == dev->sector_size) { + if (memcmp(buf, zero, dev->sector_size) == 0) { + goto one_done; + } + DEBUG("zero %s fd %d buf %p[%d]:%x\n", dev->name, dev->fd, zero, dev->sector_size, work); + count = pwrite64(dev->fd, zero, dev->sector_size, work); + if (count < dev->sector_size) { + ERROR("short write zero %d:buf[%p]+%xl:%d", dev->fd, zero, work, dev->sector_size); + return result; + } + goto one_done; + } else + ERROR("did not read file for zero check\n"); + } + DEBUG("writeout %s fd %d buf %p[%d]:%x\n", dev->name, dev->fd, buf, dev->sector_size, work); + count = pwrite64(dev->fd, buf, dev->sector_size, work); + if (count < dev->sector_size) { + ERROR("write %d:buf[%p]+%xl:%d",dev->fd, buf, work, dev->sector_size); + return result; + } +one_done: + result++; + buf += count; + work += count; + length--; + offset++; + } + return result; +} + +#ifdef TEST +struct devinfo *fd; +char *buffer; +main(int argc,char *argv[]) +{ + char *name; + int i, nr, nw; + +#define COUNT 10 + buffer = ubd_alloc(SECTOR_SIZE * COUNT); + if (!buffer) { + printf("error unable to alloc enough buffer\n"); + return 1; + } + + if (argc > 1) { + fd = open_COW(argv[1],COW_CREATE|COW_SPARSE|COW_NOMAP); + } else { + printf("%s is used with MOOFILE,COWFILE\n",argv[0]); + } + printf("fd = %p\n",fd); + if (fd) { + i = 0; + do { + nr = read_COW(fd, buffer, COUNT, i); + nw= write_COW(fd, buffer, nr, i); + i += nr; + if (nr != nw ) { + printf("error get %d wrote %d\n",nr,nw); + break; + } + } while (nr); + + close_COW(fd); + } + return 0; +} +#endif + diff -Nur uml-2.4.20-5/arch/um/drivers/cow.h work-2.4.20-5/arch/um/drivers/cow.h --- uml-2.4.20-5/arch/um/drivers/cow.h 1969-12-31 16:00:00.000000000 -0800 +++ work-2.4.20-5/arch/um/drivers/cow.h 2003-05-26 17:02:21.000000000 -0700 @@ -0,0 +1,19 @@ +#define COW_RDONLY 1 +#define COW_CREATE 2 +#define COW_DIRTY 4 +#define COW_SPARSE 8 +#define COW_NOMAP 16 +struct devinfo { + char *name; + __u64 cow_length, cow_offset, cow_current; + __u64 data_length, data_offset; + char *cow_bitmap; + struct devinfo *next_cow_dev; + int fd,flags; + int cylinders, heads, sectors, sector_size; + time_t mtime; +}; +struct devinfo *open_COW(char *cow_file, int in_flags); +ssize_t close_COW(struct devinfo *dev); +ssize_t read_COW(struct devinfo *dev, void *buf, size_t length, __u64 offset); +ssize_t write_COW(struct devinfo *dev, void *buf, size_t count, __u64 offset); diff -Nur uml-2.4.20-5/arch/um/drivers/Makefile work-2.4.20-5/arch/um/drivers/Makefile --- uml-2.4.20-5/arch/um/drivers/Makefile 2003-05-23 07:02:29.000000000 -0700 +++ work-2.4.20-5/arch/um/drivers/Makefile 2003-05-26 17:02:21.000000000 -0700 @@ -18,7 +18,7 @@ net-objs := net_kern.o net_user.o mconsole-objs := mconsole_kern.o mconsole_user.o hostaudio-objs := hostaudio_kern.o hostaudio_user.o -ubd-objs := ubd_kern.o ubd_user.o +ubd-objs := ubd_kern.o ubd_user.o cow_user.o port-objs := port_kern.o port_user.o harddog-objs := harddog_kern.o harddog_user.o diff -Nur uml-2.4.20-5/arch/um/include/ubd_user.h work-2.4.20-5/arch/um/drivers/../include/ubd_user.h --- uml-2.4.20-5/arch/um/include/ubd_user.h 2003-05-23 07:02:30.000000000 -0700 +++ work-2.4.20-5/arch/um/include/ubd_user.h 2003-05-26 17:02:22.000000000 -0700 @@ -13,56 +13,19 @@ struct io_thread_req { enum ubd_req op; - int fds[2]; - unsigned long offsets[2]; unsigned long long offset; unsigned long length; char *buffer; + struct devinfo *info; int sectorsize; - unsigned long sector_mask; - unsigned long cow_offset; - unsigned long bitmap_words[2]; int error; }; -extern 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, - int *create_cow_out); -extern int create_cow_file(char *cow_file, char *backing_file, - struct openflags flags, int sectorsize, - int *bitmap_offset_out, - unsigned long *bitmap_len_out, - int *data_offset_out); -extern int read_cow_bitmap(int fd, void *buf, int offset, int len); extern int read_ubd_fs(int fd, void *buffer, int len); extern int write_ubd_fs(int fd, char *buffer, int len); extern int start_io_thread(unsigned long sp, int *fds_out); extern void do_io(struct io_thread_req *req); -static inline int ubd_test_bit(__u64 bit, unsigned char *data) -{ - __u64 n; - int bits, off; - - bits = sizeof(data[0]) * 8; - n = bit / bits; - off = bit % bits; - return((data[n] & (1 << off)) != 0); -} - -static inline void ubd_set_bit(__u64 bit, unsigned char *data) -{ - __u64 n; - int bits, off; - - bits = sizeof(data[0]) * 8; - n = bit / bits; - off = bit % bits; - data[n] |= (1 << off); -} - - #endif /*