diff -Nur uml-2.5.69-1/arch/um/drivers/ubd_user.c work-2.5.69-1/arch/um/drivers/ubd_user.c --- uml-2.5.69-1/arch/um/drivers/ubd_user.c 2003-05-16 21:57:44.000000000 -0700 +++ work-2.5.69-1/arch/um/drivers/ubd_user.c 2003-05-26 17:16:36.000000000 -0700 @@ -24,6 +24,7 @@ #include "user.h" #include "ubd_user.h" #include "os.h" +#include "cow.h" #include #include @@ -37,411 +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; @@ -462,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.5.69-1/arch/um/drivers/ubd_kern.c work-2.5.69-1/arch/um/drivers/ubd_kern.c --- uml-2.5.69-1/arch/um/drivers/ubd_kern.c 2003-05-16 21:57:44.000000000 -0700 +++ work-2.5.69-1/arch/um/drivers/ubd_kern.c 2003-05-26 23:39:04.000000000 -0700 @@ -3,17 +3,13 @@ * 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 - * 2002-09-27...2002-10-18 massive tinkering for 2.5 - * partitions have changed in 2.5 - * 2003-01-29 more tinkering for 2.5.59-1 - * This should now address the sysfs problems and has - * the symlink for devfs to allow for booting with - * the common /dev/ubd/discX/... names rather than - * only /dev/ubdN/discN this version also has lots of - * clean ups preparing for ubd-many. +/* 2001-09-28... + * Partition stuff by James_McMechan at hotmail.com + * update for the sysfs problems and has the symlink for + * devfs to allow for booting with the common /dev/ubd/discX/ + * names rather than /dev/ubdN/discN + * clean ups for ubd-many. + * Also the Stackable COW files and fake_major is working again * James McMechan */ @@ -52,6 +48,7 @@ #include "ubd_user.h" #include "2_5compat.h" #include "os.h" +#include "cow.h" static spinlock_t ubd_io_lock = SPIN_LOCK_UNLOCKED; static spinlock_t ubd_lock = SPIN_LOCK_UNLOCKED; @@ -61,18 +58,18 @@ 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); -#define MAX_DEV (8) -#define MAX_MINOR (MAX_DEV << UBD_SHIFT) - -#define DEVICE_NR(n) (minor(n) >> UBD_SHIFT) +/* 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 = { - .owner = THIS_MODULE, - .open = ubd_open, - .release = ubd_release, - .ioctl = ubd_ioctl, + .owner = THIS_MODULE, + .open = ubd_open, + .release = ubd_release, + .ioctl = ubd_ioctl, }; /* Protected by the queue_lock */ @@ -81,15 +78,16 @@ /* Protected by ubd_lock */ static int fake_major = MAJOR_NR; -static struct gendisk *ubd_gendisk[MAX_DEV]; -static struct gendisk *fake_gendisk[MAX_DEV]; - +/* all that is now needed for fake devices is the gendisk pointer */ +struct gendisk *fakegendisk[MAX_FAKE] = {[ 0 ... MAX_FAKE - 1] = NULL }; + + #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 @@ -97,201 +95,153 @@ */ 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 gendisk *disk; + struct ubd_frame *fp; + int count, n; struct openflags boot_openflags; struct openflags openflags; - 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, \ - .cow = DEFAULT_COW, \ -} - -struct ubd ubd_dev[MAX_DEV] = { [ 0 ... MAX_DEV - 1 ] = DEFAULT_UBD }; + .file = NULL, \ + .info = NULL, \ + .fp = NULL, \ + .count = 0, \ + .n = 0, \ + .boot_openflags = OPEN_FLAGS, \ + .openflags = OPEN_FLAGS, \ + .disk = NULL, \ +} + +static struct ubd_frame { + struct ubd_frame *next_frame; + int base, major; + struct ubd dev[MAX_DEV]; +} static_frame = { + .next_frame = NULL, + .base = 0, + .major = MAJOR_NR, + .dev = { [0 ... (MAX_DEV - 1)] = DEFAULT_UBD }, +}; 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(proc_ide_root == NULL) make_proc_ide(); - - 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) { + int i, major; + fp = &static_frame; + /* major number 0 is used for dynamic numbering */ + major = register_blkdev(0, "ubd"); + /* auto device number */ + if (major == 0) { + printk(KERN_ERR "ubd: error getting dynamic major\n"); + return NULL; + } + 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; + } + fp->next_frame = 0; + fp->major = major; + fp->base = n & ~((1 << UBD_SHIFT) - 1); + for (i = 0; i < MAX_DEV; i++) { + fp->dev[i] = ((struct ubd) DEFAULT_UBD); + } + } + 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; + struct ubd *dev = NULL; struct openflags flags = global_openflags; - 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){ - printk(KERN_ERR "Can't assign a fake major twice\n"); - goto out1; - } - - fake_major = major; + spin_lock(&ubd_lock); + if (fake_major != MAJOR_NR) { + printk(KERN_ERR "Can't assign a fake major twice\n"); + goto out1; + } + + fake_major = major; printk(KERN_INFO "Setting extra ubd major number to %d\n", - major); - err = 0; - out1: - spin_unlock(&ubd_lock); - return(err); + major); +out1: + spin_unlock(&ubd_lock); + 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++; @@ -305,23 +255,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); } @@ -340,21 +283,9 @@ " to be written to disk on the host immediately.\n\n" ); -static int fakehd_set = 0; -static int fakehd(char *str) -{ - printk(KERN_INFO "fakehd : Changing ubd name to \"hd\".\n"); - fakehd_set = 1; - 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); +static int ubd_open_dev(struct ubd *dev); +static void ubd_close_dev(struct ubd *dev); /* Only changed by ubd_init, which is an initcall. */ int thread_fd = -1; @@ -369,9 +300,9 @@ int nsect; if(error){ - spin_lock(&ubd_io_lock); + spin_lock(&ubd_io_lock); end_request(req, 0); - spin_unlock(&ubd_io_lock); + spin_unlock(&ubd_io_lock); return; } nsect = req->current_nr_sectors; @@ -395,16 +326,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(&ubd_io_lock); end_request(rq, 0); spin_unlock(&ubd_io_lock); return; } - - if((req.offset != ((__u64) (rq->sector)) << 9) || - (req.length != (rq->current_nr_sectors) << 9)) + + if ((req.offset != (__u64) (rq->sector)) || + (req.length != (rq->current_nr_sectors))) panic("I/O op mismatch"); ubd_finish(rq, req.error); @@ -423,83 +353,13 @@ void kill_io_thread(void) { - if(io_pid != -1) + if (io_pid != -1) os_kill_process(io_pid, 1); } __uml_exitcall(kill_io_thread); -static int ubd_file_size(struct ubd *dev, __u64 *size_out) -{ - char *file; - - file = dev->cow.file ? dev->cow.file : dev->file; - return(os_file_size(file, size_out)); -} - -static void ubd_close(struct ubd *dev) -{ - os_close_file(dev->fd); - if(dev->cow.file == NULL) - return; - - os_close_file(dev->cow.fd); - vfree(dev->cow.bitmap); - dev->cow.bitmap = NULL; -} - -static int ubd_open_dev(struct ubd *dev) -{ - struct openflags flags; - int err, create_cow, *create_ptr; - - 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->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; - - 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); -} - -static int ubd_new_disk(int major, u64 size, int unit, - struct gendisk **disk_out) +static int ubd_new_disk(int major, struct ubd *dev, struct gendisk **disk_out) { struct gendisk *disk; @@ -507,29 +367,32 @@ int err; disk = alloc_disk(1 << UBD_SHIFT); - if(disk == NULL) + if (disk == NULL) return(-ENOMEM); disk->major = major; - disk->first_minor = unit << UBD_SHIFT; + disk->first_minor = (dev->n - dev->fp->base) << UBD_SHIFT; disk->fops = &ubd_blops; - set_capacity(disk, size / 512); - if(major == MAJOR_NR){ - sprintf(disk->disk_name, "ubd%d", unit); - sprintf(disk->devfs_name, "ubd/disc%d", unit); - sprintf(from, "ubd/%d", unit); - sprintf(to, "disc%d/disc", unit); - err = devfs_mk_symlink(from, to); - if(err) - printk("ubd_new_disk failed to make link from %s to " - "%s, error = %d\n", from, to, err); - } - else { - sprintf(disk->disk_name, "ubd_fake%d", unit); - sprintf(disk->devfs_name, "ubd_fake/disc%d", unit); + set_capacity(disk, dev->info->data_length / 512); + if (dev->n >= MAX_FAKE || major == MAJOR_NR) { + sprintf(disk->disk_name, "ubd%d", dev->n); + sprintf(disk->devfs_name, "ubd/disc%d", dev->n); + if (dev->n < MAX_FAKE) { + sprintf(from, "ubd/%d", dev->n); + sprintf(to, "disc%d/disc", dev->n); + err = devfs_mk_symlink(from, to); + if (err) { + printk("ubd_new_disk failed to make link from " + "%s to %s, error = %d\n", from, + to, err); + } + } + } else { + sprintf(disk->disk_name, "ubd_fake%d", dev->n); + sprintf(disk->devfs_name, "ubd_fake/disc%d", dev->n); } - disk->private_data = &ubd_dev[unit]; + disk->private_data = dev; disk->queue = &ubd_queue; add_disk(disk); @@ -537,59 +400,49 @@ return 0; } -static int ubd_add(int n) +static int ubd_add(struct ubd *dev) { - struct ubd *dev = &ubd_dev[n]; - int err; + int err = -ENODEV; if(dev->file == NULL) - return(-ENODEV); + return (err); if (ubd_open_dev(dev)) - return(-ENODEV); + goto out; - err = ubd_file_size(dev, &dev->size); - if(err) - return(err); + err = ubd_new_disk(dev->fp->major, dev, &dev->disk); + if (err) + goto out; - err = ubd_new_disk(MAJOR_NR, dev->size, n, &ubd_gendisk[n]); - if(err) - return(err); - - if(fake_major != MAJOR_NR) - ubd_new_disk(fake_major, dev->size, n, - &fake_gendisk[n]); - - /* perhaps this should also be under the "if (fake_major)" above */ - /* using the fake_disk->disk_name and also the fakehd_set name */ - if (fake_ide) - make_ide_entries(ubd_gendisk[n]->disk_name); + if (dev->n < MAX_FAKE && (fake_major != MAJOR_NR)) + ubd_new_disk(fake_major, dev, &fakegendisk[dev->n]); - ubd_close(dev); - return 0; +out: + ubd_close_dev(dev); + 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); + spin_lock(&ubd_lock); + err = ubd_add(dev); if(err) - ubd_dev[n].file = NULL; - spin_unlock(&ubd_lock); + dev->file = NULL; + spin_unlock(&ubd_lock); return(err); } @@ -597,21 +450,14 @@ static int ubd_get_config(char *name, char *str, int size, char **error_out) { struct ubd *dev; - char *end; - int n, 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); - } + int len = 0; - 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){ @@ -621,13 +467,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); } @@ -635,82 +481,129 @@ static int ubd_remove(char *str) { struct ubd *dev; - int n, err = -ENODEV; - n = parse_unit(&str); + 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 */ + return (-EBUSY); /* you cannot remove a open disk */ - err = 0; - spin_lock(&ubd_lock); + spin_lock(&ubd_lock); - if(ubd_gendisk[n] == NULL) - goto out; + if (dev->disk) { + del_gendisk(dev->disk); + put_disk(dev->disk); + dev->disk = NULL; + } - del_gendisk(ubd_gendisk[n]); - put_disk(ubd_gendisk[n]); - ubd_gendisk[n] = NULL; - - if(fake_gendisk[n] != NULL){ - del_gendisk(fake_gendisk[n]); - put_disk(fake_gendisk[n]); - fake_gendisk[n] = NULL; + if (dev->n < MAX_FAKE && fakegendisk[dev->n] != NULL) { + del_gendisk(fakegendisk[dev->n]); + put_disk(fakegendisk[dev->n]); + fakegendisk[dev->n] = NULL; } *dev = ((struct ubd) DEFAULT_UBD); - err = 0; - out: - spin_unlock(&ubd_lock); - return(err); + spin_unlock(&ubd_lock); + return (0); } static struct mc_device ubd_mc = { .name = "ubd", .config = ubd_config, - .get_config = ubd_get_config, + .get_config = ubd_get_config, .remove = ubd_remove, }; static int ubd_mc_init(void) { mconsole_register_dev(&ubd_mc); - return 0; + return (0); } __initcall(ubd_mc_init); int ubd_init(void) { - int i; + int i; devfs_mk_dir("ubd"); - if (register_blkdev(MAJOR_NR, "ubd")) + if (register_blkdev(MAJOR_NR, "ubd")) { + printk(KERN_ERR "ubd: unable to get major %d\n", MAJOR_NR); return -1; + } blk_init_queue(&ubd_queue, do_ubd_request, &ubd_io_lock); elevator_init(&ubd_queue, &elevator_noop); if (fake_major != MAJOR_NR) { - char name[sizeof("ubd_nnn\0")]; + int err; - snprintf(name, sizeof(name), "ubd_%d", fake_major); - devfs_mk_dir(name); - if (register_blkdev(fake_major, "ubd")) - return -1; + /* major number 0 is used for dynamic numbering */ + err = register_blkdev(fake_major, "fake"); + if (fake_major == 0) { + /* auto device number */ + fake_major = err; + if (fake_major == 0) { + printk(KERN_ERR "ubd: error getting dynamic fake major\n"); + return -ENODEV; + } + } else if (err) { + /* not auto so normal error process */ + printk(KERN_ERR "ubd: error %d getting fake major number %d\n", err, fake_major); + return -ENODEV; + } + } + for (i = 0; i < MAX_DEV; i++) { + struct ubd *dev = &static_frame.dev[i]; + dev->n = i; + dev->fp = &static_frame; + ubd_add(dev); } - for (i = 0; i < MAX_DEV; i++) - ubd_add(i); return 0; } late_initcall(ubd_init); -int ubd_driver_init(void){ +static void ubd_close_dev(struct ubd *dev) +{ + 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) +{ + int mode; + + dev->openflags = dev->boot_openflags; + + if (dev->openflags.w) + mode = COW_CREATE; + else + mode = COW_RDONLY; + + if (!dev->info) + dev->info = open_COW(dev->file, mode); + + if (dev->info == 0) + return(-ENODEV); + + dev->count++; + + return (0); +} + +int ubd_driver_init(void) +{ unsigned long stack; int err; @@ -719,18 +612,15 @@ 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); } @@ -742,21 +632,22 @@ struct ubd *dev = disk->private_data; int err = 0; - if(dev->count == 0){ + if (dev->count == 0) { + dev->openflags = dev->boot_openflags; + err = ubd_open_dev(dev); - if(err){ + if (err) { printk(KERN_ERR "%s: Can't open \"%s\": errno = %d\n", - disk->disk_name, dev->file, -err); + disk->disk_name, dev->file, -err); goto out; } + } else dev->count++; + if ((filp->f_mode & FMODE_WRITE) && !dev->openflags.w) { + ubd_close_dev(dev); + err = -EROFS; } - dev->count++; - if((filp->f_mode & FMODE_WRITE) && !dev->openflags.w){ - if(--dev->count == 0) ubd_close(dev); - err = -EROFS; - } - out: - return(err); +out: + return (err); } static int ubd_release(struct inode * inode, struct file * file) @@ -764,84 +655,34 @@ struct gendisk *disk = inode->i_bdev->bd_disk; struct ubd *dev = disk->private_data; - if(--dev->count == 0) - ubd_close(dev); - 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; - } - } + ubd_close_dev(dev); + return (0); } static int prepare_request(struct request *req, struct io_thread_req *io_req) { struct gendisk *disk = req->rq_disk; struct ubd *dev = disk->private_data; - __u64 block; - int nsect; if(req->rq_status == RQ_INACTIVE) return(1); if((rq_data_dir(req) == WRITE) && !dev->openflags.w){ - printk("Write attempted on readonly ubd device %s\n", - disk->disk_name); - spin_lock(&ubd_io_lock); + printk("Write attempted on readonly ubd device %s\n", + disk->disk_name); + spin_lock(&ubd_io_lock); end_request(req, 0); - spin_unlock(&ubd_io_lock); + spin_unlock(&ubd_io_lock); return(1); } - block = req->sector; - nsect = req->current_nr_sectors; - - io_req->op = rq_data_dir(req) == 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->op = (rq_data_dir(req) == READ) ? UBD_READ : UBD_WRITE; + 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); } @@ -855,47 +696,41 @@ while(!list_empty(&q->queue_head)){ req = elv_next_request(q); err = prepare_request(req, &io_req); - if(!err){ + if (!err) { do_io(&io_req); ubd_finish(req, io_req.error); } } - } - else { + } else { if(do_ubd || list_empty(&q->queue_head)) return; req = elv_next_request(q); err = prepare_request(req, &io_req); - if(!err){ + if (!err) { do_ubd = ubd_handler; - 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); + 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); } } } 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 = inode->i_bdev->bd_disk->private_data; int err; - struct hd_driveid ubd_id = { - .cyls = 0, - .heads = 128, - .sectors = 32, - }; + struct hd_geometry g = {}; + struct hd_driveid ubd_id = {}; + struct cdrom_volctrl volume; 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 * 512); + 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); @@ -926,9 +761,11 @@ return(0); case HDIO_GET_IDENTITY: - ubd_id.cyls = dev->size / (128 * 32 * 512); - 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); diff -Nur uml-2.5.69-1/arch/um/drivers/cow_user.c work-2.5.69-1/arch/um/drivers/cow_user.c --- uml-2.5.69-1/arch/um/drivers/cow_user.c 1969-12-31 16:00:00.000000000 -0800 +++ work-2.5.69-1/arch/um/drivers/cow_user.c 2003-05-27 06:46:09.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.5.69-1/arch/um/drivers/cow.h work-2.5.69-1/arch/um/drivers/cow.h --- uml-2.5.69-1/arch/um/drivers/cow.h 1969-12-31 16:00:00.000000000 -0800 +++ work-2.5.69-1/arch/um/drivers/cow.h 2003-05-26 17:16:36.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.5.69-1/arch/um/drivers/Makefile work-2.5.69-1/arch/um/drivers/Makefile --- uml-2.5.69-1/arch/um/drivers/Makefile 2003-05-16 21:57:44.000000000 -0700 +++ work-2.5.69-1/arch/um/drivers/Makefile 2003-05-26 17:16:36.000000000 -0700 @@ -16,7 +16,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.5.69-1/arch/um/include/ubd_user.h work-2.5.69-1/arch/um/drivers/../include/ubd_user.h --- uml-2.5.69-1/arch/um/include/ubd_user.h 2003-05-16 21:57:44.000000000 -0700 +++ work-2.5.69-1/arch/um/include/ubd_user.h 2003-05-26 17:16:36.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 /*