+static int path_requires_switch(char *from_cmdline, char *from_cow, char *cow)
+{
+ struct uml_stat buf1, buf2;
+ int err;
+
+ if(from_cmdline == NULL)
+ return 0;
+ if(!strcmp(from_cmdline, from_cow))
+ return 0;
+
+ err = os_stat_file(from_cmdline, &buf1);
+ if(err < 0){
+ printk("Couldn't stat '%s', err = %d\n", from_cmdline, -err);
+ return 0;
+ }
+ err = os_stat_file(from_cow, &buf2);
+ if(err < 0){
+ printk("Couldn't stat '%s', err = %d\n", from_cow, -err);
+ return 1;
+ }
+ if((buf1.ust_dev == buf2.ust_dev) && (buf1.ust_ino == buf2.ust_ino))
+ return 0;
+
+ printk("Backing file mismatch - \"%s\" requested,\n"
+ "\"%s\" specified in COW header of \"%s\"\n",
+ from_cmdline, from_cow, cow);
+ return 1;
+}
+
+static int backing_file_mismatch(char *file, __u64 size, time_t mtime)
+{
+ unsigned long modtime;
+ unsigned long long actual;
+ int err;
+
+ err = os_file_modtime(file, &modtime);
+ if(err < 0){
+ printk("Failed to get modification time of backing file "
+ "\"%s\", err = %d\n", file, -err);
+ return(err);
+ }
+
+ err = os_file_size(file, &actual);
+ if(err < 0){
+ printk("Failed to get size of backing file \"%s\", "
+ "err = %d\n", file, -err);
+ return(err);
+ }
+
+ if(actual != size){
+ /*__u64 can be a long on AMD64 and with %lu GCC complains; so
+ * the typecast.*/
+ printk("Size mismatch (%llu vs %llu) of COW header vs backing "
+ "file\n", (unsigned long long) size, actual);
+ return(-EINVAL);
+ }
+ if(modtime != mtime){
+ printk("mtime mismatch (%ld vs %ld) of COW header vs backing "
+ "file\n", mtime, modtime);
+ 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(err);
+
+ err = os_read_file(fd, buf, len);
+ if(err < 0)
+ return(err);
+
+ return(0);
+}
+
+int open_ubd_file(char *file, struct openflags *openflags, int shared,
+ char **backing_file_out, int *bitmap_offset_out,
+ unsigned long *bitmap_len_out, int *data_offset_out,
+ int *create_cow_out)
+{
+ time_t mtime;
+ unsigned long long size;
+ __u32 version, align;
+ char *backing_file;
+ int fd, err, sectorsize, asked_switch, mode = 0644;
+
+ fd = os_open_file(file, *openflags, mode);
+ if (fd < 0) {
+ if ((fd == -ENOENT) && (create_cow_out != NULL))
+ *create_cow_out = 1;
+ if (!openflags->w ||
+ ((fd != -EROFS) && (fd != -EACCES)))
+ return fd;
+ openflags->w = 0;
+ fd = os_open_file(file, *openflags, mode);
+ if (fd < 0)
+ return fd;
+ }
+
+ if(shared)
+ printk("Not locking \"%s\" on the host\n", file);
+ else {
+ err = os_lock_file(fd, openflags->w);
+ if(err < 0){
+ printk("Failed to lock '%s', err = %d\n", file, -err);
+ goto out_close;
+ }
+ }
+
+ /* Successful return case! */
+ if(backing_file_out == NULL)
+ return(fd);
+
+ err = read_cow_header(file_reader, &fd, &version, &backing_file, &mtime,
+ &size, §orsize, &align, bitmap_offset_out);
+ if(err && (*backing_file_out != NULL)){
+ printk("Failed to read COW header from COW file \"%s\", "
+ "errno = %d\n", file, -err);
+ goto out_close;
+ }
+ if(err)
+ return(fd);
+
+ asked_switch = path_requires_switch(*backing_file_out, backing_file, file);
+
+ /* Allow switching only if no mismatch. */
+ if (asked_switch && !backing_file_mismatch(*backing_file_out, size, mtime)) {
+ printk("Switching backing file to '%s'\n", *backing_file_out);
+ err = write_cow_header(file, fd, *backing_file_out,
+ sectorsize, align, &size);
+ if (err) {
+ printk("Switch failed, errno = %d\n", -err);
+ goto out_close;
+ }
+ } else {
+ *backing_file_out = backing_file;
+ err = backing_file_mismatch(*backing_file_out, size, mtime);
+ if (err)
+ goto out_close;
+ }
+
+ cow_sizes(version, size, sectorsize, align, *bitmap_offset_out,
+ bitmap_len_out, data_offset_out);
+
+ return fd;
+ out_close:
+ os_close_file(fd);
+ return err;
+}
+
+int create_cow_file(char *cow_file, char *backing_file, struct openflags flags,
+ int sectorsize, int alignment, int *bitmap_offset_out,
+ unsigned long *bitmap_len_out, int *data_offset_out)
+{
+ int err, fd;
+
+ flags.c = 1;
+ fd = open_ubd_file(cow_file, &flags, 0, 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 = init_cow_file(fd, cow_file, backing_file, sectorsize, alignment,
+ bitmap_offset_out, bitmap_len_out,
+ data_offset_out);
+ if(!err)
+ return(fd);
+ os_close_file(fd);
+ out:
+ return(err);
+}
+
+static int update_bitmap(struct io_thread_req *req)
+{
+ int n;
+
+ if(req->cow_offset == -1)
+ return(0);
+
+ n = os_seek_file(req->fds[1], req->cow_offset);
+ if(n < 0){
+ printk("do_io - bitmap lseek failed : err = %d\n", -n);
+ return(1);
+ }
+
+ n = os_write_file(req->fds[1], &req->bitmap_words,
+ sizeof(req->bitmap_words));
+ if(n != sizeof(req->bitmap_words)){
+ printk("do_io - bitmap update failed, err = %d fd = %d\n", -n,
+ req->fds[1]);
+ return(1);
+ }
+
+ return(0);
+}
+
+void do_io(struct io_thread_req *req)
+{
+ char *buf;
+ unsigned long len;
+ int n, nsectors, start, end, bit;
+ int err;
+ __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++;
+
+ off = req->offset + req->offsets[bit] +
+ start * req->sectorsize;
+ len = (end - start) * req->sectorsize;
+ buf = &req->buffer[start * req->sectorsize];
+
+ err = os_seek_file(req->fds[bit], off);
+ if(err < 0){
+ printk("do_io - lseek failed : err = %d\n", -err);
+ req->error = 1;
+ return;
+ }
+ if(req->op == UBD_READ){
+ n = 0;
+ do {
+ buf = &buf[n];
+ len -= n;
+ n = os_read_file(req->fds[bit], buf, len);
+ if (n < 0) {
+ printk("do_io - read failed, err = %d "
+ "fd = %d\n", -n, req->fds[bit]);
+ req->error = 1;
+ return;
+ }
+ } while((n < len) && (n != 0));
+ if (n < len) memset(&buf[n], 0, len - n);
+ } else {
+ n = os_write_file(req->fds[bit], buf, len);
+ if(n != len){
+ printk("do_io - write failed err = %d "
+ "fd = %d\n", -n, req->fds[bit]);
+ req->error = 1;
+ return;
+ }
+ }
+
+ start = end;
+ } while(start < nsectors);
+
+ req->error = update_bitmap(req);
+}
+
+/* Changed in start_io_thread, which is serialized by being called only
+ * from ubd_init, which is an initcall.