ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_)
{
    PCNetState *s = qemu_get_nic_opaque(nc);
    int is_padr = 0, is_bcast = 0, is_ladr = 0;
    uint8_t buf1[60];
    int remaining;
    int crc_err = 0;
    int size = size_;

    if (CSR_DRX(s) || CSR_STOP(s) || CSR_SPND(s) || !size ||
        (CSR_LOOP(s) && !s->looptest)) {
        return -1;
    }
#ifdef PCNET_DEBUG
    printf("pcnet_receive size=%d\n", size);
#endif

    /* if too small buffer, then expand it */
    if (size < MIN_BUF_SIZE) {
        memcpy(buf1, buf, size);
        memset(buf1 + size, 0, MIN_BUF_SIZE - size);
        buf = buf1;
        size = MIN_BUF_SIZE;
    }

    if (CSR_PROM(s)
        || (is_padr=padr_match(s, buf, size))
        || (is_bcast=padr_bcast(s, buf, size))
        || (is_ladr=ladr_match(s, buf, size))) {

        pcnet_rdte_poll(s);

        if (!(CSR_CRST(s) & 0x8000) && s->rdra) {
            struct pcnet_RMD rmd;
            int rcvrc = CSR_RCVRC(s)-1,i;
            hwaddr nrda;
            for (i = CSR_RCVRL(s)-1; i > 0; i--, rcvrc--) {
                if (rcvrc <= 1)
                    rcvrc = CSR_RCVRL(s);
                nrda = s->rdra +
                    (CSR_RCVRL(s) - rcvrc) *
                    (BCR_SWSTYLE(s) ? 16 : 8 );
                RMDLOAD(&rmd, nrda);
                if (GET_FIELD(rmd.status, RMDS, OWN)) {
#ifdef PCNET_DEBUG_RMD
                    printf("pcnet - scan buffer: RCVRC=%d PREV_RCVRC=%d\n",
                                rcvrc, CSR_RCVRC(s));
#endif
                    CSR_RCVRC(s) = rcvrc;
                    pcnet_rdte_poll(s);
                    break;
                }
            }
        }

        if (!(CSR_CRST(s) & 0x8000)) {
#ifdef PCNET_DEBUG_RMD
            printf("pcnet - no buffer: RCVRC=%d\n", CSR_RCVRC(s));
#endif
            s->csr[0] |= 0x1000; /* Set MISS flag */
            CSR_MISSC(s)++;
        } else {
            uint8_t *src = s->buffer;
            hwaddr crda = CSR_CRDA(s);
            struct pcnet_RMD rmd;
            int pktcount = 0;

            if (!s->looptest) {
                if (size > 4092) {
#ifdef PCNET_DEBUG_RMD
                    fprintf(stderr, "pcnet: truncates rx packet.\n");
#endif
                    size = 4092;
                }
                memcpy(src, buf, size);
                /* no need to compute the CRC */
                src[size] = 0;
                src[size + 1] = 0;
                src[size + 2] = 0;
                src[size + 3] = 0;
                size += 4;
            } else if (s->looptest == PCNET_LOOPTEST_CRC ||
                       !CSR_DXMTFCS(s) || size < MIN_BUF_SIZE+4) {
                uint32_t fcs = ~0;
                uint8_t *p = src;

                while (p != &src[size])
                    CRC(fcs, *p++);
                *(uint32_t *)p = htonl(fcs);
                size += 4;
            } else {
                uint32_t fcs = ~0;
                uint8_t *p = src;

                while (p != &src[size])
                    CRC(fcs, *p++);
                crc_err = (*(uint32_t *)p != htonl(fcs));
            }

#ifdef PCNET_DEBUG_MATCH
            PRINT_PKTHDR(buf);
#endif

            RMDLOAD(&rmd, PHYSADDR(s,crda));
            /*if (!CSR_LAPPEN(s))*/
                SET_FIELD(&rmd.status, RMDS, STP, 1);

#define PCNET_RECV_STORE() do {                                 \
    int count = MIN(4096 - GET_FIELD(rmd.buf_length, RMDL, BCNT),remaining); \
    hwaddr rbadr = PHYSADDR(s, rmd.rbadr);          \
    s->phys_mem_write(s->dma_opaque, rbadr, src, count, CSR_BSWP(s)); \
    src += count; remaining -= count;                           \
    SET_FIELD(&rmd.status, RMDS, OWN, 0);                       \
    RMDSTORE(&rmd, PHYSADDR(s,crda));                           \
    pktcount++;                                                 \
} while (0)

            remaining = size;
            PCNET_RECV_STORE();
            if ((remaining > 0) && CSR_NRDA(s)) {
                hwaddr nrda = CSR_NRDA(s);
#ifdef PCNET_DEBUG_RMD
                PRINT_RMD(&rmd);
#endif
                RMDLOAD(&rmd, PHYSADDR(s,nrda));
                if (GET_FIELD(rmd.status, RMDS, OWN)) {
                    crda = nrda;
                    PCNET_RECV_STORE();
#ifdef PCNET_DEBUG_RMD
                    PRINT_RMD(&rmd);
#endif
                    if ((remaining > 0) && (nrda=CSR_NNRD(s))) {
                        RMDLOAD(&rmd, PHYSADDR(s,nrda));
                        if (GET_FIELD(rmd.status, RMDS, OWN)) {
                            crda = nrda;
                            PCNET_RECV_STORE();
                        }
                    }
                }
            }

#undef PCNET_RECV_STORE

            RMDLOAD(&rmd, PHYSADDR(s,crda));
            if (remaining == 0) {
                SET_FIELD(&rmd.msg_length, RMDM, MCNT, size);
                SET_FIELD(&rmd.status, RMDS, ENP, 1);
                SET_FIELD(&rmd.status, RMDS, PAM, !CSR_PROM(s) && is_padr);
                SET_FIELD(&rmd.status, RMDS, LFAM, !CSR_PROM(s) && is_ladr);
                SET_FIELD(&rmd.status, RMDS, BAM, !CSR_PROM(s) && is_bcast);
                if (crc_err) {
                    SET_FIELD(&rmd.status, RMDS, CRC, 1);
                    SET_FIELD(&rmd.status, RMDS, ERR, 1);
                }
            } else {
                SET_FIELD(&rmd.status, RMDS, OFLO, 1);
                SET_FIELD(&rmd.status, RMDS, BUFF, 1);
                SET_FIELD(&rmd.status, RMDS, ERR, 1);
            }
            RMDSTORE(&rmd, PHYSADDR(s,crda));
            s->csr[0] |= 0x0400;

#ifdef PCNET_DEBUG
            printf("RCVRC=%d CRDA=0x%08x BLKS=%d\n",
                CSR_RCVRC(s), PHYSADDR(s,CSR_CRDA(s)), pktcount);
#endif
#ifdef PCNET_DEBUG_RMD
            PRINT_RMD(&rmd);
#endif

            while (pktcount--) {
                if (CSR_RCVRC(s) <= 1)
                    CSR_RCVRC(s) = CSR_RCVRL(s);
                else
                    CSR_RCVRC(s)--;
            }

            pcnet_rdte_poll(s);

        }
    }

    pcnet_poll(s);
    pcnet_update_irq(s);

    return size_;
}


static void btrfs_cmp_data_free(struct cmp_pages *cmp)
{
	int i;
	struct page *pg;

	for (i = 0; i < cmp->num_pages; i++) {
		pg = cmp->src_pages[i];
		if (pg)
			page_cache_release(pg);
		pg = cmp->dst_pages[i];
		if (pg)
			page_cache_release(pg);
	}
	kfree(cmp->src_pages);
	kfree(cmp->dst_pages);
}


static long btrfs_ioctl_fs_info(struct btrfs_root *root, void __user *arg)
{
	struct btrfs_ioctl_fs_info_args *fi_args;
	struct btrfs_device *device;
	struct btrfs_device *next;
	struct btrfs_fs_devices *fs_devices = root->fs_info->fs_devices;
	int ret = 0;

	fi_args = kzalloc(sizeof(*fi_args), GFP_KERNEL);
	if (!fi_args)
		return -ENOMEM;

	mutex_lock(&fs_devices->device_list_mutex);
	fi_args->num_devices = fs_devices->num_devices;
	memcpy(&fi_args->fsid, root->fs_info->fsid, sizeof(fi_args->fsid));

	list_for_each_entry_safe(device, next, &fs_devices->devices, dev_list) {
		if (device->devid > fi_args->max_id)
			fi_args->max_id = device->devid;
	}
	mutex_unlock(&fs_devices->device_list_mutex);

	fi_args->nodesize = root->fs_info->super_copy->nodesize;
	fi_args->sectorsize = root->fs_info->super_copy->sectorsize;
	fi_args->clone_alignment = root->fs_info->super_copy->sectorsize;

	if (copy_to_user(arg, fi_args, sizeof(*fi_args)))
		ret = -EFAULT;

	kfree(fi_args);
	return ret;
}


static int cluster_pages_for_defrag(struct inode *inode,
				    struct page **pages,
				    unsigned long start_index,
				    unsigned long num_pages)
{
	unsigned long file_end;
	u64 isize = i_size_read(inode);
	u64 page_start;
	u64 page_end;
	u64 page_cnt;
	int ret;
	int i;
	int i_done;
	struct btrfs_ordered_extent *ordered;
	struct extent_state *cached_state = NULL;
	struct extent_io_tree *tree;
	gfp_t mask = btrfs_alloc_write_mask(inode->i_mapping);

	file_end = (isize - 1) >> PAGE_CACHE_SHIFT;
	if (!isize || start_index > file_end)
		return 0;

	page_cnt = min_t(u64, (u64)num_pages, (u64)file_end - start_index + 1);

	ret = btrfs_delalloc_reserve_space(inode,
					   page_cnt << PAGE_CACHE_SHIFT);
	if (ret)
		return ret;
	i_done = 0;
	tree = &BTRFS_I(inode)->io_tree;

	/* step one, lock all the pages */
	for (i = 0; i < page_cnt; i++) {
		struct page *page;
again:
		page = find_or_create_page(inode->i_mapping,
					   start_index + i, mask);
		if (!page)
			break;

		page_start = page_offset(page);
		page_end = page_start + PAGE_CACHE_SIZE - 1;
		while (1) {
			lock_extent_bits(tree, page_start, page_end,
					 0, &cached_state);
			ordered = btrfs_lookup_ordered_extent(inode,
							      page_start);
			unlock_extent_cached(tree, page_start, page_end,
					     &cached_state, GFP_NOFS);
			if (!ordered)
				break;

			unlock_page(page);
			btrfs_start_ordered_extent(inode, ordered, 1);
			btrfs_put_ordered_extent(ordered);
			lock_page(page);
			/*
			 * we unlocked the page above, so we need check if
			 * it was released or not.
			 */
			if (page->mapping != inode->i_mapping) {
				unlock_page(page);
				page_cache_release(page);
				goto again;
			}
		}

		if (!PageUptodate(page)) {
			btrfs_readpage(NULL, page);
			lock_page(page);
			if (!PageUptodate(page)) {
				unlock_page(page);
				page_cache_release(page);
				ret = -EIO;
				break;
			}
		}

		if (page->mapping != inode->i_mapping) {
			unlock_page(page);
			page_cache_release(page);
			goto again;
		}

		pages[i] = page;
		i_done++;
	}
	if (!i_done || ret)
		goto out;

	if (!(inode->i_sb->s_flags & MS_ACTIVE))
		goto out;

	/*
	 * so now we have a nice long stream of locked
	 * and up to date pages, lets wait on them
	 */
	for (i = 0; i < i_done; i++)
		wait_on_page_writeback(pages[i]);

	page_start = page_offset(pages[0]);
	page_end = page_offset(pages[i_done - 1]) + PAGE_CACHE_SIZE;

	lock_extent_bits(&BTRFS_I(inode)->io_tree,
			 page_start, page_end - 1, 0, &cached_state);
	clear_extent_bit(&BTRFS_I(inode)->io_tree, page_start,
			  page_end - 1, EXTENT_DIRTY | EXTENT_DELALLOC |
			  EXTENT_DO_ACCOUNTING | EXTENT_DEFRAG, 0, 0,
			  &cached_state, GFP_NOFS);

	if (i_done != page_cnt) {
		spin_lock(&BTRFS_I(inode)->lock);
		BTRFS_I(inode)->outstanding_extents++;
		spin_unlock(&BTRFS_I(inode)->lock);
		btrfs_delalloc_release_space(inode,
				     (page_cnt - i_done) << PAGE_CACHE_SHIFT);
	}

	set_extent_defrag(&BTRFS_I(inode)->io_tree, page_start, page_end - 1,
			  &cached_state, GFP_NOFS);

	unlock_extent_cached(&BTRFS_I(inode)->io_tree,
			     page_start, page_end - 1, &cached_state,
			     GFP_NOFS);

	for (i = 0; i < i_done; i++) {
		clear_page_dirty_for_io(pages[i]);
		ClearPageChecked(pages[i]);
		set_page_extent_mapped(pages[i]);
		set_page_dirty(pages[i]);
		unlock_page(pages[i]);
		page_cache_release(pages[i]);
	}
	return i_done;
out:
	for (i = 0; i < i_done; i++) {
		unlock_page(pages[i]);
		page_cache_release(pages[i]);
	}
	btrfs_delalloc_release_space(inode, page_cnt << PAGE_CACHE_SHIFT);
	return ret;

}


static noinline int search_ioctl(struct inode *inode,
				 struct btrfs_ioctl_search_key *sk,
				 size_t *buf_size,
				 char __user *ubuf)
{
	struct btrfs_root *root;
	struct btrfs_key key;
	struct btrfs_path *path;
	struct btrfs_fs_info *info = BTRFS_I(inode)->root->fs_info;
	int ret;
	int num_found = 0;
	unsigned long sk_offset = 0;

	if (*buf_size < sizeof(struct btrfs_ioctl_search_header)) {
		*buf_size = sizeof(struct btrfs_ioctl_search_header);
		return -EOVERFLOW;
	}

	path = btrfs_alloc_path();
	if (!path)
		return -ENOMEM;

	if (sk->tree_id == 0) {
		/* search the root of the inode that was passed */
		root = BTRFS_I(inode)->root;
	} else {
		key.objectid = sk->tree_id;
		key.type = BTRFS_ROOT_ITEM_KEY;
		key.offset = (u64)-1;
		root = btrfs_read_fs_root_no_name(info, &key);
		if (IS_ERR(root)) {
			btrfs_err(info, "could not find root %llu",
			       sk->tree_id);
			btrfs_free_path(path);
			return -ENOENT;
		}
	}

	key.objectid = sk->min_objectid;
	key.type = sk->min_type;
	key.offset = sk->min_offset;

	while (1) {
		ret = btrfs_search_forward(root, &key, path, sk->min_transid);
		if (ret != 0) {
			if (ret > 0)
				ret = 0;
			goto err;
		}
		ret = copy_to_sk(root, path, &key, sk, buf_size, ubuf,
				 &sk_offset, &num_found);
		btrfs_release_path(path);
		if (ret)
			break;

	}
	if (ret > 0)
		ret = 0;
err:
	sk->nr_items = num_found;
	btrfs_free_path(path);
	return ret;
}


static noinline int may_destroy_subvol(struct btrfs_root *root)
{
	struct btrfs_path *path;
	struct btrfs_dir_item *di;
	struct btrfs_key key;
	u64 dir_id;
	int ret;

	path = btrfs_alloc_path();
	if (!path)
		return -ENOMEM;

	/* Make sure this root isn't set as the default subvol */
	dir_id = btrfs_super_root_dir(root->fs_info->super_copy);
	di = btrfs_lookup_dir_item(NULL, root->fs_info->tree_root, path,
				   dir_id, "default", 7, 0);
	if (di && !IS_ERR(di)) {
		btrfs_dir_item_key_to_cpu(path->nodes[0], di, &key);
		if (key.objectid == root->root_key.objectid) {
			ret = -EPERM;
			btrfs_err(root->fs_info, "deleting default subvolume "
				  "%llu is not allowed", key.objectid);
			goto out;
		}
		btrfs_release_path(path);
	}

	key.objectid = root->root_key.objectid;
	key.type = BTRFS_ROOT_REF_KEY;
	key.offset = (u64)-1;

	ret = btrfs_search_slot(NULL, root->fs_info->tree_root,
				&key, path, 0, 0);
	if (ret < 0)
		goto out;
	BUG_ON(ret == 0);

	ret = 0;
	if (path->slots[0] > 0) {
		path->slots[0]--;
		btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
		if (key.objectid == root->root_key.objectid &&
		    key.type == BTRFS_ROOT_REF_KEY)
			ret = -ENOTEMPTY;
	}
out:
	btrfs_free_path(path);
	return ret;
}


void btrfs_update_iflags(struct inode *inode)
{
	struct btrfs_inode *ip = BTRFS_I(inode);
	unsigned int new_fl = 0;

	if (ip->flags & BTRFS_INODE_SYNC)
		new_fl |= S_SYNC;
	if (ip->flags & BTRFS_INODE_IMMUTABLE)
		new_fl |= S_IMMUTABLE;
	if (ip->flags & BTRFS_INODE_APPEND)
		new_fl |= S_APPEND;
	if (ip->flags & BTRFS_INODE_NOATIME)
		new_fl |= S_NOATIME;
	if (ip->flags & BTRFS_INODE_DIRSYNC)
		new_fl |= S_DIRSYNC;

	set_mask_bits(&inode->i_flags,
		      S_SYNC | S_APPEND | S_IMMUTABLE | S_NOATIME | S_DIRSYNC,
		      new_fl);
}


grub_crypto_hmac_init (const struct gcry_md_spec *md,
		       const void *key, grub_size_t keylen)
{
  grub_uint8_t *helpkey = NULL;
  grub_uint8_t *ipad = NULL, *opad = NULL;
  void *ctx = NULL;
  struct grub_crypto_hmac_handle *ret = NULL;
  unsigned i;

  if (md->mdlen > md->blocksize)
    return NULL;

  ctx = grub_malloc (md->contextsize);
  if (!ctx)
    goto err;

  if ( keylen > md->blocksize )
    {
      helpkey = grub_malloc (md->mdlen);
      if (!helpkey)
	goto err;
      grub_crypto_hash (md, helpkey, key, keylen);

      key = helpkey;
      keylen = md->mdlen;
    }

  ipad = grub_zalloc (md->blocksize);
  if (!ipad)
    goto err;

  opad = grub_zalloc (md->blocksize);
  if (!opad)
    goto err;

  grub_memcpy ( ipad, key, keylen );
  grub_memcpy ( opad, key, keylen );
  for (i=0; i < md->blocksize; i++ )
    {
      ipad[i] ^= 0x36;
      opad[i] ^= 0x5c;
    }
  grub_free (helpkey);
  helpkey = NULL;

  md->init (ctx);

  md->write (ctx, ipad, md->blocksize); /* inner pad */
  grub_memset (ipad, 0, md->blocksize);
  grub_free (ipad);
  ipad = NULL;

  ret = grub_malloc (sizeof (*ret));
  if (!ret)
    goto err;

  ret->md = md;
  ret->ctx = ctx;
  ret->opad = opad;

  return ret;

 err:
  grub_free (helpkey);
  grub_free (ctx);
  grub_free (ipad);
  grub_free (opad);
  return NULL;
}


grub_auth_authenticate (const char *user)
{
  struct grub_auth_user *cur;

  cur = grub_named_list_find (GRUB_AS_NAMED_LIST (users), user);
  if (!cur)
    cur = grub_zalloc (sizeof (*cur));
  if (!cur)
    return grub_errno;

  cur->authenticated = 1;

  if (! cur->name)
    {
      cur->name = grub_strdup (user);
      if (!cur->name)
	{
	  grub_free (cur);
	  return grub_errno;
	}
      grub_list_push (GRUB_AS_LIST_P (&users), GRUB_AS_LIST (cur));
    }

  return GRUB_ERR_NONE;
}


grub_cmd_authenticate (struct grub_command *cmd __attribute__ ((unused)),
		       int argc, char **args)
{
  return grub_auth_check_authentication ((argc >= 1) ? args[0] : "");
}


grub_crypto_hmac_buffer (const struct gcry_md_spec *md,
			 const void *key, grub_size_t keylen,
			 const void *data, grub_size_t datalen, void *out)
{
  struct grub_crypto_hmac_handle *hnd;

  hnd = grub_crypto_hmac_init (md, key, keylen);
  if (!hnd)
    return GPG_ERR_OUT_OF_MEMORY;

  grub_crypto_hmac_write (hnd, data, datalen);
  return grub_crypto_hmac_fini (hnd, out);
}


int CLASS parse_tiff_ifd (int base)
{
  unsigned entries, tag, type, len, plen=16, save;
  int ifd, use_cm=0, cfa, i, j, c, ima_len=0;
  char *cbuf, *cp;
  uchar cfa_pat[16], cfa_pc[] = { 0,1,2,3 }, tab[256];
  double cc[4][4], cm[4][3], cam_xyz[4][3], num;
  double ab[]={ 1,1,1,1 }, asn[] = { 0,0,0,0 }, xyz[] = { 1,1,1 };
  unsigned sony_curve[] = { 0,0,0,0,0,4095 };
  unsigned *buf, sony_offset=0, sony_length=0, sony_key=0;
  struct jhead jh;
  int pana_raw = 0;
#ifndef LIBRAW_LIBRARY_BUILD
  FILE *sfp;
#endif

  if (tiff_nifds >= sizeof tiff_ifd / sizeof tiff_ifd[0])
    return 1;
  ifd = tiff_nifds++;
  for (j=0; j < 4; j++)
    for (i=0; i < 4; i++)
      cc[j][i] = i == j;
  entries = get2();
  if (entries > 512) return 1;
  while (entries--) {
    tiff_get (base, &tag, &type, &len, &save);

#ifdef LIBRAW_LIBRARY_BUILD
    if(callbacks.exif_cb)
      {
        int savepos = ftell(ifp);
        callbacks.exif_cb(callbacks.exifparser_data,tag|(pana_raw?0x30000:0),type,len,order,ifp);
        fseek(ifp,savepos,SEEK_SET);
      }
#endif
    switch (tag) {
      case 1:   if(len==4) pana_raw = get4(); break;
      case 5:   width  = get2();  break;
      case 6:   height = get2();  break;
      case 7:   width += get2();  break;
      case 9:   if ((i = get2())) filters = i;
#ifdef LIBRAW_LIBRARY_BUILD
        if(pana_raw && len == 1 && type ==3)
          pana_black[3]+=i;
#endif
        break;
      case 8:
      case 10:
#ifdef LIBRAW_LIBRARY_BUILD
        if(pana_raw && len == 1 && type ==3)
          pana_black[3]+=get2();
#endif
        break;
      case 17: case 18:
	if (type == 3 && len == 1)
	  cam_mul[(tag-17)*2] = get2() / 256.0;
	break;
      case 23:
	if (type == 3) iso_speed = get2();
	break;
      case 28: case 29: case 30:
#ifdef LIBRAW_LIBRARY_BUILD
        if(pana_raw && len == 1 && type ==3)
          {
            pana_black[tag-28] = get2();
          }
        else
#endif
          {
            cblack[tag-28] = get2();
            cblack[3] = cblack[1];
          }
	break;
      case 36: case 37: case 38:
	cam_mul[tag-36] = get2();
	break;
      case 39:
	if (len < 50 || cam_mul[0]) break;
	fseek (ifp, 12, SEEK_CUR);
	FORC3 cam_mul[c] = get2();
	break;
      case 46:
	if (type != 7 || fgetc(ifp) != 0xff || fgetc(ifp) != 0xd8) break;
	thumb_offset = ftell(ifp) - 2;
	thumb_length = len;
	break;
      case 61440:			/* Fuji HS10 table */
	fseek (ifp, get4()+base, SEEK_SET);
	parse_tiff_ifd (base);
	break;
      case 2: case 256: case 61441:	/* ImageWidth */
	tiff_ifd[ifd].t_width = getint(type);
	break;
      case 3: case 257: case 61442:	/* ImageHeight */
	tiff_ifd[ifd].t_height = getint(type);
	break;
      case 258:				/* BitsPerSample */
      case 61443:
	tiff_ifd[ifd].samples = len & 7;
	tiff_ifd[ifd].bps = getint(type);
	break;
      case 61446:
	raw_height = 0;
	if (tiff_ifd[ifd].bps > 12) break;
	load_raw = &CLASS packed_load_raw;
	load_flags = get4() ? 24:80;
	break;
      case 259:				/* Compression */
	tiff_ifd[ifd].comp = getint(type);
	break;
      case 262:				/* PhotometricInterpretation */
	tiff_ifd[ifd].phint = get2();
	break;
      case 270:				/* ImageDescription */
	fread (desc, 512, 1, ifp);
	break;
      case 271:				/* Make */
	fgets (make, 64, ifp);
	break;
      case 272:				/* Model */
	fgets (model, 64, ifp);
	break;
      case 280:				/* Panasonic RW2 offset */
	if (type != 4) break;
	load_raw = &CLASS panasonic_load_raw;
	load_flags = 0x2008;
      case 273:				/* StripOffset */
      case 513:				/* JpegIFOffset */
      case 61447:
	tiff_ifd[ifd].offset = get4()+base;
	if (!tiff_ifd[ifd].bps && tiff_ifd[ifd].offset > 0) {
	  fseek (ifp, tiff_ifd[ifd].offset, SEEK_SET);
	  if (ljpeg_start (&jh, 1)) {
	    tiff_ifd[ifd].comp    = 6;
	    tiff_ifd[ifd].t_width   = jh.wide;
	    tiff_ifd[ifd].t_height  = jh.high;
	    tiff_ifd[ifd].bps     = jh.bits;
	    tiff_ifd[ifd].samples = jh.clrs;
	    if (!(jh.sraw || (jh.clrs & 1)))
	      tiff_ifd[ifd].t_width *= jh.clrs;
	    if ((tiff_ifd[ifd].t_width > 4*tiff_ifd[ifd].t_height) & ~jh.clrs) {
	      tiff_ifd[ifd].t_width  /= 2;
	      tiff_ifd[ifd].t_height *= 2;
	    }
	    i = order;
	    parse_tiff (tiff_ifd[ifd].offset + 12);
	    order = i;
	  }
	}
	break;
      case 274:				/* Orientation */
	tiff_ifd[ifd].t_flip = "50132467"[get2() & 7]-'0';
	break;
      case 277:				/* SamplesPerPixel */
	tiff_ifd[ifd].samples = getint(type) & 7;
	break;
      case 279:				/* StripByteCounts */
      case 514:
      case 61448:
	tiff_ifd[ifd].bytes = get4();
	break;
      case 61454:
	FORC3 cam_mul[(4-c) % 3] = getint(type);
	break;
      case 305:  case 11:		/* Software */
	fgets (software, 64, ifp);
	if (!strncmp(software,"Adobe",5) ||
	    !strncmp(software,"dcraw",5) ||
	    !strncmp(software,"UFRaw",5) ||
	    !strncmp(software,"Bibble",6) ||
	    !strcmp (software,"Digital Photo Professional"))
	  is_raw = 0;
	break;
      case 306:				/* DateTime */
	get_timestamp(0);
	break;
      case 315:				/* Artist */
	fread (artist, 64, 1, ifp);
	break;
      case 322:				/* TileWidth */
	tiff_ifd[ifd].t_tile_width = getint(type);
	break;
      case 323:				/* TileLength */
	tiff_ifd[ifd].t_tile_length = getint(type);
	break;
      case 324:				/* TileOffsets */
	tiff_ifd[ifd].offset = len > 1 ? ftell(ifp) : get4();
	if (len == 1)
	  tiff_ifd[ifd].t_tile_width = tiff_ifd[ifd].t_tile_length = 0;
	if (len == 4) {
	  load_raw = &CLASS sinar_4shot_load_raw;
	  is_raw = 5;
	}
	break;
      case 330:				/* SubIFDs */
	if (!strcmp(model,"DSLR-A100") && tiff_ifd[ifd].t_width == 3872) {
	  load_raw = &CLASS sony_arw_load_raw;
	  data_offset = get4()+base;
	  ifd++;  break;
	}
#ifdef LIBRAW_LIBRARY_BUILD
	if (!strncmp(make,"Hasselblad",10) && libraw_internal_data.unpacker_data.hasselblad_parser_flag) {
          fseek (ifp, ftell(ifp)+4, SEEK_SET);
          fseek (ifp, get4()+base, SEEK_SET);
          parse_tiff_ifd (base);
          break;
	}
#endif
        if(len > 1000) len=1000; /* 1000 SubIFDs is enough */
	while (len--) {
	  i = ftell(ifp);
	  fseek (ifp, get4()+base, SEEK_SET);
	  if (parse_tiff_ifd (base)) break;
	  fseek (ifp, i+4, SEEK_SET);
	}
	break;
      case 400:
	strcpy (make, "Sarnoff");
	maximum = 0xfff;
	break;
#ifdef LIBRAW_LIBRARY_BUILD
      case 700:
        if((type == 1 || type == 2 || type == 6 || type == 7) && len > 1 && len < 5100000)
          {
            xmpdata = (char*)malloc(xmplen = len+1);
            fread(xmpdata,len,1,ifp);
            xmpdata[len]=0;
          }
        break;
#endif
      case 28688:
	FORC4 sony_curve[c+1] = get2() >> 2 & 0xfff;
	for (i=0; i < 5; i++)
	  for (j = sony_curve[i]+1; j <= sony_curve[i+1]; j++)
	    curve[j] = curve[j-1] + (1 << i);
	break;
      case 29184: sony_offset = get4();  break;
      case 29185: sony_length = get4();  break;
      case 29217: sony_key    = get4();  break;
      case 29264:
	parse_minolta (ftell(ifp));
	raw_width = 0;
	break;
      case 29443:
	FORC4 cam_mul[c ^ (c < 2)] = get2();
	break;
      case 29459:
	FORC4 cam_mul[c] = get2();
	i = (cam_mul[1] == 1024 && cam_mul[2] == 1024) << 1;
	SWAP (cam_mul[i],cam_mul[i+1])
	break;
    case 30720: // Sony matrix, Sony_SR2SubIFD_0x7800
      for (i=0; i < 3; i++)
        FORC3 cmatrix[i][c] = ((short) get2()) / 1024.0;
#ifdef DCRAW_VERBOSE
	if (verbose) fprintf (stderr, _(" Sony matrix:\n%f %f %f\n%f %f %f\n%f %f %f\n"), cmatrix[0][0],  cmatrix[0][1], cmatrix[0][2], cmatrix[1][0], cmatrix[1][1], cmatrix[1][2], cmatrix[2][0], cmatrix[2][1], cmatrix[2][2]);
#endif
	break;
    case 29456: // Sony black level, Sony_SR2SubIFD_0x7310, no more needs to be divided by 4
      FORC4 cblack[c ^ c >> 1] = get2();
      i = cblack[3];
      FORC3 if(i>cblack[c]) i = cblack[c];
      FORC4 cblack[c]-=i;
      black = i;
#ifdef DCRAW_VERBOSE
      if (verbose) fprintf (stderr, _("...Sony black: %u cblack: %u %u %u %u\n"),black, cblack[0],cblack[1],cblack[2], cblack[3]);
#endif
      break;
      case 33405:			/* Model2 */
	fgets (model2, 64, ifp);
	break;
      case 33421:			/* CFARepeatPatternDim */
	if (get2() == 6 && get2() == 6)
	  filters = 9;
	break;
      case 33422:			/* CFAPattern */
	if (filters == 9) {
	  FORC(36) ((char *)xtrans)[c] = fgetc(ifp) & 3;
	  break;
	}
      case 64777:			/* Kodak P-series */
        if(len == 36)
          {
            filters = 9;
            colors = 3;
            FORC(36) xtrans[0][c] = fgetc(ifp) & 3;
          }
        else if(len > 0)
          {
            if ((plen=len) > 16) plen = 16;
            fread (cfa_pat, 1, plen, ifp);
            for (colors=cfa=i=0; i < plen && colors < 4; i++) {
              colors += !(cfa & (1 << cfa_pat[i]));
              cfa |= 1 << cfa_pat[i];
            }
            if (cfa == 070) memcpy (cfa_pc,"\003\004\005",3);	/* CMY */
            if (cfa == 072) memcpy (cfa_pc,"\005\003\004\001",4);	/* GMCY */
            goto guess_cfa_pc;
          }
        break;
      case 33424:
      case 65024:
	fseek (ifp, get4()+base, SEEK_SET);
	parse_kodak_ifd (base);
	break;
      case 33434:			/* ExposureTime */
	shutter = getreal(type);
	break;
      case 33437:			/* FNumber */
	aperture = getreal(type);
	break;
#ifdef LIBRAW_LIBRARY_BUILD
// IB start
    case 0xa405:		// FocalLengthIn35mmFormat
      imgdata.lens.FocalLengthIn35mmFormat = get2();
      break;
    case 0xa432:		// LensInfo, 42034dec, Lens Specification per EXIF standard
      imgdata.lens.MinFocal = getreal(type);
      imgdata.lens.MaxFocal = getreal(type);
      imgdata.lens.MaxAp4MinFocal = getreal(type);
      imgdata.lens.MaxAp4MaxFocal = getreal(type);
      break;
    case 0xc630:		// DNG LensInfo, Lens Specification per EXIF standard
      imgdata.lens.MinFocal = getreal(type);
      imgdata.lens.MaxFocal = getreal(type);
      imgdata.lens.MaxAp4MinFocal = getreal(type);
      imgdata.lens.MaxAp4MaxFocal = getreal(type);
      break;
    case 0xa433:		// LensMake
      fread(imgdata.lens.LensMake, MIN(len, sizeof(imgdata.lens.LensMake)), 1, ifp);
      break;
    case 0xa434:		// LensModel
      fread(imgdata.lens.Lens, MIN(len, sizeof(imgdata.lens.Lens)), 1, ifp);
      if (!strncmp(imgdata.lens.Lens, "----", 4))
        imgdata.lens.Lens[0] = 0;
      break;
    case 0x9205:
				imgdata.lens.EXIF_MaxAp = powf64(2.0f, (getreal(type) / 2.0f));
      break;
// IB end
#endif
      case 34306:			/* Leaf white balance */
	FORC4 cam_mul[c ^ 1] = 4096.0 / get2();
	break;
      case 34307:			/* Leaf CatchLight color matrix */
	fread (software, 1, 7, ifp);
	if (strncmp(software,"MATRIX",6)) break;
	colors = 4;
	for (raw_color = i=0; i < 3; i++) {
	  FORC4 fscanf (ifp, "%f", &rgb_cam[i][c^1]);
	  if (!use_camera_wb) continue;
	  num = 0;
	  FORC4 num += rgb_cam[i][c];
	  FORC4 rgb_cam[i][c] /= num;
	}
	break;
      case 34310:			/* Leaf metadata */
	parse_mos (ftell(ifp));
      case 34303:
	strcpy (make, "Leaf");
	break;
      case 34665:			/* EXIF tag */
	fseek (ifp, get4()+base, SEEK_SET);
	parse_exif (base);
	break;
      case 34853:			/* GPSInfo tag */
        {
          unsigned pos;
          fseek(ifp, pos = (get4() + base), SEEK_SET);
          parse_gps(base);
#ifdef LIBRAW_LIBRARY_BUILD
          fseek(ifp, pos, SEEK_SET);
          parse_gps_libraw(base);
#endif
        }
	break;
      case 34675:			/* InterColorProfile */
      case 50831:			/* AsShotICCProfile */
	profile_offset = ftell(ifp);
	profile_length = len;
	break;
      case 37122:			/* CompressedBitsPerPixel */
	kodak_cbpp = get4();
	break;
      case 37386:			/* FocalLength */
	focal_len = getreal(type);
	break;
      case 37393:			/* ImageNumber */
	shot_order = getint(type);
	break;
      case 37400:			/* old Kodak KDC tag */
	for (raw_color = i=0; i < 3; i++) {
	  getreal(type);
	  FORC3 rgb_cam[i][c] = getreal(type);
	}
	break;
      case 40976:
	strip_offset = get4();
	switch (tiff_ifd[ifd].comp) {
	  case 32770: load_raw = &CLASS samsung_load_raw;   break;
	  case 32772: load_raw = &CLASS samsung2_load_raw;  break;
	  case 32773: load_raw = &CLASS samsung3_load_raw;  break;
	}
	break;
      case 46275:			/* Imacon tags */
	strcpy (make, "Imacon");
	data_offset = ftell(ifp);
	ima_len = len;
	break;
      case 46279:
	if (!ima_len) break;
	fseek (ifp, 38, SEEK_CUR);
      case 46274:
	fseek (ifp, 40, SEEK_CUR);
	raw_width  = get4();
	raw_height = get4();
	left_margin = get4() & 7;
	width = raw_width - left_margin - (get4() & 7);
	top_margin = get4() & 7;
	height = raw_height - top_margin - (get4() & 7);
	if (raw_width == 7262 && ima_len == 234317952 ) {
	  height = 5412;
	  width  = 7216;
	  left_margin = 7;
          filters=0;
	} else 	if (raw_width == 7262) {
	  height = 5444;
	  width  = 7244;
	  left_margin = 7;
	}
	fseek (ifp, 52, SEEK_CUR);
	FORC3 cam_mul[c] = getreal(11);
	fseek (ifp, 114, SEEK_CUR);
	flip = (get2() >> 7) * 90;
	if (width * height * 6 == ima_len) {
	  if (flip % 180 == 90) SWAP(width,height);
	  raw_width = width;
	  raw_height = height;
	  left_margin = top_margin = filters = flip = 0;
	}
	sprintf (model, "Ixpress %d-Mp", height*width/1000000);
	load_raw = &CLASS imacon_full_load_raw;
	if (filters) {
	  if (left_margin & 1) filters = 0x61616161;
	  load_raw = &CLASS unpacked_load_raw;
	}
	maximum = 0xffff;
	break;
      case 50454:			/* Sinar tag */
      case 50455:
	if (!(cbuf = (char *) malloc(len))) break;
#ifndef LIBRAW_LIBRARY_BUILD
	fread (cbuf, 1, len, ifp);
#else
	if(fread (cbuf, 1, len, ifp) != len)
		throw LIBRAW_EXCEPTION_IO_CORRUPT; // cbuf to be free'ed in recycle
#endif
	for (cp = cbuf-1; cp && cp < cbuf+len; cp = strchr(cp,'\n'))
	  if (!strncmp (++cp,"Neutral ",8))
	    sscanf (cp+8, "%f %f %f", cam_mul, cam_mul+1, cam_mul+2);
	free (cbuf);
	break;
      case 50458:
	if (!make[0]) strcpy (make, "Hasselblad");
	break;
      case 50459:			/* Hasselblad tag */
#ifdef LIBRAW_LIBRARY_BUILD
        libraw_internal_data.unpacker_data.hasselblad_parser_flag=1;
#endif
	i = order;
	j = ftell(ifp);
	c = tiff_nifds;
	order = get2();
	fseek (ifp, j+(get2(),get4()), SEEK_SET);
	parse_tiff_ifd (j);
	maximum = 0xffff;
	tiff_nifds = c;
	order = i;
	break;
      case 50706:			/* DNGVersion */
	FORC4 dng_version = (dng_version << 8) + fgetc(ifp);
	if (!make[0]) strcpy (make, "DNG");
	is_raw = 1;
	break;
      case 50708:			/* UniqueCameraModel */
	if (model[0]) break;
	fgets (make, 64, ifp);
	if ((cp = strchr(make,' '))) {
	  strcpy(model,cp+1);
	  *cp = 0;
	}
	break;
      case 50710:			/* CFAPlaneColor */
	if (filters == 9) break;
	if (len > 4) len = 4;
	colors = len;
	fread (cfa_pc, 1, colors, ifp);
guess_cfa_pc:
        FORCC tab[cfa_pc[c]] = c;
        cdesc[c] = 0;
        for (i=16; i--; )
          filters = filters << 2 | tab[cfa_pat[i % plen]];
        filters -= !filters;
	break;
      case 50711:			/* CFALayout */
	if (get2() == 2) fuji_width = 1;
	break;
      case 291:
      case 50712:			/* LinearizationTable */
	linear_table (len);
	break;
      case 50713:			/* BlackLevelRepeatDim */
	cblack[4] = get2();
	cblack[5] = get2();
	if (cblack[4] * cblack[5] > (sizeof(cblack) / sizeof (cblack[0]) - 6))
	    cblack[4] = cblack[5] = 1;
	break;
      case 61450:
	cblack[4] = cblack[5] = MIN(sqrt((double)len),64);
      case 50714:			/* BlackLevel */
        if((cblack[4] * cblack[5] < 2) && len == 1)
          {
            black = getreal(type);
          }
        else if(cblack[4] * cblack[5] <= len)
          {
            FORC (cblack[4] * cblack[5])
              cblack[6+c] = getreal(type);
            black = 0;
          }
	break;
      case 50715:			/* BlackLevelDeltaH */
      case 50716:			/* BlackLevelDeltaV */
	for (num=i=0; i < len && i < 65536; i++)
	  num += getreal(type);
	black += num/len + 0.5;
	break;
      case 50717:			/* WhiteLevel */
	maximum = getint(type);
	break;
      case 50718:			/* DefaultScale */
	pixel_aspect  = getreal(type);
	pixel_aspect /= getreal(type);
	if(pixel_aspect > 0.995 && pixel_aspect < 1.005)
          pixel_aspect = 1.0;
	break;
#ifdef LIBRAW_LIBRARY_BUILD
      case 50778:
        imgdata.color.dng_color[0].illuminant = get2();
        break;
      case 50779:
        imgdata.color.dng_color[1].illuminant = get2();
        break;
#endif
      case 50721:			/* ColorMatrix1 */
      case 50722:			/* ColorMatrix2 */
#ifdef LIBRAW_LIBRARY_BUILD
        i = tag == 50721?0:1;
#endif
	FORCC for (j=0; j < 3; j++)
          {
#ifdef LIBRAW_LIBRARY_BUILD
          imgdata.color.dng_color[i].colormatrix[c][j]=
#endif
	  cm[c][j] = getreal(type);
          }
	use_cm = 1;
	break;
      case 50723:			/* CameraCalibration1 */
      case 50724:			/* CameraCalibration2 */
#ifdef LIBRAW_LIBRARY_BUILD
        j = tag == 50723?0:1;
#endif
	for (i=0; i < colors; i++)
	  FORCC
            {
#ifdef LIBRAW_LIBRARY_BUILD
              imgdata.color.dng_color[j].calibration[i][c]=
#endif
              cc[i][c] = getreal(type);
            }
	break;
      case 50727:			/* AnalogBalance */
	FORCC ab[c] = getreal(type);
	break;
      case 50728:			/* AsShotNeutral */
	FORCC asn[c] = getreal(type);
	break;
      case 50729:			/* AsShotWhiteXY */
	xyz[0] = getreal(type);
	xyz[1] = getreal(type);
	xyz[2] = 1 - xyz[0] - xyz[1];
	FORC3 xyz[c] /= d65_white[c];
	break;
#ifdef LIBRAW_LIBRARY_BUILD
      case 50730:			/* DNG: Baseline Exposure */
        baseline_exposure = getreal(type);
        break;
#endif
		  // IB start
    case 50740:			/* tag 0xc634 : DNG Adobe, DNG Pentax, Sony SR2, DNG Private */
#ifdef LIBRAW_LIBRARY_BUILD
      {
        char mbuf[64];
        unsigned short makernote_found = 0;
        INT64 curr_pos, start_pos = ftell(ifp);
        unsigned MakN_order, m_sorder = order;
        unsigned MakN_length;
        unsigned pos_in_original_raw;
        fread(mbuf, 1, 6, ifp);

        if (!strcmp(mbuf, "Adobe"))
          {
            order = 0x4d4d;				// Adobe header is always in "MM" / big endian
            curr_pos = start_pos + 6;
            while (curr_pos + 8 - start_pos <= len)
              {
                fread(mbuf, 1, 4, ifp);
                curr_pos += 8;
                if (!strncmp(mbuf, "MakN", 4)) {
                  makernote_found = 1;
                MakN_length = get4();
                MakN_order = get2();
                pos_in_original_raw = get4();
                order = MakN_order;
                parse_makernote_0xc634(curr_pos + 6 - pos_in_original_raw, 0, AdobeDNG);
                break;
                }
              }
          }
        else
          {
            fread(mbuf + 6, 1, 2, ifp);
            if (!strcmp(mbuf, "PENTAX ") ||
                !strcmp(mbuf, "SAMSUNG"))
              {
                makernote_found = 1;
                fseek(ifp, start_pos, SEEK_SET);
                parse_makernote_0xc634(base, 0, CameraDNG);
              }
          }

        fseek(ifp, start_pos, SEEK_SET);
        order = m_sorder;
      }
      // IB end
#endif
      if (dng_version) break;
      parse_minolta (j = get4()+base);
      fseek (ifp, j, SEEK_SET);
      parse_tiff_ifd (base);
      break;
    case 50752:
      read_shorts (cr2_slice, 3);
      break;
    case 50829:			/* ActiveArea */
      top_margin = getint(type);
      left_margin = getint(type);
      height = getint(type) - top_margin;
      width = getint(type) - left_margin;
      break;
    case 50830:			/* MaskedAreas */
      for (i=0; i < len && i < 32; i++)
        ((int*)mask)[i] = getint(type);
      black = 0;
      break;
    case 51009:			/* OpcodeList2 */
      meta_offset = ftell(ifp);
      break;
    case 64772:			/* Kodak P-series */
      if (len < 13) break;
      fseek (ifp, 16, SEEK_CUR);
      data_offset = get4();
      fseek (ifp, 28, SEEK_CUR);
      data_offset += get4();
      load_raw = &CLASS packed_load_raw;
      break;
    case 65026:
      if (type == 2) fgets (model2, 64, ifp);
    }
    fseek (ifp, save, SEEK_SET);
  }
  if (sony_length && (buf = (unsigned *) malloc(sony_length))) {
    fseek (ifp, sony_offset, SEEK_SET);
    fread (buf, sony_length, 1, ifp);
    sony_decrypt (buf, sony_length/4, 1, sony_key);
#ifndef LIBRAW_LIBRARY_BUILD
    sfp = ifp;
    if ((ifp = tmpfile())) {
      fwrite (buf, sony_length, 1, ifp);
      fseek (ifp, 0, SEEK_SET);
      parse_tiff_ifd (-sony_offset);
      fclose (ifp);
    }
    ifp = sfp;
#else
    if( !ifp->tempbuffer_open(buf,sony_length))
        {
            parse_tiff_ifd(-sony_offset);
            ifp->tempbuffer_close();
        }
#endif
    free (buf);
  }
  for (i=0; i < colors; i++)
    FORCC cc[i][c] *= ab[i];
  if (use_cm) {
    FORCC for (i=0; i < 3; i++)
      for (cam_xyz[c][i]=j=0; j < colors; j++)
	cam_xyz[c][i] += cc[c][j] * cm[j][i] * xyz[i];
    cam_xyz_coeff (cmatrix, cam_xyz);
  }
  if (asn[0]) {
    cam_mul[3] = 0;
    FORCC cam_mul[c] = 1 / asn[c];
  }
  if (!use_cm)
    FORCC pre_mul[c] /= cc[c][c];
  return 0;
}


CameraMetaDataLR::CameraMetaDataLR(char *data, int sz) : CameraMetaData() {
  ctxt = xmlNewParserCtxt();
  if (ctxt == NULL) {
    ThrowCME("CameraMetaData:Could not initialize context.");
  }

  xmlResetLastError();
  doc = xmlCtxtReadMemory(ctxt, data,sz, "", NULL, XML_PARSE_DTDVALID);

  if (doc == NULL) {
    ThrowCME("CameraMetaData: XML Document could not be parsed successfully. Error was: %s", ctxt->lastError.message);
  }

  if (ctxt->valid == 0) {
    if (ctxt->lastError.code == 0x5e) {
      // printf("CameraMetaData: Unable to locate DTD, attempting to ignore.");
    } else {
      ThrowCME("CameraMetaData: XML file does not validate. DTD Error was: %s", ctxt->lastError.message);
    }
  }

  xmlNodePtr cur;
  cur = xmlDocGetRootElement(doc);
  if (xmlStrcmp(cur->name, (const xmlChar *) "Cameras")) {
    ThrowCME("CameraMetaData: XML document of the wrong type, root node is not cameras.");
    return;
  }

  cur = cur->xmlChildrenNode;
  while (cur != NULL) {
    if ((!xmlStrcmp(cur->name, (const xmlChar *)"Camera"))) {
      Camera *camera = new Camera(doc, cur);
      addCamera(camera);

      // Create cameras for aliases.
      for (unsigned int i = 0; i < camera->aliases.size(); i++) {
        addCamera(new Camera(camera, i));
      }
    }
    cur = cur->next;
  }
  if (doc)
    xmlFreeDoc(doc);
  doc = 0;
  if (ctxt)
    xmlFreeParserCtxt(ctxt);
  ctxt = 0;
}


void x3f_clear(void *p)
{
  x3f_delete((x3f_t*)p);
}


void LibRaw:: recycle_datastream()
{
  if(libraw_internal_data.internal_data.input && libraw_internal_data.internal_data.input_internal)
    {
      delete libraw_internal_data.internal_data.input;
      libraw_internal_data.internal_data.input = NULL;
    }
  libraw_internal_data.internal_data.input_internal = 0;
}


  void default_memory_callback(void *,const char *file,const char *where)
  {
    fprintf (stderr,"%s: Out of memory in %s\n", file?file:"unknown file", where);
  }


libraw_processed_image_t * LibRaw::dcraw_make_mem_thumb(int *errcode)
{
  if(!T.thumb)
    {
      if ( !ID.toffset)
        {
          if(errcode) *errcode= LIBRAW_NO_THUMBNAIL;
        }
      else
        {
          if(errcode) *errcode= LIBRAW_OUT_OF_ORDER_CALL;
        }
      return NULL;
    }

  if (T.tformat == LIBRAW_THUMBNAIL_BITMAP)
    {
      libraw_processed_image_t * ret =
        (libraw_processed_image_t *)::malloc(sizeof(libraw_processed_image_t)+T.tlength);

      if(!ret)
        {
          if(errcode) *errcode= ENOMEM;
          return NULL;
        }

      memset(ret,0,sizeof(libraw_processed_image_t));
      ret->type   = LIBRAW_IMAGE_BITMAP;
      ret->height = T.theight;
      ret->width  = T.twidth;
      ret->colors = 3;
      ret->bits   = 8;
      ret->data_size = T.tlength;
      memmove(ret->data,T.thumb,T.tlength);
      if(errcode) *errcode= 0;
      return ret;
    }
  else if (T.tformat == LIBRAW_THUMBNAIL_JPEG)
    {
      ushort exif[5];
      int mk_exif = 0;
      if(strcmp(T.thumb+6,"Exif")) mk_exif = 1;

      int dsize = T.tlength + mk_exif * (sizeof(exif)+sizeof(tiff_hdr));

      libraw_processed_image_t * ret =
        (libraw_processed_image_t *)::malloc(sizeof(libraw_processed_image_t)+dsize);

      if(!ret)
        {
          if(errcode) *errcode= ENOMEM;
          return NULL;
        }

      memset(ret,0,sizeof(libraw_processed_image_t));

      ret->type = LIBRAW_IMAGE_JPEG;
      ret->data_size = dsize;

      ret->data[0] = 0xff;
      ret->data[1] = 0xd8;
      if(mk_exif)
        {
          struct tiff_hdr th;
          memcpy (exif, "\xff\xe1  Exif\0\0", 10);
          exif[1] = htons (8 + sizeof th);
          memmove(ret->data+2,exif,sizeof(exif));
          tiff_head (&th, 0);
          memmove(ret->data+(2+sizeof(exif)),&th,sizeof(th));
          memmove(ret->data+(2+sizeof(exif)+sizeof(th)),T.thumb+2,T.tlength-2);
        }
      else
        {
          memmove(ret->data+2,T.thumb+2,T.tlength-2);
        }
      if(errcode) *errcode= 0;
      return ret;

    }
  else
    {
      if(errcode) *errcode= LIBRAW_UNSUPPORTED_THUMBNAIL;
      return NULL;
    }
}


int LibRaw::set_rawspeed_camerafile(char *filename)
{
#ifdef USE_RAWSPEED
  try
    {
      CameraMetaDataLR *camerameta = new CameraMetaDataLR(filename);
      if(_rawspeed_camerameta)
        {
          CameraMetaDataLR *d = static_cast<CameraMetaDataLR*>(_rawspeed_camerameta);
          delete d;
        }
      _rawspeed_camerameta = static_cast<void*>(camerameta);
    }
  catch (...)
    {
      //just return error code
      return -1;
    }
#endif
  return 0;
}


void LibRaw::unpacked_load_raw_fuji_f700s20()
{
  int base_offset = 0;
  int row_size = imgdata.sizes.raw_width * 2; // in bytes
  if(imgdata.idata.raw_count==2 && imgdata.params.shot_select)
    {
      libraw_internal_data.internal_data.input->seek(-row_size,SEEK_CUR);
      base_offset = row_size; // in bytes
    }
  unsigned char *buffer = (unsigned char*)malloc(row_size*2);
  for(int row = 0; row < imgdata.sizes.raw_height; row++)
    {
      read_shorts((ushort*)buffer,imgdata.sizes.raw_width * 2);
      memmove(&imgdata.rawdata.raw_image[row*imgdata.sizes.raw_pitch/2],buffer+base_offset,row_size);
    }
  free(buffer);
}


int CLASS ljpeg_start (struct jhead *jh, int info_only)
{
  int c, tag;
  ushort len;
  int cnt = 0;
  uchar data[0x10000];
  const uchar *dp;

  memset (jh, 0, sizeof *jh);
  jh->restart = INT_MAX;
  fread (data, 2, 1, ifp);
  if (data[1] != 0xd8) return 0;
  do {
    if(feof(ifp)) return 0;
    if(cnt++ > 1024) return 0; // 1024 tags limit
    fread (data, 2, 2, ifp);
    tag =  data[0] << 8 | data[1];
    len = (data[2] << 8 | data[3]) - 2;
    if (tag <= 0xff00) return 0;
    fread (data, 1, len, ifp);
    switch (tag) {
      case 0xffc3:        // start of frame; lossless, Huffman
	jh->sraw = ((data[7] >> 4) * (data[7] & 15) - 1) & 3;
      case 0xffc0:        // start of frame; baseline jpeg
	jh->bits = data[0];
	jh->high = data[1] << 8 | data[2];
	jh->wide = data[3] << 8 | data[4];
	jh->clrs = data[5] + jh->sraw;

	if (len == 9 && !dng_version) getc(ifp);
	break;
      case 0xffc4:          // define Huffman tables
	if (info_only) break;
	for (dp = data; dp < data+len && (c = *dp++) < 4; )
	  jh->free[c] = jh->huff[c] = make_decoder_ref (&dp);
	break;
      case 0xffda:          // start of scan
	jh->psv = data[1+data[0]*2];
	jh->bits -= data[3+data[0]*2] & 15;
	break;
      case 0xffdd:          // define restart interval
	jh->restart = data[0] << 8 | data[1];
    }
  } while (tag != 0xffda);
  if (info_only) return 1;
  if (jh->clrs > 6 || !jh->huff[0]) return 0;
  FORC(5) if (!jh->huff[c+1]) jh->huff[c+1] = jh->huff[c];
  if (jh->sraw) {
    FORC(4)        jh->huff[2+c] = jh->huff[1];
    FORC(jh->sraw) jh->huff[1+c] = jh->huff[0];
  }
  jh->row = (ushort *) calloc (jh->wide*jh->clrs, 4);
  merror (jh->row, "ljpeg_start()");
  return zero_after_ff = 1;
}


void CLASS sony_arw2_load_raw()
{
  uchar *data, *dp;
  ushort pix[16];
  int row, col, val, max, min, imax, imin, sh, bit, i;

  data = (uchar *) malloc (raw_width+1);
  merror (data, "sony_arw2_load_raw()");
#ifdef LIBRAW_LIBRARY_BUILD
  try {
#endif
  for (row=0; row < height; row++) {
#ifdef LIBRAW_LIBRARY_BUILD
    checkCancel();
#endif
    fread (data, 1, raw_width, ifp);
    for (dp=data, col=0; col < raw_width-30; dp+=16) {
      max = 0x7ff & (val = sget4(dp));
      min = 0x7ff & val >> 11;
      imax = 0x0f & val >> 22;
      imin = 0x0f & val >> 26;
      for (sh=0; sh < 4 && 0x80 << sh <= max-min; sh++);
#ifdef LIBRAW_LIBRARY_BUILD
      /* flag checks if outside of loop */
      if(imgdata.params.sony_arw2_options == LIBRAW_SONYARW2_NONE
         || imgdata.params.sony_arw2_options == LIBRAW_SONYARW2_DELTATOVALUE
         )
        {
          for (bit=30, i=0; i < 16; i++)
            if      (i == imax) pix[i] = max;
            else if (i == imin) pix[i] = min;
            else {
              pix[i] = ((sget2(dp+(bit >> 3)) >> (bit & 7) & 0x7f) << sh) + min;
              if (pix[i] > 0x7ff) pix[i] = 0x7ff;
              bit += 7;
            }
        }
      else if(imgdata.params.sony_arw2_options == LIBRAW_SONYARW2_BASEONLY)
        {
          for (bit=30, i=0; i < 16; i++)
            if      (i == imax) pix[i] = max;
            else if (i == imin) pix[i] = min;
            else pix[i]=0;
        }
      else if(imgdata.params.sony_arw2_options == LIBRAW_SONYARW2_DELTAONLY)
        {
          for (bit=30, i=0; i < 16; i++)
            if      (i == imax) pix[i] = 0;
            else if (i == imin) pix[i] = 0;
            else {
              pix[i] = ((sget2(dp+(bit >> 3)) >> (bit & 7) & 0x7f) << sh) + min;
              if (pix[i] > 0x7ff) pix[i] = 0x7ff;
              bit += 7;
            }
        }
      else if(imgdata.params.sony_arw2_options == LIBRAW_SONYARW2_DELTAZEROBASE)
        {
          for (bit=30, i=0; i < 16; i++)
            if      (i == imax) pix[i] = 0;
            else if (i == imin) pix[i] = 0;
            else {
              pix[i] = ((sget2(dp+(bit >> 3)) >> (bit & 7) & 0x7f) << sh);
              if (pix[i] > 0x7ff) pix[i] = 0x7ff;
              bit += 7;
            }
        }
#else
      /* unaltered dcraw processing */
      for (bit=30, i=0; i < 16; i++)
	if      (i == imax) pix[i] = max;
	else if (i == imin) pix[i] = min;
	else {
	  pix[i] = ((sget2(dp+(bit >> 3)) >> (bit & 7) & 0x7f) << sh) + min;
	  if (pix[i] > 0x7ff) pix[i] = 0x7ff;
	  bit += 7;
	}
#endif

#ifdef LIBRAW_LIBRARY_BUILD
      if(imgdata.params.sony_arw2_options == LIBRAW_SONYARW2_DELTATOVALUE)
        {
          for (i=0; i < 16; i++, col+=2)
            {
              unsigned slope = pix[i] < 1001? 2 : curve[pix[i]<<1]-curve[(pix[i]<<1)-2];
              unsigned step = 1 << sh;
              RAW(row,col)=curve[pix[i]<<1]>black+imgdata.params.sony_arw2_posterization_thr?
                LIM(((slope*step*1000)/(curve[pix[i]<<1]-black)),0,10000):0;
            }
        }
      else
        {
          for (i=0; i < 16; i++, col+=2)
            RAW(row,col) = curve[pix[i] << 1];
        }
#else
      for (i=0; i < 16; i++, col+=2)
	RAW(row,col) = curve[pix[i] << 1] >> 2;
#endif
      col -= col & 1 ? 1:31;
    }
  }
#ifdef LIBRAW_LIBRARY_BUILD
  } catch(...) {
    free (data);
    throw;
  }
  if(imgdata.params.sony_arw2_options == LIBRAW_SONYARW2_DELTATOVALUE)
    maximum=10000;
#endif
  free (data);
}


int LibRaw::is_coolscan_nef() { return load_raw == &LibRaw::nikon_coolscan_load_raw;}


int LibRaw::sraw_midpoint() {
  if (load_raw == &LibRaw::canon_sraw_load_raw) return 8192;
  else if (load_raw == &LibRaw::nikon_load_sraw) return 2048;
  else return 0;
}


void CLASS parse_fuji (int offset)
{
  unsigned entries, tag, len, save, c;

  fseek (ifp, offset, SEEK_SET);
  entries = get4();
  if (entries > 255) return;
  while (entries--) {
    tag = get2();
    len = get2();
    save = ftell(ifp);

    if (tag == 0x100) {
      raw_height = get2();
      raw_width  = get2();
    } else if (tag == 0x121) {
      height = get2();
      if ((width = get2()) == 4284) width += 3;
    } else if (tag == 0x130) {
      fuji_layout = fgetc(ifp) >> 7;
      fuji_width = !(fgetc(ifp) & 8);
    } else if (tag == 0x131) {
      filters = 9;
      FORC(36) xtrans_abs[0][35-c] = fgetc(ifp) & 3;
    } else if (tag == 0x2ff0) {
      FORC4 cam_mul[c ^ 1] = get2();

// IB start
#ifdef LIBRAW_LIBRARY_BUILD
    } else if (tag == 0x9650) {
      imgdata.color.FujiExpoMidPointShift = ((short)get2()) / ((float)get2());
#endif
// IB end

    } else if (tag == 0xc000) {
      c = order;
      order = 0x4949;
      if ((tag = get4()) > 10000) tag = get4();
      width = tag;
      height = get4();
      order = c;
    }
    fseek (ifp, save+len, SEEK_SET);
  }
  height <<= fuji_layout;
  width  >>= fuji_layout;
}


void LibRaw::copy_bayer(unsigned short cblack[4],unsigned short *dmaxp)
{
  // Both cropped and uncropped
  int row;

#if defined(LIBRAW_USE_OPENMP)
#pragma omp parallel for default(shared)
#endif
  for (row=0; row < S.height; row++)
    {
      int col;
      unsigned short ldmax = 0;
      for (col=0; col < S.width; col++)
        {
          unsigned short val = imgdata.rawdata.raw_image[(row+S.top_margin)*S.raw_pitch/2+(col+S.left_margin)];
          int cc = fcol(row,col);
          if(val>cblack[cc])
            {
              val-=cblack[cc];
              if(val>ldmax)ldmax = val;
            }
          else
            val = 0;
          imgdata.image[((row) >> IO.shrink)*S.iwidth + ((col) >> IO.shrink)][cc] = val;
        }
#if defined(LIBRAW_USE_OPENMP)
#pragma omp critical(dataupdate)
#endif
      {
        if(*dmaxp < ldmax)
          *dmaxp = ldmax;
      }
    }
}


int LibRaw::phase_one_subtract_black(ushort *src, ushort *dest)
{

  try
    {
      if (O.user_black < 0 && O.user_cblack[0] <= -1000000 && O.user_cblack[1] <= -1000000 && O.user_cblack[2] <= -1000000 && O.user_cblack[3] <= -1000000)
        {
          if (!imgdata.rawdata.ph1_cblack || !imgdata.rawdata.ph1_rblack)
            {
              register int bl = imgdata.color.phase_one_data.t_black;
              for (int row = 0; row < S.raw_height; row++)
                {
                  checkCancel();
                  for (int col = 0; col < S.raw_width; col++)
                    {
                      int idx = row*S.raw_width + col;
                      int val = int(src[idx]) - bl;
                      dest[idx] = val>0 ? val : 0;
                    }
                }
            }
          else
            {
              register int bl = imgdata.color.phase_one_data.t_black;
              for (int row = 0; row < S.raw_height; row++)
                {
                  checkCancel();
                  for (int col = 0; col < S.raw_width; col++)
                    {
                      int idx = row*S.raw_width + col;
                      int val = int(src[idx]) - bl
                      + imgdata.rawdata.ph1_cblack[row][col >= imgdata.rawdata.color.phase_one_data.split_col]
                        + imgdata.rawdata.ph1_rblack[col][row >= imgdata.rawdata.color.phase_one_data.split_row];
                      dest[idx] = val>0 ? val : 0;
                    }
                }
            }
        }
      else // black set by user interaction
        {
          // Black level in cblack!
          for (int row = 0; row < S.raw_height; row++)
            {
              checkCancel();
              unsigned short cblk[16];
              for (int cc = 0; cc < 16; cc++)
                cblk[cc] = C.cblack[fcol(row, cc)];
              for (int col = 0; col < S.raw_width; col++)
                {
                  int idx = row*S.raw_width + col;
                  ushort val = src[idx];
                  ushort bl = cblk[col & 0xf];
                  dest[idx] = val>bl ? val - bl : 0;
                }
            }
        }
      return 0;
    }
  catch (LibRaw_exceptions err) {
    return LIBRAW_CANCELLED_BY_CALLBACK;
  }
}


int LibRaw::copy_mem_image(void* scan0, int stride, int bgr)

{
    // the image memory pointed to by scan0 is assumed to be in the format returned by get_mem_image_format
    if((imgdata.progress_flags & LIBRAW_PROGRESS_THUMB_MASK) < LIBRAW_PROGRESS_PRE_INTERPOLATE)
        return LIBRAW_OUT_OF_ORDER_CALL;

    if(libraw_internal_data.output_data.histogram)
      {
        int perc, val, total, t_white=0x2000,c;
        perc = S.width * S.height * O.auto_bright_thr;
        if (IO.fuji_width) perc /= 2;
        if (!((O.highlight & ~2) || O.no_auto_bright))
          for (t_white=c=0; c < P1.colors; c++) {
            for (val=0x2000, total=0; --val > 32; )
              if ((total += libraw_internal_data.output_data.histogram[c][val]) > perc) break;
            if (t_white < val) t_white = val;
          }
        gamma_curve (O.gamm[0], O.gamm[1], 2, (t_white << 3)/O.bright);
      }

    int s_iheight = S.iheight;
    int s_iwidth = S.iwidth;
    int s_width = S.width;
    int s_hwight = S.height;

    S.iheight = S.height;
    S.iwidth  = S.width;

    if (S.flip & 4) SWAP(S.height,S.width);
    uchar *ppm;
    ushort *ppm2;
    int c, row, col, soff, rstep, cstep;

    soff  = flip_index (0, 0);
    cstep = flip_index (0, 1) - soff;
    rstep = flip_index (1, 0) - flip_index (0, S.width);

    for (row=0; row < S.height; row++, soff += rstep)
      {
        uchar *bufp = ((uchar*)scan0)+row*stride;
        ppm2 = (ushort*) (ppm = bufp);
        // keep trivial decisions in the outer loop for speed
        if (bgr) {
          if (O.output_bps == 8) {
            for (col=0; col < S.width; col++, soff += cstep)
              FORBGR *ppm++ = imgdata.color.curve[imgdata.image[soff][c]]>>8;
          }
          else {
            for (col=0; col < S.width; col++, soff += cstep)
              FORBGR *ppm2++ = imgdata.color.curve[imgdata.image[soff][c]];
          }
        }
        else {
          if (O.output_bps == 8) {
            for (col=0; col < S.width; col++, soff += cstep)
              FORRGB *ppm++ = imgdata.color.curve[imgdata.image[soff][c]]>>8;
          }
          else {
            for (col=0; col < S.width; col++, soff += cstep)
              FORRGB *ppm2++ = imgdata.color.curve[imgdata.image[soff][c]];
          }
        }

//            bufp += stride;           // go to the next line
      }

    S.iheight = s_iheight;
    S.iwidth = s_iwidth;
    S.width = s_width;
    S.height = s_hwight;

    return 0;

}


static bool ldap_encode_response(struct asn1_data *data, struct ldap_Result *result)
{
	if (!asn1_write_enumerated(data, result->resultcode)) return false;
	if (!asn1_write_OctetString(data, result->dn,
			       (result->dn) ? strlen(result->dn) : 0)) return false;
	if (!asn1_write_OctetString(data, result->errormessage,
			       (result->errormessage) ?
			       strlen(result->errormessage) : 0)) return false;
	if (result->referral) {
		if (!asn1_push_tag(data, ASN1_CONTEXT(3))) return false;
		if (!asn1_write_OctetString(data, result->referral,
				       strlen(result->referral))) return false;
		if (!asn1_pop_tag(data)) return false;
	}
	return true;
}


static struct ldb_parse_tree *ldap_decode_filter_tree(TALLOC_CTX *mem_ctx,
						      struct asn1_data *data)
{
	uint8_t filter_tag;
	struct ldb_parse_tree *ret;

	if (!asn1_peek_uint8(data, &filter_tag)) {
		return NULL;
	}

	filter_tag &= 0x1f;	/* strip off the asn1 stuff */

	ret = talloc(mem_ctx, struct ldb_parse_tree);
	if (ret == NULL) return NULL;

	switch(filter_tag) {
	case 0:
	case 1:
		/* AND or OR of one or more filters */
		ret->operation = (filter_tag == 0)?LDB_OP_AND:LDB_OP_OR;
		ret->u.list.num_elements = 0;
		ret->u.list.elements = NULL;

		if (!asn1_start_tag(data, ASN1_CONTEXT(filter_tag))) {
			goto failed;
		}

		while (asn1_tag_remaining(data) > 0) {
			struct ldb_parse_tree *subtree;
			subtree = ldap_decode_filter_tree(ret, data);
			if (subtree == NULL) {
				goto failed;
			}
			ret->u.list.elements =
				talloc_realloc(ret, ret->u.list.elements,
					       struct ldb_parse_tree *,
					       ret->u.list.num_elements+1);
			if (ret->u.list.elements == NULL) {
				goto failed;
			}
			talloc_steal(ret->u.list.elements, subtree);
			ret->u.list.elements[ret->u.list.num_elements] = subtree;
			ret->u.list.num_elements++;
		}
		if (!asn1_end_tag(data)) {
			goto failed;
		}
		break;

	case 2:
		/* 'not' operation */
		if (!asn1_start_tag(data, ASN1_CONTEXT(filter_tag))) {
			goto failed;
		}

		ret->operation = LDB_OP_NOT;
		ret->u.isnot.child = ldap_decode_filter_tree(ret, data);
		if (ret->u.isnot.child == NULL) {
			goto failed;
		}
		if (!asn1_end_tag(data)) {
			goto failed;
		}
		break;

	case 3: {
		/* equalityMatch */
		const char *attrib;
		DATA_BLOB value;

		if (!asn1_start_tag(data, ASN1_CONTEXT(filter_tag))) goto failed;
		if (!asn1_read_OctetString_talloc(mem_ctx, data, &attrib)) goto failed;
		if (!asn1_read_OctetString(data, mem_ctx, &value)) goto failed;
		if (!asn1_end_tag(data)) goto failed;
		if ((data->has_error) || (attrib == NULL) || (value.data == NULL)) {
			goto failed;
		}

		ret->operation = LDB_OP_EQUALITY;
		ret->u.equality.attr = talloc_steal(ret, attrib);
		ret->u.equality.value.data = talloc_steal(ret, value.data);
		ret->u.equality.value.length = value.length;
		break;
	}
	case 4: {
		/* substrings */
		DATA_BLOB attr;
		uint8_t subs_tag;
		char *value;
		int chunk_num = 0;

		if (!asn1_start_tag(data, ASN1_CONTEXT(filter_tag))) {
			goto failed;
		}
		if (!asn1_read_OctetString(data, mem_ctx, &attr)) {
			goto failed;
		}

		ret->operation = LDB_OP_SUBSTRING;
		ret->u.substring.attr = talloc_strndup(ret, (char *)attr.data, attr.length);
		ret->u.substring.chunks = NULL;
		ret->u.substring.start_with_wildcard = 1;
		ret->u.substring.end_with_wildcard = 1;

		if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
			goto failed;
		}

		while (asn1_tag_remaining(data)) {
			if (!asn1_peek_uint8(data, &subs_tag)) goto failed;
			subs_tag &= 0x1f;	/* strip off the asn1 stuff */
			if (subs_tag > 2) goto failed;

			if (!asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(subs_tag))) goto failed;
			if (!asn1_read_LDAPString(data, mem_ctx, &value)) goto failed;
			if (!asn1_end_tag(data)) goto failed;

			switch (subs_tag) {
			case 0:
				if (ret->u.substring.chunks != NULL) {
					/* initial value found in the middle */
					goto failed;
				}

				ret->u.substring.chunks = ldap_decode_substring(ret, NULL, 0, value);
				if (ret->u.substring.chunks == NULL) {
					goto failed;
				}

				ret->u.substring.start_with_wildcard = 0;
				chunk_num = 1;
				break;

			case 1:
				if (ret->u.substring.end_with_wildcard == 0) {
					/* "any" value found after a "final" value */
					goto failed;
				}

				ret->u.substring.chunks = ldap_decode_substring(ret,
										ret->u.substring.chunks,
										chunk_num,
										value);
				if (ret->u.substring.chunks == NULL) {
					goto failed;
				}

				chunk_num++;
				break;

			case 2:
				ret->u.substring.chunks = ldap_decode_substring(ret,
										ret->u.substring.chunks,
										chunk_num,
										value);
				if (ret->u.substring.chunks == NULL) {
					goto failed;
				}

				ret->u.substring.end_with_wildcard = 0;
				break;

			default:
				goto failed;
			}

		}

		if (!asn1_end_tag(data)) { /* SEQUENCE */
			goto failed;
		}

		if (!asn1_end_tag(data)) {
			goto failed;
		}
		break;
	}
	case 5: {
		/* greaterOrEqual */
		const char *attrib;
		DATA_BLOB value;

		if (!asn1_start_tag(data, ASN1_CONTEXT(filter_tag))) goto failed;
		if (!asn1_read_OctetString_talloc(mem_ctx, data, &attrib)) goto failed;
		if (!asn1_read_OctetString(data, mem_ctx, &value)) goto failed;
		if (!asn1_end_tag(data)) goto failed;
		if ((data->has_error) || (attrib == NULL) || (value.data == NULL)) {
			goto failed;
		}

		ret->operation = LDB_OP_GREATER;
		ret->u.comparison.attr = talloc_steal(ret, attrib);
		ret->u.comparison.value.data = talloc_steal(ret, value.data);
		ret->u.comparison.value.length = value.length;
		break;
	}
	case 6: {
		/* lessOrEqual */
		const char *attrib;
		DATA_BLOB value;

		if (!asn1_start_tag(data, ASN1_CONTEXT(filter_tag))) goto failed;
		if (!asn1_read_OctetString_talloc(mem_ctx, data, &attrib)) goto failed;
		if (!asn1_read_OctetString(data, mem_ctx, &value)) goto failed;
		if (!asn1_end_tag(data)) goto failed;
		if ((data->has_error) || (attrib == NULL) || (value.data == NULL)) {
			goto failed;
		}

		ret->operation = LDB_OP_LESS;
		ret->u.comparison.attr = talloc_steal(ret, attrib);
		ret->u.comparison.value.data = talloc_steal(ret, value.data);
		ret->u.comparison.value.length = value.length;
		break;
	}
	case 7: {
		/* Normal presence, "attribute=*" */
		char *attr;

		if (!asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(filter_tag))) {
			goto failed;
		}
		if (!asn1_read_LDAPString(data, ret, &attr)) {
			goto failed;
		}

		ret->operation = LDB_OP_PRESENT;
		ret->u.present.attr = talloc_steal(ret, attr);

		if (!asn1_end_tag(data)) {
			goto failed;
		}
		break;
	}
	case 8: {
		/* approx */
		const char *attrib;
		DATA_BLOB value;

		if (!asn1_start_tag(data, ASN1_CONTEXT(filter_tag))) goto failed;
		if (!asn1_read_OctetString_talloc(mem_ctx, data, &attrib)) goto failed;
		if (!asn1_read_OctetString(data, mem_ctx, &value)) goto failed;
		if (!asn1_end_tag(data)) goto failed;
		if ((data->has_error) || (attrib == NULL) || (value.data == NULL)) {
			goto failed;
		}

		ret->operation = LDB_OP_APPROX;
		ret->u.comparison.attr = talloc_steal(ret, attrib);
		ret->u.comparison.value.data = talloc_steal(ret, value.data);
		ret->u.comparison.value.length = value.length;
		break;
	}
	case 9: {
		char *oid = NULL, *attr = NULL, *value;
		uint8_t dnAttributes;
		/* an extended search */
		if (!asn1_start_tag(data, ASN1_CONTEXT(filter_tag))) {
			goto failed;
		}

		/* FIXME: read carefully rfc2251.txt there are a number of 'MUST's
		   we need to check we properly implement --SSS */
		/* either oid or type must be defined */
		if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(1))) { /* optional */
			if (!asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(1))) goto failed;
			if (!asn1_read_LDAPString(data, ret, &oid)) goto failed;
			if (!asn1_end_tag(data)) goto failed;
		}
		if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(2))) {	/* optional  */
			if (!asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(2))) goto failed;
			if (!asn1_read_LDAPString(data, ret, &attr)) goto failed;
			if (!asn1_end_tag(data)) goto failed;
		}
		if (!asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(3))) goto failed;
		if (!asn1_read_LDAPString(data, ret, &value)) goto failed;
		if (!asn1_end_tag(data)) goto failed;
		/* dnAttributes is marked as BOOLEAN DEFAULT FALSE
		   it is not marked as OPTIONAL but openldap tools
		   do not set this unless it is to be set as TRUE
		   NOTE: openldap tools do not work with AD as it
		   seems that AD always requires the dnAttributes
		   boolean value to be set */
		if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(4))) {
			if (!asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(4))) goto failed;
			if (!asn1_read_uint8(data, &dnAttributes)) goto failed;
			if (!asn1_end_tag(data)) goto failed;
		} else {
			dnAttributes = 0;
		}
		if ((oid == NULL && attr == NULL) || (value == NULL)) {
			goto failed;
		}

		if (oid) {
			ret->operation               = LDB_OP_EXTENDED;

			/* From the RFC2251: If the type field is
			   absent and matchingRule is present, the matchValue is compared
			   against all attributes in an entry which support that matchingRule
			*/
			if (attr) {
				ret->u.extended.attr = talloc_steal(ret, attr);
			} else {
				ret->u.extended.attr = talloc_strdup(ret, "*");
			}
			ret->u.extended.rule_id      = talloc_steal(ret, oid);
			ret->u.extended.value.data   = (uint8_t *)talloc_steal(ret, value);
			ret->u.extended.value.length = strlen(value);
			ret->u.extended.dnAttributes = dnAttributes;
		} else {
			ret->operation               = LDB_OP_EQUALITY;
			ret->u.equality.attr         = talloc_steal(ret, attr);
			ret->u.equality.value.data   = (uint8_t *)talloc_steal(ret, value);
			ret->u.equality.value.length = strlen(value);
		}
		if (!asn1_end_tag(data)) {
			goto failed;
		}
		break;
	}

	default:
		goto failed;
	}

	return ret;

failed:
	talloc_free(ret);
	return NULL;
}


static bool encode_asq_control(void *mem_ctx, void *in, DATA_BLOB *out)
{
	struct ldb_asq_control *lac = talloc_get_type(in, struct ldb_asq_control);
	struct asn1_data *data = asn1_init(mem_ctx);

	if (!data) return false;

	if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
		return false;
	}

	if (lac->request) {

		if (!asn1_write_OctetString(data, lac->source_attribute, lac->src_attr_len)) {
			return false;
		}
	} else {
		if (!asn1_write_enumerated(data, lac->result)) {
			return false;
		}
	}

	if (!asn1_pop_tag(data)) {
		return false;
	}

	*out = data_blob_talloc(mem_ctx, data->data, data->length);
	if (out->data == NULL) {
		return false;
	}
	talloc_free(data);

	return true;
}


static bool encode_extended_dn_request(void *mem_ctx, void *in, DATA_BLOB *out)
{
	struct ldb_extended_dn_control *ledc = talloc_get_type(in, struct ldb_extended_dn_control);
	struct asn1_data *data;

	if (!in) {
		*out = data_blob(NULL, 0);
		return true;
	}

	data = asn1_init(mem_ctx);

	if (!data) return false;

	if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
		return false;
	}

	if (!asn1_write_Integer(data, ledc->type)) {
		return false;
	}

	if (!asn1_pop_tag(data)) {
		return false;
	}

	*out = data_blob_talloc(mem_ctx, data->data, data->length);
	if (out->data == NULL) {
		return false;
	}
	talloc_free(data);

	return true;
}


static bool decode_openldap_dereference(void *mem_ctx, DATA_BLOB in, void *_out)
{
	void **out = (void **)_out;
	struct asn1_data *data = asn1_init(mem_ctx);
	struct dsdb_openldap_dereference_result_control *control;
	struct dsdb_openldap_dereference_result **r = NULL;
	int i = 0;
	if (!data) return false;

	control = talloc(mem_ctx, struct dsdb_openldap_dereference_result_control);
	if (!control) return false;

	if (!asn1_load(data, in)) {
		return false;
	}

	control = talloc(mem_ctx, struct dsdb_openldap_dereference_result_control);
	if (!control) {
		return false;
	}

	if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
		return false;
	}

	while (asn1_tag_remaining(data) > 0) {
		r = talloc_realloc(control, r, struct dsdb_openldap_dereference_result *, i + 2);
		if (!r) {
			return false;
		}
		r[i] = talloc_zero(r, struct dsdb_openldap_dereference_result);
		if (!r[i]) {
			return false;
		}

		if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
			return false;
		}

		asn1_read_OctetString_talloc(r[i], data, &r[i]->source_attribute);
		asn1_read_OctetString_talloc(r[i], data, &r[i]->dereferenced_dn);
		if (asn1_peek_tag(data, ASN1_CONTEXT(0))) {
			if (!asn1_start_tag(data, ASN1_CONTEXT(0))) {
				return false;
			}
			if (!ldap_decode_attribs_bare(r, data, &r[i]->attributes,
						 &r[i]->num_attributes)) {
				return false;
			}
			if (!asn1_end_tag(data)) {
				return false;
			}
		}
		if (!asn1_end_tag(data)) {
			return false;
		}
		i++;
		r[i] = NULL;
	}

	if (!asn1_end_tag(data)) {
		return false;
	}

	control->attributes = r;
	*out = control;

	return true;
}


bool asn1_write_DATA_BLOB_LDAPString(struct asn1_data *data, const DATA_BLOB *s)
{
	return asn1_write(data, s->data, s->length);
}


bool asn1_write(struct asn1_data *data, const void *p, int len)
{
	if (data->has_error) return false;
	if (data->length < data->ofs+len) {
		uint8_t *newp;
		newp = talloc_realloc(data, data->data, uint8_t, data->ofs+len);
		if (!newp) {
			asn1_free(data);
			data->has_error = true;
			return false;
		}
		data->data = newp;
		data->length = data->ofs+len;
	}
	memcpy(data->data + data->ofs, p, len);
	data->ofs += len;
	return true;
}


bool asn1_write_BOOLEAN(struct asn1_data *data, bool v)
{
	if (!asn1_push_tag(data, ASN1_BOOLEAN)) return false;
	if (!asn1_write_uint8(data, v ? 0xFF : 0)) return false;
	return asn1_pop_tag(data);
}


bool asn1_write_Integer(struct asn1_data *data, int i)
{
	if (!asn1_push_tag(data, ASN1_INTEGER)) return false;
	if (!asn1_write_implicit_Integer(data, i)) return false;
	return asn1_pop_tag(data);
}


static bool push_int_bigendian(struct asn1_data *data, unsigned int i, bool negative)
{
	uint8_t lowest = i & 0xFF;

	i = i >> 8;
	if (i != 0)
		if (!push_int_bigendian(data, i, negative))
			return false;

	if (data->nesting->start+1 == data->ofs) {

		/* We did not write anything yet, looking at the highest
		 * valued byte */

		if (negative) {
			/* Don't write leading 0xff's */
			if (lowest == 0xFF)
				return true;

			if ((lowest & 0x80) == 0) {
				/* The only exception for a leading 0xff is if
				 * the highest bit is 0, which would indicate
				 * a positive value */
				if (!asn1_write_uint8(data, 0xff))
					return false;
			}
		} else {
			if (lowest & 0x80) {
				/* The highest bit of a positive integer is 1,
				 * this would indicate a negative number. Push
				 * a 0 to indicate a positive one */
				if (!asn1_write_uint8(data, 0))
					return false;
			}
		}
	}

	return asn1_write_uint8(data, lowest);
}


bool asn1_pop_tag(struct asn1_data *data)
{
	struct nesting *nesting;
	size_t len;

	nesting = data->nesting;

	if (!nesting) {
		data->has_error = true;
		return false;
	}
	len = data->ofs - (nesting->start+1);
	/* yes, this is ugly. We don't know in advance how many bytes the length
	   of a tag will take, so we assumed 1 byte. If we were wrong then we
	   need to correct our mistake */
	if (len > 0xFFFFFF) {
		data->data[nesting->start] = 0x84;
		if (!asn1_write_uint8(data, 0)) return false;
		if (!asn1_write_uint8(data, 0)) return false;
		if (!asn1_write_uint8(data, 0)) return false;
		if (!asn1_write_uint8(data, 0)) return false;
		memmove(data->data+nesting->start+5, data->data+nesting->start+1, len);
		data->data[nesting->start+1] = (len>>24) & 0xFF;
		data->data[nesting->start+2] = (len>>16) & 0xFF;
		data->data[nesting->start+3] = (len>>8) & 0xFF;
		data->data[nesting->start+4] = len&0xff;
	} else if (len > 0xFFFF) {
		data->data[nesting->start] = 0x83;
		if (!asn1_write_uint8(data, 0)) return false;
		if (!asn1_write_uint8(data, 0)) return false;
		if (!asn1_write_uint8(data, 0)) return false;
		memmove(data->data+nesting->start+4, data->data+nesting->start+1, len);
		data->data[nesting->start+1] = (len>>16) & 0xFF;
		data->data[nesting->start+2] = (len>>8) & 0xFF;
		data->data[nesting->start+3] = len&0xff;
	} else if (len > 255) {
		data->data[nesting->start] = 0x82;
		if (!asn1_write_uint8(data, 0)) return false;
		if (!asn1_write_uint8(data, 0)) return false;
		memmove(data->data+nesting->start+3, data->data+nesting->start+1, len);
		data->data[nesting->start+1] = len>>8;
		data->data[nesting->start+2] = len&0xff;
	} else if (len > 127) {
		data->data[nesting->start] = 0x81;
		if (!asn1_write_uint8(data, 0)) return false;
		memmove(data->data+nesting->start+2, data->data+nesting->start+1, len);
		data->data[nesting->start+1] = len;
	} else {
		data->data[nesting->start] = len;
	}

	data->nesting = nesting->next;
	talloc_free(nesting);
	return true;
}


bool asn1_write_OctetString(struct asn1_data *data, const void *p, size_t length)
{
	if (!asn1_push_tag(data, ASN1_OCTET_STRING)) return false;
	if (!asn1_write(data, p, length)) return false;
	return asn1_pop_tag(data);
}


bool asn1_read_OctetString(struct asn1_data *data, TALLOC_CTX *mem_ctx, DATA_BLOB *blob)
{
	int len;
	ZERO_STRUCTP(blob);
	if (!asn1_start_tag(data, ASN1_OCTET_STRING)) return false;
	len = asn1_tag_remaining(data);
	if (len < 0) {
		data->has_error = true;
		return false;
	}
	*blob = data_blob_talloc(mem_ctx, NULL, len+1);
	if (!blob->data || blob->length < len) {
		data->has_error = true;
		return false;
	}
	if (!asn1_read(data, blob->data, len)) goto err;
	if (!asn1_end_tag(data)) goto err;
	blob->length--;
	blob->data[len] = 0;
	return true;

  err:

	data_blob_free(blob);
	*blob = data_blob_null;
	return false;
}


bool asn1_write_implicit_Integer(struct asn1_data *data, int i)
{
	if (i == -1) {
		/* -1 is special as it consists of all-0xff bytes. In
                    push_int_bigendian this is the only case that is not
                    properly handled, as all 0xff bytes would be handled as
                    leading ones to be ignored. */
		return asn1_write_uint8(data, 0xff);
	} else {
		return push_int_bigendian(data, i, i<0);
	}
}


static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
{
    unsigned char response[VNC_AUTH_CHALLENGE_SIZE];
    size_t i, pwlen;
    unsigned char key[8];
    time_t now = time(NULL);
    QCryptoCipher *cipher = NULL;
    Error *err = NULL;

    if (!vs->vd->password) {
        VNC_DEBUG("No password configured on server");
        goto reject;
    }
    if (vs->vd->expires < now) {
        VNC_DEBUG("Password is expired");
        goto reject;
    }

    memcpy(response, vs->challenge, VNC_AUTH_CHALLENGE_SIZE);

    /* Calculate the expected challenge response */
    pwlen = strlen(vs->vd->password);
    for (i=0; i<sizeof(key); i++)
        key[i] = i<pwlen ? vs->vd->password[i] : 0;

    cipher = qcrypto_cipher_new(
        QCRYPTO_CIPHER_ALG_DES_RFB,
        QCRYPTO_CIPHER_MODE_ECB,
        key, G_N_ELEMENTS(key),
        &err);
    if (!cipher) {
        VNC_DEBUG("Cannot initialize cipher %s",
                  error_get_pretty(err));
        error_free(err);
        goto reject;
    }

    if (qcrypto_cipher_encrypt(cipher,
                               vs->challenge,
                               response,
                               VNC_AUTH_CHALLENGE_SIZE,
                               &err) < 0) {
        VNC_DEBUG("Cannot encrypt challenge %s",
                  error_get_pretty(err));
        error_free(err);
        goto reject;
    }

    /* Compare expected vs actual challenge response */
    if (memcmp(response, data, VNC_AUTH_CHALLENGE_SIZE) != 0) {
        VNC_DEBUG("Client challenge response did not match\n");
        goto reject;
    } else {
        VNC_DEBUG("Accepting VNC challenge response\n");
        vnc_write_u32(vs, 0); /* Accept auth */
        vnc_flush(vs);

        start_client_init(vs);
    }

    qcrypto_cipher_free(cipher);
    return 0;

reject:
    vnc_write_u32(vs, 1); /* Reject auth */
    if (vs->minor >= 8) {
        static const char err[] = "Authentication failed";
        vnc_write_u32(vs, sizeof(err));
        vnc_write(vs, err, sizeof(err));
    }
    vnc_flush(vs);
    vnc_client_error(vs);
    qcrypto_cipher_free(cipher);
    return 0;
}


ssize_t vnc_tls_push(const char *buf, size_t len, void *opaque)
{
    VncState *vs = opaque;
    ssize_t ret;

 retry:
    ret = send(vs->csock, buf, len, 0);
    if (ret < 0) {
        if (errno == EINTR) {
            goto retry;
        }
        return -1;
    }
    return ret;
}


static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
{
    int i;
    uint16_t limit;
    VncDisplay *vd = vs->vd;

    if (data[0] > 3) {
        update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
    }

    switch (data[0]) {
    case VNC_MSG_CLIENT_SET_PIXEL_FORMAT:
        if (len == 1)
            return 20;

        set_pixel_format(vs, read_u8(data, 4), read_u8(data, 5),
                         read_u8(data, 6), read_u8(data, 7),
                         read_u16(data, 8), read_u16(data, 10),
                         read_u16(data, 12), read_u8(data, 14),
                         read_u8(data, 15), read_u8(data, 16));
        break;
    case VNC_MSG_CLIENT_SET_ENCODINGS:
        if (len == 1)
            return 4;

        if (len == 4) {
            limit = read_u16(data, 2);
            if (limit > 0)
                return 4 + (limit * 4);
        } else
            limit = read_u16(data, 2);

        for (i = 0; i < limit; i++) {
            int32_t val = read_s32(data, 4 + (i * 4));
            memcpy(data + 4 + (i * 4), &val, sizeof(val));
        }

        set_encodings(vs, (int32_t *)(data + 4), limit);
        break;
    case VNC_MSG_CLIENT_FRAMEBUFFER_UPDATE_REQUEST:
        if (len == 1)
            return 10;

        framebuffer_update_request(vs,
                                   read_u8(data, 1), read_u16(data, 2), read_u16(data, 4),
                                   read_u16(data, 6), read_u16(data, 8));
        break;
    case VNC_MSG_CLIENT_KEY_EVENT:
        if (len == 1)
            return 8;

        key_event(vs, read_u8(data, 1), read_u32(data, 4));
        break;
    case VNC_MSG_CLIENT_POINTER_EVENT:
        if (len == 1)
            return 6;

        pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4));
        break;
    case VNC_MSG_CLIENT_CUT_TEXT:
        if (len == 1) {
            return 8;
        }
        if (len == 8) {
            uint32_t dlen = read_u32(data, 4);
            if (dlen > (1 << 20)) {
                error_report("vnc: client_cut_text msg payload has %u bytes"
                             " which exceeds our limit of 1MB.", dlen);
                vnc_client_error(vs);
                break;
            }
            if (dlen > 0) {
                return 8 + dlen;
            }
        }

        client_cut_text(vs, read_u32(data, 4), data + 8);
        break;
    case VNC_MSG_CLIENT_QEMU:
        if (len == 1)
            return 2;

        switch (read_u8(data, 1)) {
        case VNC_MSG_CLIENT_QEMU_EXT_KEY_EVENT:
            if (len == 2)
                return 12;

            ext_key_event(vs, read_u16(data, 2),
                          read_u32(data, 4), read_u32(data, 8));
            break;
        case VNC_MSG_CLIENT_QEMU_AUDIO:
            if (len == 2)
                return 4;

            switch (read_u16 (data, 2)) {
            case VNC_MSG_CLIENT_QEMU_AUDIO_ENABLE:
                audio_add(vs);
                break;
            case VNC_MSG_CLIENT_QEMU_AUDIO_DISABLE:
                audio_del(vs);
                break;
            case VNC_MSG_CLIENT_QEMU_AUDIO_SET_FORMAT:
                if (len == 4)
                    return 10;
                switch (read_u8(data, 4)) {
                case 0: vs->as.fmt = AUD_FMT_U8; break;
                case 1: vs->as.fmt = AUD_FMT_S8; break;
                case 2: vs->as.fmt = AUD_FMT_U16; break;
                case 3: vs->as.fmt = AUD_FMT_S16; break;
                case 4: vs->as.fmt = AUD_FMT_U32; break;
                case 5: vs->as.fmt = AUD_FMT_S32; break;
                default:
                    VNC_DEBUG("Invalid audio format %d\n", read_u8(data, 4));
                    vnc_client_error(vs);
                    break;
                }
                vs->as.nchannels = read_u8(data, 5);
                if (vs->as.nchannels != 1 && vs->as.nchannels != 2) {
                    VNC_DEBUG("Invalid audio channel coount %d\n",
                              read_u8(data, 5));
                    vnc_client_error(vs);
                    break;
                }
                vs->as.freq = read_u32(data, 6);
                break;
            default:
                VNC_DEBUG("Invalid audio message %d\n", read_u8(data, 4));
                vnc_client_error(vs);
                break;
            }
            break;

        default:
            VNC_DEBUG("Msg: %d\n", read_u16(data, 0));
            vnc_client_error(vs);
            break;
        }
        break;
    default:
        VNC_DEBUG("Msg: %d\n", data[0]);
        vnc_client_error(vs);
        break;
    }

    vnc_read_when(vs, protocol_client_msg, 1);
    return 0;
}


char *vnc_display_local_addr(const char *id)
{
    VncDisplay *vs = vnc_display_find(id);

    assert(vs);
    return vnc_socket_local_addr("%s:%s", vs->lsock);
}


static void vnc_update_server_surface(VncDisplay *vd)
{
    qemu_pixman_image_unref(vd->server);
    vd->server = NULL;

    if (QTAILQ_EMPTY(&vd->clients)) {
        return;
    }

    vd->server = pixman_image_create_bits(VNC_SERVER_FB_FORMAT,
                                          vnc_width(vd),
                                          vnc_height(vd),
                                          NULL, 0);
}


static int vnc_width(VncDisplay *vd)
{
    return MIN(VNC_MAX_WIDTH, ROUND_UP(surface_width(vd->ds),
                                       VNC_DIRTY_PIXELS_PER_BIT));
}


static void vnc_auto_assign_id(QemuOptsList *olist, QemuOpts *opts)
{
    int i = 2;
    char *id;

    id = g_strdup("default");
    while (qemu_opts_find(olist, id)) {
        g_free(id);
        id = g_strdup_printf("vnc%d", i++);
    }
    qemu_opts_set_id(opts, id);
}


static int ldb_comparator_false(struct ldb_context *ldb,
				const char *oid,
				const struct ldb_message *msg,
				const char *attribute_to_match,
				const struct ldb_val *value_to_match,
				bool *matched)
{
	*matched = false;
	return LDB_SUCCESS;
}


int smb_vfs_call_fsync(struct vfs_handle_struct *handle,
		       struct files_struct *fsp)
{
	VFS_FIND(fsync);
	return handle->fns->fsync_fn(handle, fsp);
}


int smb_vfs_call_connect(struct vfs_handle_struct *handle,
			 const char *service, const char *user)
{
	VFS_FIND(connect);
	return handle->fns->connect_fn(handle, service, user);
}


NTSTATUS smb_vfs_call_get_dfs_referrals(struct vfs_handle_struct *handle,
					struct dfs_GetDFSReferral *r)
{
	VFS_FIND(get_dfs_referrals);
	return handle->fns->get_dfs_referrals_fn(handle, r);
}


int smb_vfs_call_sys_acl_set_file(struct vfs_handle_struct *handle,
				  const char *name, SMB_ACL_TYPE_T acltype,
				  SMB_ACL_T theacl)
{
	VFS_FIND(sys_acl_set_file);
	return handle->fns->sys_acl_set_file_fn(handle, name, acltype, theacl);
}


NTSTATUS smb_vfs_call_fsctl(struct vfs_handle_struct *handle,
			    struct files_struct *fsp,
			    TALLOC_CTX *ctx,
			    uint32_t function,
			    uint16_t req_flags,
			    const uint8_t *in_data,
			    uint32_t in_len,
			    uint8_t **out_data,
			    uint32_t max_out_len,
			    uint32_t *out_len)
{
	VFS_FIND(fsctl);
	return handle->fns->fsctl_fn(handle, fsp, ctx, function, req_flags,
				     in_data, in_len, out_data, max_out_len,
				     out_len);
}


long smb_vfs_call_telldir(struct vfs_handle_struct *handle,
			  DIR *dirp)
{
	VFS_FIND(telldir);
	return handle->fns->telldir_fn(handle, dirp);
}


ssize_t smb_vfs_call_flistxattr(struct vfs_handle_struct *handle,
				struct files_struct *fsp, char *list,
				size_t size)
{
	VFS_FIND(flistxattr);
	return handle->fns->flistxattr_fn(handle, fsp, list, size);
}


char *vfs_GetWd(TALLOC_CTX *ctx, connection_struct *conn)
{
        char *current_dir = NULL;
	char *result = NULL;
	DATA_BLOB cache_value;
	struct file_id key;
	struct smb_filename *smb_fname_dot = NULL;
	struct smb_filename *smb_fname_full = NULL;

	if (!lp_getwd_cache()) {
		goto nocache;
	}

	smb_fname_dot = synthetic_smb_fname(ctx, ".", NULL, NULL);
	if (smb_fname_dot == NULL) {
		errno = ENOMEM;
		goto out;
	}

	if (SMB_VFS_STAT(conn, smb_fname_dot) == -1) {
		/*
		 * Known to fail for root: the directory may be NFS-mounted
		 * and exported with root_squash (so has no root access).
		 */
		DEBUG(1,("vfs_GetWd: couldn't stat \".\" error %s "
			 "(NFS problem ?)\n", strerror(errno) ));
		goto nocache;
	}

	key = vfs_file_id_from_sbuf(conn, &smb_fname_dot->st);

	if (!memcache_lookup(smbd_memcache(), GETWD_CACHE,
			     data_blob_const(&key, sizeof(key)),
			     &cache_value)) {
		goto nocache;
	}

	SMB_ASSERT((cache_value.length > 0)
		   && (cache_value.data[cache_value.length-1] == '\0'));

	smb_fname_full = synthetic_smb_fname(ctx, (char *)cache_value.data,
					     NULL, NULL);
	if (smb_fname_full == NULL) {
		errno = ENOMEM;
		goto out;
	}

	if ((SMB_VFS_STAT(conn, smb_fname_full) == 0) &&
	    (smb_fname_dot->st.st_ex_dev == smb_fname_full->st.st_ex_dev) &&
	    (smb_fname_dot->st.st_ex_ino == smb_fname_full->st.st_ex_ino) &&
	    (S_ISDIR(smb_fname_dot->st.st_ex_mode))) {
		/*
		 * Ok, we're done
		 */
		result = talloc_strdup(ctx, smb_fname_full->base_name);
		if (result == NULL) {
			errno = ENOMEM;
		}
		goto out;
	}

 nocache:

	/*
	 * We don't have the information to hand so rely on traditional
	 * methods. The very slow getcwd, which spawns a process on some
	 * systems, or the not quite so bad getwd.
	 */

	current_dir = SMB_VFS_GETWD(conn);
	if (current_dir == NULL) {
		DEBUG(0, ("vfs_GetWd: SMB_VFS_GETWD call failed: %s\n",
			  strerror(errno)));
		goto out;
	}

	if (lp_getwd_cache() && VALID_STAT(smb_fname_dot->st)) {
		key = vfs_file_id_from_sbuf(conn, &smb_fname_dot->st);

		memcache_add(smbd_memcache(), GETWD_CACHE,
			     data_blob_const(&key, sizeof(key)),
			     data_blob_const(current_dir,
						strlen(current_dir)+1));
	}

	result = talloc_strdup(ctx, current_dir);
	if (result == NULL) {
		errno = ENOMEM;
	}

 out:
	TALLOC_FREE(smb_fname_dot);
	TALLOC_FREE(smb_fname_full);
	SAFE_FREE(current_dir);
	return result;
}


NTSTATUS smb_vfs_call_translate_name(struct vfs_handle_struct *handle,
				     const char *name,
				     enum vfs_translate_direction direction,
				     TALLOC_CTX *mem_ctx,
				     char **mapped_name)
{
	VFS_FIND(translate_name);
	return handle->fns->translate_name_fn(handle, name, direction, mem_ctx,
					      mapped_name);
}


int smb_vfs_call_link(struct vfs_handle_struct *handle, const char *oldpath,
		      const char *newpath)
{
	VFS_FIND(link);
	return handle->fns->link_fn(handle, oldpath, newpath);
}


int smb_vfs_call_chflags(struct vfs_handle_struct *handle, const char *path,
			 unsigned int flags)
{
	VFS_FIND(chflags);
	return handle->fns->chflags_fn(handle, path, flags);
}


int smb_vfs_call_ftruncate(struct vfs_handle_struct *handle,
			   struct files_struct *fsp, off_t offset)
{
	VFS_FIND(ftruncate);
	return handle->fns->ftruncate_fn(handle, fsp, offset);
}


NTSTATUS smb_vfs_call_copy_chunk_recv(struct vfs_handle_struct *handle,
				      struct tevent_req *req,
				      off_t *copied)
{
	VFS_FIND(copy_chunk_recv);
	return handle->fns->copy_chunk_recv_fn(handle, req, copied);
}


int smb_vfs_call_sys_acl_delete_def_file(struct vfs_handle_struct *handle,
					 const char *path)
{
	VFS_FIND(sys_acl_delete_def_file);
	return handle->fns->sys_acl_delete_def_file_fn(handle, path);
}


SMBC_attr_server(TALLOC_CTX *ctx,
                 SMBCCTX *context,
                 const char *server,
                 uint16_t port,
                 const char *share,
                 char **pp_workgroup,
                 char **pp_username,
                 char **pp_password)
{
        int flags;
	struct cli_state *ipc_cli = NULL;
	struct rpc_pipe_client *pipe_hnd = NULL;
        NTSTATUS nt_status;
	SMBCSRV *srv=NULL;
	SMBCSRV *ipc_srv=NULL;

	/*
	 * Use srv->cli->desthost and srv->cli->share instead of
	 * server and share below to connect to the actual share,
	 * i.e., a normal share or a referred share from
	 * 'msdfs proxy' share.
	 */
	srv = SMBC_server(ctx, context, true, server, port, share,
			pp_workgroup, pp_username, pp_password);
	if (!srv) {
		return NULL;
	}
	server = smbXcli_conn_remote_name(srv->cli->conn);
	share = srv->cli->share;

        /*
         * See if we've already created this special connection.  Reference
         * our "special" share name '*IPC$', which is an impossible real share
         * name due to the leading asterisk.
         */
        ipc_srv = SMBC_find_server(ctx, context, server, "*IPC$",
                                   pp_workgroup, pp_username, pp_password);
        if (!ipc_srv) {
		int signing_state = SMB_SIGNING_DEFAULT;

                /* We didn't find a cached connection.  Get the password */
		if (!*pp_password || (*pp_password)[0] == '\0') {
                        /* ... then retrieve it now. */
			SMBC_call_auth_fn(ctx, context, server, share,
                                          pp_workgroup,
                                          pp_username,
                                          pp_password);
			if (!*pp_workgroup || !*pp_username || !*pp_password) {
				errno = ENOMEM;
				return NULL;
			}
                }

                flags = 0;
                if (smbc_getOptionUseKerberos(context)) {
                        flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
                }
                if (smbc_getOptionUseCCache(context)) {
                        flags |= CLI_FULL_CONNECTION_USE_CCACHE;
                }
		if (context->internal->smb_encryption_level != SMBC_ENCRYPTLEVEL_NONE) {
			signing_state = SMB_SIGNING_REQUIRED;
		}

                nt_status = cli_full_connection(&ipc_cli,
						lp_netbios_name(), server,
						NULL, 0, "IPC$", "?????",
						*pp_username,
						*pp_workgroup,
						*pp_password,
						flags,
						signing_state);
                if (! NT_STATUS_IS_OK(nt_status)) {
                        DEBUG(1,("cli_full_connection failed! (%s)\n",
                                 nt_errstr(nt_status)));
                        errno = ENOTSUP;
                        return NULL;
                }

		if (context->internal->smb_encryption_level) {
			/* Attempt UNIX smb encryption. */
			if (!NT_STATUS_IS_OK(cli_force_encryption(ipc_cli,
                                                                  *pp_username,
                                                                  *pp_password,
                                                                  *pp_workgroup))) {

				/*
				 * context->smb_encryption_level ==
				 * 1 means don't fail if encryption can't be
				 * negotiated, == 2 means fail if encryption
				 * can't be negotiated.
				 */

				DEBUG(4,(" SMB encrypt failed on IPC$\n"));

				if (context->internal->smb_encryption_level == 2) {
		                        cli_shutdown(ipc_cli);
					errno = EPERM;
					return NULL;
				}
			}
			DEBUG(4,(" SMB encrypt ok on IPC$\n"));
		}

                ipc_srv = SMB_MALLOC_P(SMBCSRV);
                if (!ipc_srv) {
                        errno = ENOMEM;
                        cli_shutdown(ipc_cli);
                        return NULL;
                }

                ZERO_STRUCTP(ipc_srv);
                ipc_srv->cli = ipc_cli;

                nt_status = cli_rpc_pipe_open_noauth(
			ipc_srv->cli, &ndr_table_lsarpc, &pipe_hnd);
                if (!NT_STATUS_IS_OK(nt_status)) {
                        DEBUG(1, ("cli_nt_session_open fail!\n"));
                        errno = ENOTSUP;
                        cli_shutdown(ipc_srv->cli);
                        free(ipc_srv);
                        return NULL;
                }

                /*
                 * Some systems don't support
                 * SEC_FLAG_MAXIMUM_ALLOWED, but NT sends 0x2000000
                 * so we might as well do it too.
                 */

                nt_status = rpccli_lsa_open_policy(
                        pipe_hnd,
                        talloc_tos(),
                        True,
                        GENERIC_EXECUTE_ACCESS,
                        &ipc_srv->pol);

                if (!NT_STATUS_IS_OK(nt_status)) {
                        errno = SMBC_errno(context, ipc_srv->cli);
                        cli_shutdown(ipc_srv->cli);
                        free(ipc_srv);
                        return NULL;
                }

                /* now add it to the cache (internal or external) */

                errno = 0;      /* let cache function set errno if it likes */
                if (smbc_getFunctionAddCachedServer(context)(context, ipc_srv,
                                                             server,
                                                             "*IPC$",
                                                             *pp_workgroup,
                                                             *pp_username)) {
                        DEBUG(3, (" Failed to add server to cache\n"));
                        if (errno == 0) {
                                errno = ENOMEM;
                        }
                        cli_shutdown(ipc_srv->cli);
                        free(ipc_srv);
                        return NULL;
                }

                DLIST_ADD(context->internal->servers, ipc_srv);
        }

        return ipc_srv;
}


static void smb1cli_req_cancel_done(struct tevent_req *subreq)
{
	/* we do not care about the result */
	TALLOC_FREE(subreq);
}


static NTSTATUS smb2cli_inbuf_parse_compound(struct smbXcli_conn *conn,
					     uint8_t *buf,
					     size_t buflen,
					     TALLOC_CTX *mem_ctx,
					     struct iovec **piov, int *pnum_iov)
{
	struct iovec *iov;
	int num_iov = 0;
	size_t taken = 0;
	uint8_t *first_hdr = buf;
	size_t verified_buflen = 0;
	uint8_t *tf = NULL;
	size_t tf_len = 0;

	iov = talloc_array(mem_ctx, struct iovec, num_iov);
	if (iov == NULL) {
		return NT_STATUS_NO_MEMORY;
	}

	while (taken < buflen) {
		size_t len = buflen - taken;
		uint8_t *hdr = first_hdr + taken;
		struct iovec *cur;
		size_t full_size;
		size_t next_command_ofs;
		uint16_t body_size;
		struct iovec *iov_tmp;

		if (verified_buflen > taken) {
			len = verified_buflen - taken;
		} else {
			tf = NULL;
			tf_len = 0;
		}

		if (len < 4) {
			DEBUG(10, ("%d bytes left, expected at least %d\n",
				   (int)len, 4));
			goto inval;
		}
		if (IVAL(hdr, 0) == SMB2_TF_MAGIC) {
			struct smbXcli_session *s;
			uint64_t uid;
			struct iovec tf_iov[2];
			size_t enc_len;
			NTSTATUS status;

			if (len < SMB2_TF_HDR_SIZE) {
				DEBUG(10, ("%d bytes left, expected at least %d\n",
					   (int)len, SMB2_TF_HDR_SIZE));
				goto inval;
			}
			tf = hdr;
			tf_len = SMB2_TF_HDR_SIZE;
			taken += tf_len;

			hdr = first_hdr + taken;
			enc_len = IVAL(tf, SMB2_TF_MSG_SIZE);
			uid = BVAL(tf, SMB2_TF_SESSION_ID);

			if (len < SMB2_TF_HDR_SIZE + enc_len) {
				DEBUG(10, ("%d bytes left, expected at least %d\n",
					   (int)len,
					   (int)(SMB2_TF_HDR_SIZE + enc_len)));
				goto inval;
			}

			s = conn->sessions;
			for (; s; s = s->next) {
				if (s->smb2->session_id != uid) {
					continue;
				}
				break;
			}

			if (s == NULL) {
				DEBUG(10, ("unknown session_id %llu\n",
					   (unsigned long long)uid));
				goto inval;
			}

			tf_iov[0].iov_base = (void *)tf;
			tf_iov[0].iov_len = tf_len;
			tf_iov[1].iov_base = (void *)hdr;
			tf_iov[1].iov_len = enc_len;

			status = smb2_signing_decrypt_pdu(s->smb2->decryption_key,
							  conn->smb2.server.cipher,
							  tf_iov, 2);
			if (!NT_STATUS_IS_OK(status)) {
				TALLOC_FREE(iov);
				return status;
			}

			verified_buflen = taken + enc_len;
			len = enc_len;
		}

		/*
		 * We need the header plus the body length field
		 */

		if (len < SMB2_HDR_BODY + 2) {
			DEBUG(10, ("%d bytes left, expected at least %d\n",
				   (int)len, SMB2_HDR_BODY));
			goto inval;
		}
		if (IVAL(hdr, 0) != SMB2_MAGIC) {
			DEBUG(10, ("Got non-SMB2 PDU: %x\n",
				   IVAL(hdr, 0)));
			goto inval;
		}
		if (SVAL(hdr, 4) != SMB2_HDR_BODY) {
			DEBUG(10, ("Got HDR len %d, expected %d\n",
				   SVAL(hdr, 4), SMB2_HDR_BODY));
			goto inval;
		}

		full_size = len;
		next_command_ofs = IVAL(hdr, SMB2_HDR_NEXT_COMMAND);
		body_size = SVAL(hdr, SMB2_HDR_BODY);

		if (next_command_ofs != 0) {
			if (next_command_ofs < (SMB2_HDR_BODY + 2)) {
				goto inval;
			}
			if (next_command_ofs > full_size) {
				goto inval;
			}
			full_size = next_command_ofs;
		}
		if (body_size < 2) {
			goto inval;
		}
		body_size &= 0xfffe;

		if (body_size > (full_size - SMB2_HDR_BODY)) {
			goto inval;
		}

		iov_tmp = talloc_realloc(mem_ctx, iov, struct iovec,
					 num_iov + 4);
		if (iov_tmp == NULL) {
			TALLOC_FREE(iov);
			return NT_STATUS_NO_MEMORY;
		}
		iov = iov_tmp;
		cur = &iov[num_iov];
		num_iov += 4;

		cur[0].iov_base = tf;
		cur[0].iov_len  = tf_len;
		cur[1].iov_base = hdr;
		cur[1].iov_len  = SMB2_HDR_BODY;
		cur[2].iov_base = hdr + SMB2_HDR_BODY;
		cur[2].iov_len  = body_size;
		cur[3].iov_base = hdr + SMB2_HDR_BODY + body_size;
		cur[3].iov_len  = full_size - (SMB2_HDR_BODY + body_size);

		taken += full_size;
	}

	*piov = iov;
	*pnum_iov = num_iov;
	return NT_STATUS_OK;

inval:
	TALLOC_FREE(iov);
	return NT_STATUS_INVALID_NETWORK_RESPONSE;
}