static int parse_options(char *options, struct super_block *sb,
			 unsigned long *journal_devnum,
			 unsigned int *journal_ioprio,
			 ext4_fsblk_t *n_blocks_count, int is_remount)
{
	struct ext4_sb_info *sbi = EXT4_SB(sb);
	char *p;
	substring_t args[MAX_OPT_ARGS];
	int data_opt = 0;
	int option;
#ifdef CONFIG_QUOTA
	int qtype, qfmt;
	char *qname;
#endif
	ext4_fsblk_t last_block;

	if (!options)
		return 1;

	while ((p = strsep(&options, ",")) != NULL) {
		int token;
		if (!*p)
			continue;

		token = match_token(p, tokens, args);
		switch (token) {
		case Opt_bsd_df:
			clear_opt(sbi->s_mount_opt, MINIX_DF);
			break;
		case Opt_minix_df:
			set_opt(sbi->s_mount_opt, MINIX_DF);
			break;
		case Opt_grpid:
			set_opt(sbi->s_mount_opt, GRPID);
			break;
		case Opt_nogrpid:
			clear_opt(sbi->s_mount_opt, GRPID);
			break;
		case Opt_resuid:
			if (match_int(&args[0], &option))
				return 0;
			sbi->s_resuid = option;
			break;
		case Opt_resgid:
			if (match_int(&args[0], &option))
				return 0;
			sbi->s_resgid = option;
			break;
		case Opt_sb:
			/* handled by get_sb_block() instead of here */
			/* *sb_block = match_int(&args[0]); */
			break;
		case Opt_err_panic:
			clear_opt(sbi->s_mount_opt, ERRORS_CONT);
			clear_opt(sbi->s_mount_opt, ERRORS_RO);
			set_opt(sbi->s_mount_opt, ERRORS_PANIC);
			break;
		case Opt_err_ro:
			clear_opt(sbi->s_mount_opt, ERRORS_CONT);
			clear_opt(sbi->s_mount_opt, ERRORS_PANIC);
			set_opt(sbi->s_mount_opt, ERRORS_RO);
			break;
		case Opt_err_cont:
			clear_opt(sbi->s_mount_opt, ERRORS_RO);
			clear_opt(sbi->s_mount_opt, ERRORS_PANIC);
			set_opt(sbi->s_mount_opt, ERRORS_CONT);
			break;
		case Opt_nouid32:
			set_opt(sbi->s_mount_opt, NO_UID32);
			break;
		case Opt_debug:
			set_opt(sbi->s_mount_opt, DEBUG);
			break;
		case Opt_oldalloc:
			set_opt(sbi->s_mount_opt, OLDALLOC);
			break;
		case Opt_orlov:
			clear_opt(sbi->s_mount_opt, OLDALLOC);
			break;
#ifdef CONFIG_EXT4_FS_XATTR
		case Opt_user_xattr:
			set_opt(sbi->s_mount_opt, XATTR_USER);
			break;
		case Opt_nouser_xattr:
			clear_opt(sbi->s_mount_opt, XATTR_USER);
			break;
#else
		case Opt_user_xattr:
		case Opt_nouser_xattr:
			printk(KERN_ERR "EXT4 (no)user_xattr options "
			       "not supported\n");
			break;
#endif
#ifdef CONFIG_EXT4_FS_POSIX_ACL
		case Opt_acl:
			set_opt(sbi->s_mount_opt, POSIX_ACL);
			break;
		case Opt_noacl:
			clear_opt(sbi->s_mount_opt, POSIX_ACL);
			break;
#else
		case Opt_acl:
		case Opt_noacl:
			printk(KERN_ERR "EXT4 (no)acl options "
			       "not supported\n");
			break;
#endif
		case Opt_reservation:
			set_opt(sbi->s_mount_opt, RESERVATION);
			break;
		case Opt_noreservation:
			clear_opt(sbi->s_mount_opt, RESERVATION);
			break;
		case Opt_journal_update:
			/* @@@ FIXME */
			/* Eventually we will want to be able to create
			   a journal file here.  For now, only allow the
			   user to specify an existing inode to be the
			   journal file. */
			if (is_remount) {
				printk(KERN_ERR "EXT4-fs: cannot specify "
				       "journal on remount\n");
				return 0;
			}
			set_opt(sbi->s_mount_opt, UPDATE_JOURNAL);
			break;
		case Opt_journal_dev:
			if (is_remount) {
				printk(KERN_ERR "EXT4-fs: cannot specify "
				       "journal on remount\n");
				return 0;
			}
			if (match_int(&args[0], &option))
				return 0;
			*journal_devnum = option;
			break;
		case Opt_journal_checksum:
			set_opt(sbi->s_mount_opt, JOURNAL_CHECKSUM);
			break;
		case Opt_journal_async_commit:
			set_opt(sbi->s_mount_opt, JOURNAL_ASYNC_COMMIT);
			set_opt(sbi->s_mount_opt, JOURNAL_CHECKSUM);
			break;
		case Opt_noload:
			set_opt(sbi->s_mount_opt, NOLOAD);
			break;
		case Opt_commit:
			if (match_int(&args[0], &option))
				return 0;
			if (option < 0)
				return 0;
			if (option == 0)
				option = JBD2_DEFAULT_MAX_COMMIT_AGE;
			sbi->s_commit_interval = HZ * option;
			break;
		case Opt_max_batch_time:
			if (match_int(&args[0], &option))
				return 0;
			if (option < 0)
				return 0;
			if (option == 0)
				option = EXT4_DEF_MAX_BATCH_TIME;
			sbi->s_max_batch_time = option;
			break;
		case Opt_min_batch_time:
			if (match_int(&args[0], &option))
				return 0;
			if (option < 0)
				return 0;
			sbi->s_min_batch_time = option;
			break;
		case Opt_data_journal:
			data_opt = EXT4_MOUNT_JOURNAL_DATA;
			goto datacheck;
		case Opt_data_ordered:
			data_opt = EXT4_MOUNT_ORDERED_DATA;
			goto datacheck;
		case Opt_data_writeback:
			data_opt = EXT4_MOUNT_WRITEBACK_DATA;
		datacheck:
			if (is_remount) {
				if ((sbi->s_mount_opt & EXT4_MOUNT_DATA_FLAGS)
						!= data_opt) {
					printk(KERN_ERR
						"EXT4-fs: cannot change data "
						"mode on remount\n");
					return 0;
				}
			} else {
				sbi->s_mount_opt &= ~EXT4_MOUNT_DATA_FLAGS;
				sbi->s_mount_opt |= data_opt;
			}
			break;
		case Opt_data_err_abort:
			set_opt(sbi->s_mount_opt, DATA_ERR_ABORT);
			break;
		case Opt_data_err_ignore:
			clear_opt(sbi->s_mount_opt, DATA_ERR_ABORT);
			break;
#ifdef CONFIG_QUOTA
		case Opt_usrjquota:
			qtype = USRQUOTA;
			goto set_qf_name;
		case Opt_grpjquota:
			qtype = GRPQUOTA;
set_qf_name:
			if ((sb_any_quota_enabled(sb) ||
			     sb_any_quota_suspended(sb)) &&
			    !sbi->s_qf_names[qtype]) {
				printk(KERN_ERR
				       "EXT4-fs: Cannot change journaled "
				       "quota options when quota turned on.\n");
				return 0;
			}
			qname = match_strdup(&args[0]);
			if (!qname) {
				printk(KERN_ERR
					"EXT4-fs: not enough memory for "
					"storing quotafile name.\n");
				return 0;
			}
			if (sbi->s_qf_names[qtype] &&
			    strcmp(sbi->s_qf_names[qtype], qname)) {
				printk(KERN_ERR
					"EXT4-fs: %s quota file already "
					"specified.\n", QTYPE2NAME(qtype));
				kfree(qname);
				return 0;
			}
			sbi->s_qf_names[qtype] = qname;
			if (strchr(sbi->s_qf_names[qtype], '/')) {
				printk(KERN_ERR
					"EXT4-fs: quotafile must be on "
					"filesystem root.\n");
				kfree(sbi->s_qf_names[qtype]);
				sbi->s_qf_names[qtype] = NULL;
				return 0;
			}
			set_opt(sbi->s_mount_opt, QUOTA);
			break;
		case Opt_offusrjquota:
			qtype = USRQUOTA;
			goto clear_qf_name;
		case Opt_offgrpjquota:
			qtype = GRPQUOTA;
clear_qf_name:
			if ((sb_any_quota_enabled(sb) ||
			     sb_any_quota_suspended(sb)) &&
			    sbi->s_qf_names[qtype]) {
				printk(KERN_ERR "EXT4-fs: Cannot change "
					"journaled quota options when "
					"quota turned on.\n");
				return 0;
			}
			/*
			 * The space will be released later when all options
			 * are confirmed to be correct
			 */
			sbi->s_qf_names[qtype] = NULL;
			break;
		case Opt_jqfmt_vfsold:
			qfmt = QFMT_VFS_OLD;
			goto set_qf_format;
		case Opt_jqfmt_vfsv0:
			qfmt = QFMT_VFS_V0;
set_qf_format:
			if ((sb_any_quota_enabled(sb) ||
			     sb_any_quota_suspended(sb)) &&
			    sbi->s_jquota_fmt != qfmt) {
				printk(KERN_ERR "EXT4-fs: Cannot change "
					"journaled quota options when "
					"quota turned on.\n");
				return 0;
			}
			sbi->s_jquota_fmt = qfmt;
			break;
		case Opt_quota:
		case Opt_usrquota:
			set_opt(sbi->s_mount_opt, QUOTA);
			set_opt(sbi->s_mount_opt, USRQUOTA);
			break;
		case Opt_grpquota:
			set_opt(sbi->s_mount_opt, QUOTA);
			set_opt(sbi->s_mount_opt, GRPQUOTA);
			break;
		case Opt_noquota:
			if (sb_any_quota_enabled(sb)) {
				printk(KERN_ERR "EXT4-fs: Cannot change quota "
					"options when quota turned on.\n");
				return 0;
			}
			clear_opt(sbi->s_mount_opt, QUOTA);
			clear_opt(sbi->s_mount_opt, USRQUOTA);
			clear_opt(sbi->s_mount_opt, GRPQUOTA);
			break;
#else
		case Opt_quota:
		case Opt_usrquota:
		case Opt_grpquota:
			printk(KERN_ERR
				"EXT4-fs: quota options not supported.\n");
			break;
		case Opt_usrjquota:
		case Opt_grpjquota:
		case Opt_offusrjquota:
		case Opt_offgrpjquota:
		case Opt_jqfmt_vfsold:
		case Opt_jqfmt_vfsv0:
			printk(KERN_ERR
				"EXT4-fs: journaled quota options not "
				"supported.\n");
			break;
		case Opt_noquota:
			break;
#endif
		case Opt_abort:
			set_opt(sbi->s_mount_opt, ABORT);
			break;
		case Opt_barrier:
			if (match_int(&args[0], &option))
				return 0;
			if (option)
				set_opt(sbi->s_mount_opt, BARRIER);
			else
				clear_opt(sbi->s_mount_opt, BARRIER);
			break;
		case Opt_ignore:
			break;
		case Opt_resize:
			if (!is_remount) {
				printk("EXT4-fs: resize option only available "
					"for remount\n");
				return 0;
			}
			if (match_int(&args[0], &option) != 0)
				return 0;
			*n_blocks_count = option;
			break;
		case Opt_nobh:
			set_opt(sbi->s_mount_opt, NOBH);
			break;
		case Opt_bh:
			clear_opt(sbi->s_mount_opt, NOBH);
			break;
		case Opt_extents:
			if (!EXT4_HAS_INCOMPAT_FEATURE(sb,
					EXT4_FEATURE_INCOMPAT_EXTENTS)) {
				ext4_warning(sb, __func__,
					"extents feature not enabled "
					"on this filesystem, use tune2fs");
				return 0;
			}
			set_opt(sbi->s_mount_opt, EXTENTS);
			break;
		case Opt_noextents:
			/*
			 * When e2fsprogs support resizing an already existing
			 * ext3 file system to greater than 2**32 we need to
			 * add support to block allocator to handle growing
			 * already existing block  mapped inode so that blocks
			 * allocated for them fall within 2**32
			 */
			last_block = ext4_blocks_count(sbi->s_es) - 1;
			if (last_block  > 0xffffffffULL) {
				printk(KERN_ERR "EXT4-fs: Filesystem too "
						"large to mount with "
						"-o noextents options\n");
				return 0;
			}
			clear_opt(sbi->s_mount_opt, EXTENTS);
			break;
		case Opt_i_version:
			set_opt(sbi->s_mount_opt, I_VERSION);
			sb->s_flags |= MS_I_VERSION;
			break;
		case Opt_nodelalloc:
			clear_opt(sbi->s_mount_opt, DELALLOC);
			break;
		case Opt_stripe:
			if (match_int(&args[0], &option))
				return 0;
			if (option < 0)
				return 0;
			sbi->s_stripe = option;
			break;
		case Opt_delalloc:
			set_opt(sbi->s_mount_opt, DELALLOC);
			break;
		case Opt_inode_readahead_blks:
			if (match_int(&args[0], &option))
				return 0;
			if (option < 0 || option > (1 << 30))
				return 0;
			sbi->s_inode_readahead_blks = option;
			break;
		case Opt_journal_ioprio:
			if (match_int(&args[0], &option))
				return 0;
			if (option < 0 || option > 7)
				break;
			*journal_ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE,
							    option);
			break;
		default:
			printk(KERN_ERR
			       "EXT4-fs: Unrecognized mount option \"%s\" "
			       "or missing value\n", p);
			return 0;
		}
	}
#ifdef CONFIG_QUOTA
	if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]) {
		if ((sbi->s_mount_opt & EXT4_MOUNT_USRQUOTA) &&
		     sbi->s_qf_names[USRQUOTA])
			clear_opt(sbi->s_mount_opt, USRQUOTA);

		if ((sbi->s_mount_opt & EXT4_MOUNT_GRPQUOTA) &&
		     sbi->s_qf_names[GRPQUOTA])
			clear_opt(sbi->s_mount_opt, GRPQUOTA);

		if ((sbi->s_qf_names[USRQUOTA] &&
				(sbi->s_mount_opt & EXT4_MOUNT_GRPQUOTA)) ||
		    (sbi->s_qf_names[GRPQUOTA] &&
				(sbi->s_mount_opt & EXT4_MOUNT_USRQUOTA))) {
			printk(KERN_ERR "EXT4-fs: old and new quota "
					"format mixing.\n");
			return 0;
		}

		if (!sbi->s_jquota_fmt) {
			printk(KERN_ERR "EXT4-fs: journaled quota format "
					"not specified.\n");
			return 0;
		}
	} else {
		if (sbi->s_jquota_fmt) {
			printk(KERN_ERR "EXT4-fs: journaled quota format "
					"specified with no journaling "
					"enabled.\n");
			return 0;
		}
	}
#endif
	return 1;
}


static void __exit exit_ext4_fs(void)
{
	unregister_filesystem(&ext4_fs_type);
#ifdef CONFIG_EXT4DEV_COMPAT
	unregister_filesystem(&ext4dev_fs_type);
#endif
	destroy_inodecache();
	exit_ext4_xattr();
	exit_ext4_mballoc();
	remove_proc_entry("fs/ext4", NULL);
}


static ext4_fsblk_t descriptor_loc(struct super_block *sb,
				ext4_fsblk_t logical_sb_block, int nr)
{
	struct ext4_sb_info *sbi = EXT4_SB(sb);
	ext4_group_t bg, first_meta_bg;
	int has_super = 0;

	first_meta_bg = le32_to_cpu(sbi->s_es->s_first_meta_bg);

	if (!EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_META_BG) ||
	    nr < first_meta_bg)
		return logical_sb_block + nr + 1;
	bg = sbi->s_desc_per_block * nr;
	if (ext4_bg_has_super(sb, bg))
		has_super = 1;
	return (has_super + ext4_group_first_block_no(sb, bg));
}


void selinux_netlbl_sk_security_free(struct sk_security_struct *ssec)
{
	if (ssec->nlbl_secattr != NULL)
		netlbl_secattr_free(ssec->nlbl_secattr);
}


static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb,
					   struct netlbl_lsm_secattr *secattr,
					   u32 *sid)
{
	int rc;

	rc = security_netlbl_secattr_to_sid(secattr, sid);
	if (rc == 0 &&
	    (secattr->flags & NETLBL_SECATTR_CACHEABLE) &&
	    (secattr->flags & NETLBL_SECATTR_CACHE))
		netlbl_cache_add(skb, secattr);

	return rc;
}


static void inotify_dev_event_dequeue(struct inotify_device *dev)
{
	if (!list_empty(&dev->events)) {
		struct inotify_kernel_event *kevent;
		kevent = inotify_dev_get_event(dev);
		remove_kevent(dev, kevent);
		free_kevent(kevent);
	}
}


int ecryptfs_write_metadata(struct dentry *ecryptfs_dentry)
{
	struct ecryptfs_crypt_stat *crypt_stat =
		&ecryptfs_inode_to_private(ecryptfs_dentry->d_inode)->crypt_stat;
	unsigned int order;
	char *virt;
	size_t virt_len;
	size_t size = 0;
	int rc = 0;

	if (likely(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) {
		if (!(crypt_stat->flags & ECRYPTFS_KEY_VALID)) {
			printk(KERN_ERR "Key is invalid; bailing out\n");
			rc = -EINVAL;
			goto out;
		}
	} else {
		printk(KERN_WARNING "%s: Encrypted flag not set\n",
		       __func__);
		rc = -EINVAL;
		goto out;
	}
	virt_len = crypt_stat->num_header_bytes_at_front;
	order = get_order(virt_len);
	/* Released in this function */
	virt = (char *)ecryptfs_get_zeroed_pages(GFP_KERNEL, order);
	if (!virt) {
		printk(KERN_ERR "%s: Out of memory\n", __func__);
		rc = -ENOMEM;
		goto out;
	}
	rc = ecryptfs_write_headers_virt(virt, virt_len, &size, crypt_stat,
					 ecryptfs_dentry);
	if (unlikely(rc)) {
		printk(KERN_ERR "%s: Error whilst writing headers; rc = [%d]\n",
		       __func__, rc);
		goto out_free;
	}
	if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR)
		rc = ecryptfs_write_metadata_to_xattr(ecryptfs_dentry, virt,
						      size);
	else
		rc = ecryptfs_write_metadata_to_contents(ecryptfs_dentry, virt,
							 virt_len);
	if (rc) {
		printk(KERN_ERR "%s: Error writing metadata out to lower file; "
		       "rc = [%d]\n", __func__, rc);
		goto out_free;
	}
out_free:
	free_pages((unsigned long)virt, order);
out:
	return rc;
}


int virt_to_scatterlist(const void *addr, int size, struct scatterlist *sg,
			int sg_size)
{
	int i = 0;
	struct page *pg;
	int offset;
	int remainder_of_page;

	sg_init_table(sg, sg_size);

	while (size > 0 && i < sg_size) {
		pg = virt_to_page(addr);
		offset = offset_in_page(addr);
		if (sg)
			sg_set_page(&sg[i], pg, 0, offset);
		remainder_of_page = PAGE_CACHE_SIZE - offset;
		if (size >= remainder_of_page) {
			if (sg)
				sg[i].length = remainder_of_page;
			addr += remainder_of_page;
			size -= remainder_of_page;
		} else {
			if (sg)
				sg[i].length = size;
			addr += size;
			size = 0;
		}
		i++;
	}
	if (size > 0)
		return -ENOMEM;
	return i;
}


static int decrypt_scatterlist(struct ecryptfs_crypt_stat *crypt_stat,
			       struct scatterlist *dest_sg,
			       struct scatterlist *src_sg, int size,
			       unsigned char *iv)
{
	struct blkcipher_desc desc = {
		.tfm = crypt_stat->tfm,
		.info = iv,
		.flags = CRYPTO_TFM_REQ_MAY_SLEEP
	};
	int rc = 0;

	/* Consider doing this once, when the file is opened */
	mutex_lock(&crypt_stat->cs_tfm_mutex);
	rc = crypto_blkcipher_setkey(crypt_stat->tfm, crypt_stat->key,
				     crypt_stat->key_size);
	if (rc) {
		ecryptfs_printk(KERN_ERR, "Error setting key; rc = [%d]\n",
				rc);
		mutex_unlock(&crypt_stat->cs_tfm_mutex);
		rc = -EINVAL;
		goto out;
	}
	ecryptfs_printk(KERN_DEBUG, "Decrypting [%d] bytes.\n", size);
	rc = crypto_blkcipher_decrypt_iv(&desc, dest_sg, src_sg, size);
	mutex_unlock(&crypt_stat->cs_tfm_mutex);
	if (rc) {
		ecryptfs_printk(KERN_ERR, "Error decrypting; rc = [%d]\n",
				rc);
		goto out;
	}
	rc = size;
out:
	return rc;
}


void ecryptfs_destroy_crypt_stat(struct ecryptfs_crypt_stat *crypt_stat)
{
	struct ecryptfs_key_sig *key_sig, *key_sig_tmp;

	if (crypt_stat->tfm)
		crypto_free_blkcipher(crypt_stat->tfm);
	if (crypt_stat->hash_tfm)
		crypto_free_hash(crypt_stat->hash_tfm);
	mutex_lock(&crypt_stat->keysig_list_mutex);
	list_for_each_entry_safe(key_sig, key_sig_tmp,
				 &crypt_stat->keysig_list, crypt_stat_list) {
		list_del(&key_sig->crypt_stat_list);
		kmem_cache_free(ecryptfs_key_sig_cache, key_sig);
	}
	mutex_unlock(&crypt_stat->keysig_list_mutex);
	memset(crypt_stat, 0, sizeof(struct ecryptfs_crypt_stat));
}


clear_selection(void) {
	highlight_pointer(-1); /* hide the pointer */
	if (sel_start != -1) {
		highlight(sel_start, sel_end);
		sel_start = -1;
	}
}


int set_selection(const struct tiocl_selection __user *sel, struct tty_struct *tty)
{
	struct vc_data *vc = vc_cons[fg_console].d;
	int sel_mode, new_sel_start, new_sel_end, spc;
	char *bp, *obp;
	int i, ps, pe, multiplier;
	u16 c;
	struct kbd_struct *kbd = kbd_table + fg_console;

	poke_blanked_console();

	{ unsigned short xs, ys, xe, ye;

	  if (!access_ok(VERIFY_READ, sel, sizeof(*sel)))
		return -EFAULT;
	  __get_user(xs, &sel->xs);
	  __get_user(ys, &sel->ys);
	  __get_user(xe, &sel->xe);
	  __get_user(ye, &sel->ye);
	  __get_user(sel_mode, &sel->sel_mode);
	  xs--; ys--; xe--; ye--;
	  xs = limit(xs, vc->vc_cols - 1);
	  ys = limit(ys, vc->vc_rows - 1);
	  xe = limit(xe, vc->vc_cols - 1);
	  ye = limit(ye, vc->vc_rows - 1);
	  ps = ys * vc->vc_size_row + (xs << 1);
	  pe = ye * vc->vc_size_row + (xe << 1);

	  if (sel_mode == TIOCL_SELCLEAR) {
	      /* useful for screendump without selection highlights */
	      clear_selection();
	      return 0;
	  }

	  if (mouse_reporting() && (sel_mode & TIOCL_SELMOUSEREPORT)) {
	      mouse_report(tty, sel_mode & TIOCL_SELBUTTONMASK, xs, ys);
	      return 0;
	  }
        }

	if (ps > pe)	/* make sel_start <= sel_end */
	{
		int tmp = ps;
		ps = pe;
		pe = tmp;
	}

	if (sel_cons != vc_cons[fg_console].d) {
		clear_selection();
		sel_cons = vc_cons[fg_console].d;
	}
	use_unicode = kbd && kbd->kbdmode == VC_UNICODE;

	switch (sel_mode)
	{
		case TIOCL_SELCHAR:	/* character-by-character selection */
			new_sel_start = ps;
			new_sel_end = pe;
			break;
		case TIOCL_SELWORD:	/* word-by-word selection */
			spc = isspace(sel_pos(ps));
			for (new_sel_start = ps; ; ps -= 2)
			{
				if ((spc && !isspace(sel_pos(ps))) ||
				    (!spc && !inword(sel_pos(ps))))
					break;
				new_sel_start = ps;
				if (!(ps % vc->vc_size_row))
					break;
			}
			spc = isspace(sel_pos(pe));
			for (new_sel_end = pe; ; pe += 2)
			{
				if ((spc && !isspace(sel_pos(pe))) ||
				    (!spc && !inword(sel_pos(pe))))
					break;
				new_sel_end = pe;
				if (!((pe + 2) % vc->vc_size_row))
					break;
			}
			break;
		case TIOCL_SELLINE:	/* line-by-line selection */
			new_sel_start = ps - ps % vc->vc_size_row;
			new_sel_end = pe + vc->vc_size_row
				    - pe % vc->vc_size_row - 2;
			break;
		case TIOCL_SELPOINTER:
			highlight_pointer(pe);
			return 0;
		default:
			return -EINVAL;
	}

	/* remove the pointer */
	highlight_pointer(-1);

	/* select to end of line if on trailing space */
	if (new_sel_end > new_sel_start &&
		!atedge(new_sel_end, vc->vc_size_row) &&
		isspace(sel_pos(new_sel_end))) {
		for (pe = new_sel_end + 2; ; pe += 2)
			if (!isspace(sel_pos(pe)) ||
			    atedge(pe, vc->vc_size_row))
				break;
		if (isspace(sel_pos(pe)))
			new_sel_end = pe;
	}
	if (sel_start == -1)	/* no current selection */
		highlight(new_sel_start, new_sel_end);
	else if (new_sel_start == sel_start)
	{
		if (new_sel_end == sel_end)	/* no action required */
			return 0;
		else if (new_sel_end > sel_end)	/* extend to right */
			highlight(sel_end + 2, new_sel_end);
		else				/* contract from right */
			highlight(new_sel_end + 2, sel_end);
	}
	else if (new_sel_end == sel_end)
	{
		if (new_sel_start < sel_start)	/* extend to left */
			highlight(new_sel_start, sel_start - 2);
		else				/* contract from left */
			highlight(sel_start, new_sel_start - 2);
	}
	else	/* some other case; start selection from scratch */
	{
		clear_selection();
		highlight(new_sel_start, new_sel_end);
	}
	sel_start = new_sel_start;
	sel_end = new_sel_end;

	/* Allocate a new buffer before freeing the old one ... */
	multiplier = use_unicode ? 3 : 1;  /* chars can take up to 3 bytes */
	bp = kmalloc(((sel_end-sel_start)/2+1)*multiplier, GFP_KERNEL);
	if (!bp) {
		printk(KERN_WARNING "selection: kmalloc() failed\n");
		clear_selection();
		return -ENOMEM;
	}
	kfree(sel_buffer);
	sel_buffer = bp;

	obp = bp;
	for (i = sel_start; i <= sel_end; i += 2) {
		c = sel_pos(i);
		if (use_unicode)
			bp += store_utf8(c, bp);
		else
			*bp++ = c;
		if (!isspace(c))
			obp = bp;
		if (! ((i + 2) % vc->vc_size_row)) {
			/* strip trailing blanks from line and add newline,
			   unless non-space at end of line. */
			if (obp != bp) {
				bp = obp;
				*bp++ = '\r';
			}
			obp = bp;
		}
	}
	sel_buffer_lth = bp - sel_buffer;
	return 0;
}


static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
	struct dentry	*dentry = filp->f_path.dentry;
	struct inode	*inode = dentry->d_inode;
	nfs_readdir_descriptor_t my_desc,
			*desc = &my_desc;
	struct nfs_entry my_entry;
	struct nfs_fh	 fh;
	struct nfs_fattr fattr;
	long		res;

	dfprintk(VFS, "NFS: readdir(%s/%s) starting at cookie %Lu\n",
			dentry->d_parent->d_name.name, dentry->d_name.name,
			(long long)filp->f_pos);
	nfs_inc_stats(inode, NFSIOS_VFSGETDENTS);

	lock_kernel();

	res = nfs_revalidate_mapping_nolock(inode, filp->f_mapping);
	if (res < 0) {
		unlock_kernel();
		return res;
	}

	/*
	 * filp->f_pos points to the dirent entry number.
	 * *desc->dir_cookie has the cookie for the next entry. We have
	 * to either find the entry with the appropriate number or
	 * revalidate the cookie.
	 */
	memset(desc, 0, sizeof(*desc));

	desc->file = filp;
	desc->dir_cookie = &((struct nfs_open_context *)filp->private_data)->dir_cookie;
	desc->decode = NFS_PROTO(inode)->decode_dirent;
	desc->plus = NFS_USE_READDIRPLUS(inode);

	my_entry.cookie = my_entry.prev_cookie = 0;
	my_entry.eof = 0;
	my_entry.fh = &fh;
	my_entry.fattr = &fattr;
	nfs_fattr_init(&fattr);
	desc->entry = &my_entry;

	while(!desc->entry->eof) {
		res = readdir_search_pagecache(desc);

		if (res == -EBADCOOKIE) {
			/* This means either end of directory */
			if (*desc->dir_cookie && desc->entry->cookie != *desc->dir_cookie) {
				/* Or that the server has 'lost' a cookie */
				res = uncached_readdir(desc, dirent, filldir);
				if (res >= 0)
					continue;
			}
			res = 0;
			break;
		}
		if (res == -ETOOSMALL && desc->plus) {
			clear_bit(NFS_INO_ADVISE_RDPLUS, &NFS_FLAGS(inode));
			nfs_zap_caches(inode);
			desc->plus = 0;
			desc->entry->eof = 0;
			continue;
		}
		if (res < 0)
			break;

		res = nfs_do_filldir(desc, dirent, filldir);
		if (res < 0) {
			res = 0;
			break;
		}
	}
	unlock_kernel();
	if (res > 0)
		res = 0;
	dfprintk(VFS, "NFS: readdir(%s/%s) returns %ld\n",
			dentry->d_parent->d_name.name, dentry->d_name.name,
			res);
	return res;
}


static int nfs_start_lockd(struct nfs_server *server)
{
	int error = 0;

	if (server->nfs_client->cl_nfsversion > 3)
		goto out;
	if (server->flags & NFS_MOUNT_NONLM)
		goto out;
	error = lockd_up((server->flags & NFS_MOUNT_TCP) ?
			IPPROTO_TCP : IPPROTO_UDP);
	if (error < 0)
		server->flags |= NFS_MOUNT_NONLM;
	else
		server->destroy = nfs_destroy_server;
out:
	return error;
}


static struct nfs_client *nfs_alloc_client(const char *hostname,
					   const struct sockaddr_in *addr,
					   int nfsversion)
{
	struct nfs_client *clp;

	if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL)
		goto error_0;

	if (nfsversion == 4) {
		if (nfs_callback_up() < 0)
			goto error_2;
		__set_bit(NFS_CS_CALLBACK, &clp->cl_res_state);
	}

	atomic_set(&clp->cl_count, 1);
	clp->cl_cons_state = NFS_CS_INITING;

	clp->cl_nfsversion = nfsversion;
	memcpy(&clp->cl_addr, addr, sizeof(clp->cl_addr));

	if (hostname) {
		clp->cl_hostname = kstrdup(hostname, GFP_KERNEL);
		if (!clp->cl_hostname)
			goto error_3;
	}

	INIT_LIST_HEAD(&clp->cl_superblocks);
	clp->cl_rpcclient = ERR_PTR(-EINVAL);

#ifdef CONFIG_NFS_V4
	init_rwsem(&clp->cl_sem);
	INIT_LIST_HEAD(&clp->cl_delegations);
	spin_lock_init(&clp->cl_lock);
	INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state);
	rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client");
	clp->cl_boot_time = CURRENT_TIME;
	clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED;
#endif

	return clp;

error_3:
	if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state))
		nfs_callback_down();
error_2:
	kfree(clp);
error_0:
	return NULL;
}


int nfs4_path_walk(struct nfs_server *server,
		   struct nfs_fh *mntfh,
		   const char *path)
{
	struct nfs_fsinfo fsinfo;
	struct nfs_fattr fattr;
	struct nfs_fh lastfh;
	struct qstr name;
	int ret;

	dprintk("--> nfs4_path_walk(,,%s)\n", path);

	fsinfo.fattr = &fattr;
	nfs_fattr_init(&fattr);

	/* Eat leading slashes */
	while (*path == '/')
		path++;

	/* Start by getting the root filehandle from the server */
	ret = server->nfs_client->rpc_ops->getroot(server, mntfh, &fsinfo);
	if (ret < 0) {
		dprintk("nfs4_get_root: getroot error = %d\n", -ret);
		return ret;
	}

	if (fattr.type != NFDIR) {
		printk(KERN_ERR "nfs4_get_root:"
		       " getroot encountered non-directory\n");
		return -ENOTDIR;
	}

	/* FIXME: It is quite valid for the server to return a referral here */
	if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) {
		printk(KERN_ERR "nfs4_get_root:"
		       " getroot obtained referral\n");
		return -EREMOTE;
	}

next_component:
	dprintk("Next: %s\n", path);

	/* extract the next bit of the path */
	if (!*path)
		goto path_walk_complete;

	name.name = path;
	while (*path && *path != '/')
		path++;
	name.len = path - (const char *) name.name;

	if (name.len > NFS4_MAXNAMLEN)
		return -ENAMETOOLONG;

eat_dot_dir:
	while (*path == '/')
		path++;

	if (path[0] == '.' && (path[1] == '/' || !path[1])) {
		path += 2;
		goto eat_dot_dir;
	}

	/* FIXME: Why shouldn't the user be able to use ".." in the path? */
	if (path[0] == '.' && path[1] == '.' && (path[2] == '/' || !path[2])
	    ) {
		printk(KERN_ERR "nfs4_get_root:"
		       " Mount path contains reference to \"..\"\n");
		return -EINVAL;
	}

	/* lookup the next FH in the sequence */
	memcpy(&lastfh, mntfh, sizeof(lastfh));

	dprintk("LookupFH: %*.*s [%s]\n", name.len, name.len, name.name, path);

	ret = server->nfs_client->rpc_ops->lookupfh(server, &lastfh, &name,
						    mntfh, &fattr);
	if (ret < 0) {
		dprintk("nfs4_get_root: getroot error = %d\n", -ret);
		return ret;
	}

	if (fattr.type != NFDIR) {
		printk(KERN_ERR "nfs4_get_root:"
		       " lookupfh encountered non-directory\n");
		return -ENOTDIR;
	}

	/* FIXME: Referrals are quite valid here too */
	if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) {
		printk(KERN_ERR "nfs4_get_root:"
		       " lookupfh obtained referral\n");
		return -EREMOTE;
	}

	goto next_component;

path_walk_complete:
	memcpy(&server->fsid, &fattr.fsid, sizeof(server->fsid));
	dprintk("<-- nfs4_path_walk() = 0\n");
	return 0;
}


void nfs_put_client(struct nfs_client *clp)
{
	if (!clp)
		return;

	dprintk("--> nfs_put_client({%d})\n", atomic_read(&clp->cl_count));

	if (atomic_dec_and_lock(&clp->cl_count, &nfs_client_lock)) {
		list_del(&clp->cl_share_link);
		spin_unlock(&nfs_client_lock);

		BUG_ON(!list_empty(&clp->cl_superblocks));

		nfs_free_client(clp);
	}
}


static int nfs_create(struct inode *dir, struct dentry *dentry, int mode,
		struct nameidata *nd)
{
	struct iattr attr;
	int error;
	int open_flags = 0;

	dfprintk(VFS, "NFS: create(%s/%ld), %s\n",
			dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);

	attr.ia_mode = mode;
	attr.ia_valid = ATTR_MODE;

	if ((nd->flags & LOOKUP_CREATE) != 0)
		open_flags = nd->intent.open.flags;

	lock_kernel();
	nfs_begin_data_update(dir);
	error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, nd);
	nfs_end_data_update(dir);
	if (error != 0)
		goto out_err;
	nfs_renew_times(dentry);
	nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
	unlock_kernel();
	return 0;
out_err:
	unlock_kernel();
	d_drop(dentry);
	return error;
}


nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
{
	struct iattr attr;
	int status;

	dfprintk(VFS, "NFS: mknod(%s/%ld), %s\n",
			dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);

	if (!new_valid_dev(rdev))
		return -EINVAL;

	attr.ia_mode = mode;
	attr.ia_valid = ATTR_MODE;

	lock_kernel();
	nfs_begin_data_update(dir);
	status = NFS_PROTO(dir)->mknod(dir, dentry, &attr, rdev);
	nfs_end_data_update(dir);
	if (status != 0)
		goto out_err;
	nfs_renew_times(dentry);
	nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
	unlock_kernel();
	return 0;
out_err:
	unlock_kernel();
	d_drop(dentry);
	return status;
}


int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
				struct nfs_fattr *fattr)
{
	struct inode *inode;
	int error = -EACCES;

	/* We may have been initialized further down */
	if (dentry->d_inode)
		return 0;
	if (fhandle->size == 0) {
		struct inode *dir = dentry->d_parent->d_inode;
		error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
		if (error)
			return error;
	}
	if (!(fattr->valid & NFS_ATTR_FATTR)) {
		struct nfs_server *server = NFS_SB(dentry->d_sb);
		error = server->nfs_client->rpc_ops->getattr(server, fhandle, fattr);
		if (error < 0)
			return error;
	}
	inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
	error = PTR_ERR(inode);
	if (IS_ERR(inode))
		return error;
	d_instantiate(dentry, inode);
	if (d_unhashed(dentry))
		d_rehash(dentry);
	return 0;
}


int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size)
{
	if (udev_monitor == NULL)
		return -1;
	return setsockopt(udev_monitor->sock, SOL_SOCKET, SO_RCVBUFFORCE, &size, sizeof(size));
}


static int utf8_encoded_valid_unichar(const char *str)
{
	int len;
	int unichar;
	int i;

	len = utf8_encoded_expected_len(str);
	if (len == 0)
		return -1;

	/* ascii is valid */
	if (len == 1)
		return 1;

	/* check if expected encoded chars are available */
	for (i = 0; i < len; i++)
		if ((str[i] & 0x80) != 0x80)
			return -1;

	unichar = utf8_encoded_to_unichar(str);

	/* check if encoded length matches encoded value */
	if (utf8_unichar_to_encoded_len(unichar) != len)
		return -1;

	/* check if value has valid range */
	if (!utf8_unichar_valid_range(unichar))
		return -1;

	return len;
}


void util_set_fd_cloexec(int fd)
{
	int flags;

	flags = fcntl(fd, F_GETFD);
	if (flags < 0)
		flags = FD_CLOEXEC;
	else
		flags |= FD_CLOEXEC;
	fcntl(fd, F_SETFD, flags);
}


static void async_request_timeout_handler(struct event_context *ctx,
					struct timed_event *te,
					const struct timeval *now,
					void *private_data)
{
	struct winbindd_async_request *state =
		talloc_get_type_abort(private_data, struct winbindd_async_request);

	DEBUG(0,("async_request_timeout_handler: child pid %u is not responding. "
		"Closing connection to it.\n",
		state->child_pid ));

	/* Deal with the reply - set to error. */
	async_reply_recv(private_data, False);
}


static void async_request_fail(struct winbindd_async_request *state)
{
	DLIST_REMOVE(state->child->requests, state);

	TALLOC_FREE(state->reply_timeout_event);

	SMB_ASSERT(state->child_pid != (pid_t)0);

	/* If not already reaped, send kill signal to child. */
	if (state->child->pid == state->child_pid) {
		kill(state->child_pid, SIGTERM);

		/*
		 * Close the socket to the child.
		 */
		winbind_child_died(state->child_pid);
	}

	state->response->length = sizeof(struct winbindd_response);
	state->response->result = WINBINDD_ERROR;
	state->continuation(state->private_data, False);
}


static int handle_halt(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
{
	skip_emulated_instruction(vcpu);
	return kvm_emulate_halt(vcpu);
}


static inline int cpu_has_vmx_invept_context(void)
{
	return (!!(vmx_capability.ept & VMX_EPT_EXTENT_CONTEXT_BIT));
}


static void vmx_update_window_states(struct kvm_vcpu *vcpu)
{
	u32 guest_intr = vmcs_read32(GUEST_INTERRUPTIBILITY_INFO);

	vcpu->arch.nmi_window_open =
		!(guest_intr & (GUEST_INTR_STATE_STI |
				GUEST_INTR_STATE_MOV_SS |
				GUEST_INTR_STATE_NMI));
	if (!cpu_has_virtual_nmis() && to_vmx(vcpu)->soft_vnmi_blocked)
		vcpu->arch.nmi_window_open = 0;

	vcpu->arch.interrupt_window_open =
		((vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_IF) &&
		 !(guest_intr & (GUEST_INTR_STATE_STI |
				 GUEST_INTR_STATE_MOV_SS)));
}


static gva_t rmode_tss_base(struct kvm *kvm)
{
	if (!kvm->arch.tss_addr) {
		gfn_t base_gfn = kvm->memslots[0].base_gfn +
				 kvm->memslots[0].npages - 3;
		return base_gfn << PAGE_SHIFT;
	}
	return kvm->arch.tss_addr;
}


static void vmx_set_efer(struct kvm_vcpu *vcpu, u64 efer)
{
	struct vcpu_vmx *vmx = to_vmx(vcpu);
	struct kvm_msr_entry *msr = find_msr_entry(vmx, MSR_EFER);

	vcpu->arch.shadow_efer = efer;
	if (!msr)
		return;
	if (efer & EFER_LMA) {
		vmcs_write32(VM_ENTRY_CONTROLS,
			     vmcs_read32(VM_ENTRY_CONTROLS) |
			     VM_ENTRY_IA32E_MODE);
		msr->data = efer;
	} else {
		vmcs_write32(VM_ENTRY_CONTROLS,
			     vmcs_read32(VM_ENTRY_CONTROLS) &
			     ~VM_ENTRY_IA32E_MODE);

		msr->data = efer & ~EFER_LME;
	}
	setup_msrs(vmx);
}


static void exit_lmode(struct kvm_vcpu *vcpu)
{
	vcpu->arch.shadow_efer &= ~EFER_LMA;

	vmcs_write32(VM_ENTRY_CONTROLS,
		     vmcs_read32(VM_ENTRY_CONTROLS)
		     & ~VM_ENTRY_IA32E_MODE);
}


static inline int cpu_has_secondary_exec_ctrls(void)
{
	return (vmcs_config.cpu_based_exec_ctrl &
		CPU_BASED_ACTIVATE_SECONDARY_CONTROLS);
}


static int vmx_get_mt_mask_shift(void)
{
	return VMX_EPT_MT_EPTE_SHIFT;
}


static u64 vmcs_read64(unsigned long field)
{
#ifdef CONFIG_X86_64
	return vmcs_readl(field);
#else
	return vmcs_readl(field) | ((u64)vmcs_readl(field+1) << 32);
#endif
}


static bool guest_state_valid(struct kvm_vcpu *vcpu)
{
	/* real mode guest state checks */
	if (!(vcpu->arch.cr0 & X86_CR0_PE)) {
		if (!rmode_segment_valid(vcpu, VCPU_SREG_CS))
			return false;
		if (!rmode_segment_valid(vcpu, VCPU_SREG_SS))
			return false;
		if (!rmode_segment_valid(vcpu, VCPU_SREG_DS))
			return false;
		if (!rmode_segment_valid(vcpu, VCPU_SREG_ES))
			return false;
		if (!rmode_segment_valid(vcpu, VCPU_SREG_FS))
			return false;
		if (!rmode_segment_valid(vcpu, VCPU_SREG_GS))
			return false;
	} else {
	/* protected mode guest state checks */
		if (!cs_ss_rpl_check(vcpu))
			return false;
		if (!code_segment_valid(vcpu))
			return false;
		if (!stack_segment_valid(vcpu))
			return false;
		if (!data_segment_valid(vcpu, VCPU_SREG_DS))
			return false;
		if (!data_segment_valid(vcpu, VCPU_SREG_ES))
			return false;
		if (!data_segment_valid(vcpu, VCPU_SREG_FS))
			return false;
		if (!data_segment_valid(vcpu, VCPU_SREG_GS))
			return false;
		if (!tr_valid(vcpu))
			return false;
		if (!ldtr_valid(vcpu))
			return false;
	}
	/* TODO:
	 * - Add checks on RIP
	 * - Add checks on RFLAGS
	 */

	return true;
}


static void vmx_free_vmcs(struct kvm_vcpu *vcpu)
{
	struct vcpu_vmx *vmx = to_vmx(vcpu);

	if (vmx->vmcs) {
		vcpu_clear(vmx);
		free_vmcs(vmx->vmcs);
		vmx->vmcs = NULL;
	}
}


static void save_msrs(struct kvm_msr_entry *e, int n)
{
	int i;

	for (i = 0; i < n; ++i)
		rdmsrl(e[i].index, e[i].data);
}


static void vmx_free_vcpu(struct kvm_vcpu *vcpu)
{
	struct vcpu_vmx *vmx = to_vmx(vcpu);

	spin_lock(&vmx_vpid_lock);
	if (vmx->vpid != 0)
		__clear_bit(vmx->vpid, vmx_vpid_bitmap);
	spin_unlock(&vmx_vpid_lock);
	vmx_free_vmcs(vcpu);
	kfree(vmx->host_msrs);
	kfree(vmx->guest_msrs);
	kvm_vcpu_uninit(vcpu);
	kmem_cache_free(kvm_vcpu_cache, vmx);
}


bool set_unix_posix_acl(connection_struct *conn, files_struct *fsp, const char *fname, uint16 num_acls, const char *pdata)
{
	SMB_ACL_T file_acl = NULL;

	if (!num_acls) {
		/* Remove the ACL from the file. */
		return remove_posix_acl(conn, fsp, fname);
	}

	if ((file_acl = create_posix_acl_from_wire(conn, num_acls, pdata)) == NULL) {
		return False;
	}

	if (fsp && fsp->fh->fd != -1) {
		/* The preferred way - use an open fd. */
		if (SMB_VFS_SYS_ACL_SET_FD(fsp, file_acl) == -1) {
			DEBUG(5,("set_unix_posix_acl: acl_set_file failed on %s (%s)\n",
				fname, strerror(errno) ));
		        SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
			return False;
		}
	} else {
		if (SMB_VFS_SYS_ACL_SET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS, file_acl) == -1) {
			DEBUG(5,("set_unix_posix_acl: acl_set_file failed on %s (%s)\n",
				fname, strerror(errno) ));
		        SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
			return False;
		}
	}

	DEBUG(10,("set_unix_posix_acl: set acl for file %s\n", fname ));
	SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
	return True;
}


static NTSTATUS posix_get_nt_acl_common(struct connection_struct *conn,
				      const char *name,
				      const SMB_STRUCT_STAT *sbuf,
				      struct pai_val *pal,
				      SMB_ACL_T posix_acl,
				      SMB_ACL_T def_acl,
				      uint32_t security_info,
				      SEC_DESC **ppdesc)
{
	DOM_SID owner_sid;
	DOM_SID group_sid;
	size_t sd_size = 0;
	SEC_ACL *psa = NULL;
	size_t num_acls = 0;
	size_t num_def_acls = 0;
	size_t num_aces = 0;
	canon_ace *file_ace = NULL;
	canon_ace *dir_ace = NULL;
	SEC_ACE *nt_ace_list = NULL;
	size_t num_profile_acls = 0;
	DOM_SID orig_owner_sid;
	SEC_DESC *psd = NULL;
	int i;

	/*
	 * Get the owner, group and world SIDs.
	 */

	create_file_sids(sbuf, &owner_sid, &group_sid);

	if (lp_profile_acls(SNUM(conn))) {
		/* For WXP SP1 the owner must be administrators. */
		sid_copy(&orig_owner_sid, &owner_sid);
		sid_copy(&owner_sid, &global_sid_Builtin_Administrators);
		sid_copy(&group_sid, &global_sid_Builtin_Users);
		num_profile_acls = 3;
	}

	if ((security_info & DACL_SECURITY_INFORMATION) && !(security_info & PROTECTED_DACL_SECURITY_INFORMATION)) {

		/*
		 * In the optimum case Creator Owner and Creator Group would be used for
		 * the ACL_USER_OBJ and ACL_GROUP_OBJ entries, respectively, but this
		 * would lead to usability problems under Windows: The Creator entries
		 * are only available in browse lists of directories and not for files;
		 * additionally the identity of the owning group couldn't be determined.
		 * We therefore use those identities only for Default ACLs.
		 */

		/* Create the canon_ace lists. */
		file_ace = canonicalise_acl(conn, name, posix_acl, sbuf,
					    &owner_sid, &group_sid, pal,
					    SMB_ACL_TYPE_ACCESS);

		/* We must have *some* ACLS. */

		if (count_canon_ace_list(file_ace) == 0) {
			DEBUG(0,("get_nt_acl : No ACLs on file (%s) !\n", name));
			goto done;
		}

		if (S_ISDIR(sbuf->st_mode) && def_acl) {
			dir_ace = canonicalise_acl(conn, name, def_acl,
						   sbuf,
						   &global_sid_Creator_Owner,
						   &global_sid_Creator_Group,
						   pal, SMB_ACL_TYPE_DEFAULT);
		}

		/*
		 * Create the NT ACE list from the canonical ace lists.
		 */

		{
			canon_ace *ace;
			enum security_ace_type nt_acl_type;

			if (nt4_compatible_acls() && dir_ace) {
				/*
				 * NT 4 chokes if an ACL contains an INHERIT_ONLY entry
				 * but no non-INHERIT_ONLY entry for one SID. So we only
				 * remove entries from the Access ACL if the
				 * corresponding Default ACL entries have also been
				 * removed. ACEs for CREATOR-OWNER and CREATOR-GROUP
				 * are exceptions. We can do nothing
				 * intelligent if the Default ACL contains entries that
				 * are not also contained in the Access ACL, so this
				 * case will still fail under NT 4.
				 */

				ace = canon_ace_entry_for(dir_ace, SMB_ACL_OTHER, NULL);
				if (ace && !ace->perms) {
					DLIST_REMOVE(dir_ace, ace);
					SAFE_FREE(ace);

					ace = canon_ace_entry_for(file_ace, SMB_ACL_OTHER, NULL);
					if (ace && !ace->perms) {
						DLIST_REMOVE(file_ace, ace);
						SAFE_FREE(ace);
					}
				}

				/*
				 * WinNT doesn't usually have Creator Group
				 * in browse lists, so we send this entry to
				 * WinNT even if it contains no relevant
				 * permissions. Once we can add
				 * Creator Group to browse lists we can
				 * re-enable this.
				 */

#if 0
				ace = canon_ace_entry_for(dir_ace, SMB_ACL_GROUP_OBJ, NULL);
				if (ace && !ace->perms) {
					DLIST_REMOVE(dir_ace, ace);
					SAFE_FREE(ace);
				}
#endif

				ace = canon_ace_entry_for(file_ace, SMB_ACL_GROUP_OBJ, NULL);
				if (ace && !ace->perms) {
					DLIST_REMOVE(file_ace, ace);
					SAFE_FREE(ace);
				}
			}

			num_acls = count_canon_ace_list(file_ace);
			num_def_acls = count_canon_ace_list(dir_ace);

			/* Allocate the ace list. */
			if ((nt_ace_list = SMB_MALLOC_ARRAY(SEC_ACE,num_acls + num_profile_acls + num_def_acls)) == NULL) {
				DEBUG(0,("get_nt_acl: Unable to malloc space for nt_ace_list.\n"));
				goto done;
			}

			memset(nt_ace_list, '\0', (num_acls + num_def_acls) * sizeof(SEC_ACE) );

			/*
			 * Create the NT ACE list from the canonical ace lists.
			 */

			for (ace = file_ace; ace != NULL; ace = ace->next) {
				SEC_ACCESS acc;

				acc = map_canon_ace_perms(SNUM(conn),
						&nt_acl_type,
						ace->perms,
						S_ISDIR(sbuf->st_mode));
				init_sec_ace(&nt_ace_list[num_aces++],
					&ace->trustee,
					nt_acl_type,
					acc,
					ace->inherited ?
						SEC_ACE_FLAG_INHERITED_ACE : 0);
			}

			/* The User must have access to a profile share - even
			 * if we can't map the SID. */
			if (lp_profile_acls(SNUM(conn))) {
 				add_or_replace_ace(nt_ace_list, &num_aces,
 						   &global_sid_Builtin_Users,
 						   SEC_ACE_TYPE_ACCESS_ALLOWED,
 						   FILE_GENERIC_ALL, 0);
			}

			for (ace = dir_ace; ace != NULL; ace = ace->next) {
				SEC_ACCESS acc;

				acc = map_canon_ace_perms(SNUM(conn),
						&nt_acl_type,
						ace->perms,
						S_ISDIR(sbuf->st_mode));
				init_sec_ace(&nt_ace_list[num_aces++],
					&ace->trustee,
					nt_acl_type,
					acc,
					SEC_ACE_FLAG_OBJECT_INHERIT|
					SEC_ACE_FLAG_CONTAINER_INHERIT|
					SEC_ACE_FLAG_INHERIT_ONLY|
					(ace->inherited ?
					   SEC_ACE_FLAG_INHERITED_ACE : 0));
			}

			/* The User must have access to a profile share - even
			 * if we can't map the SID. */
			if (lp_profile_acls(SNUM(conn))) {
 				add_or_replace_ace(nt_ace_list, &num_aces,
 						&global_sid_Builtin_Users,
 						SEC_ACE_TYPE_ACCESS_ALLOWED,
 						FILE_GENERIC_ALL,
 						SEC_ACE_FLAG_OBJECT_INHERIT |
 						SEC_ACE_FLAG_CONTAINER_INHERIT |
 						SEC_ACE_FLAG_INHERIT_ONLY);
			}

			/*
			 * Merge POSIX default ACLs and normal ACLs into one NT ACE.
			 * Win2K needs this to get the inheritance correct when replacing ACLs
			 * on a directory tree. Based on work by Jim @ IBM.
			 */

			num_aces = merge_default_aces(nt_ace_list, num_aces);

			if (lp_profile_acls(SNUM(conn))) {
				for (i = 0; i < num_aces; i++) {
					if (sid_equal(&nt_ace_list[i].trustee, &owner_sid)) {
						add_or_replace_ace(nt_ace_list, &num_aces,
	    							   &orig_owner_sid,
			    					   nt_ace_list[i].type,
					    			   nt_ace_list[i].access_mask,
								   nt_ace_list[i].flags);
						break;
					}
				}
			}
		}

		if (num_aces) {
			if((psa = make_sec_acl( talloc_tos(), NT4_ACL_REVISION, num_aces, nt_ace_list)) == NULL) {
				DEBUG(0,("get_nt_acl: Unable to malloc space for acl.\n"));
				goto done;
			}
		}
	} /* security_info & DACL_SECURITY_INFORMATION */

	psd = make_standard_sec_desc( talloc_tos(),
			(security_info & OWNER_SECURITY_INFORMATION) ? &owner_sid : NULL,
			(security_info & GROUP_SECURITY_INFORMATION) ? &group_sid : NULL,
			psa,
			&sd_size);

	if(!psd) {
		DEBUG(0,("get_nt_acl: Unable to malloc space for security descriptor.\n"));
		sd_size = 0;
		goto done;
	}

	/*
	 * Windows 2000: The DACL_PROTECTED flag in the security
	 * descriptor marks the ACL as non-inheriting, i.e., no
	 * ACEs from higher level directories propagate to this
	 * ACL. In the POSIX ACL model permissions are only
	 * inherited at file create time, so ACLs never contain
	 * any ACEs that are inherited dynamically. The DACL_PROTECTED
	 * flag doesn't seem to bother Windows NT.
	 * Always set this if map acl inherit is turned off.
	 */
	if (get_protected_flag(pal) || !lp_map_acl_inherit(SNUM(conn))) {
		psd->type |= SE_DESC_DACL_PROTECTED;
	}

	if (psd->dacl) {
		dacl_sort_into_canonical_order(psd->dacl->aces, (unsigned int)psd->dacl->num_aces);
	}

	*ppdesc = psd;

 done:

	if (posix_acl) {
		SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
	}
	if (def_acl) {
		SMB_VFS_SYS_ACL_FREE_ACL(conn, def_acl);
	}
	free_canon_ace_list(file_ace);
	free_canon_ace_list(dir_ace);
	free_inherited_info(pal);
	SAFE_FREE(nt_ace_list);

	return NT_STATUS_OK;
}


int fchmod_acl(files_struct *fsp, mode_t mode)
{
	connection_struct *conn = fsp->conn;
	SMB_ACL_T posix_acl = NULL;
	int ret = -1;

	if ((posix_acl = SMB_VFS_SYS_ACL_GET_FD(fsp)) == NULL)
		return -1;

	if ((ret = chmod_acl_internals(conn, posix_acl, mode)) == -1)
		goto done;

	ret = SMB_VFS_SYS_ACL_SET_FD(fsp, posix_acl);

  done:

	SMB_VFS_SYS_ACL_FREE_ACL(conn, posix_acl);
	return ret;
}


static bool remove_posix_acl(connection_struct *conn, files_struct *fsp, const char *fname)
{
	SMB_ACL_T file_acl = NULL;
	int entry_id = SMB_ACL_FIRST_ENTRY;
	SMB_ACL_ENTRY_T entry;
	bool ret = False;
	/* Create a new ACL with only 3 entries, u/g/w. */
	SMB_ACL_T new_file_acl = SMB_VFS_SYS_ACL_INIT(conn, 3);
	SMB_ACL_ENTRY_T user_ent = NULL;
	SMB_ACL_ENTRY_T group_ent = NULL;
	SMB_ACL_ENTRY_T other_ent = NULL;

	if (new_file_acl == NULL) {
		DEBUG(5,("remove_posix_acl: failed to init new ACL with 3 entries for file %s.\n", fname));
		return False;
	}

	/* Now create the u/g/w entries. */
	if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &new_file_acl, &user_ent) == -1) {
		DEBUG(5,("remove_posix_acl: Failed to create user entry for file %s. (%s)\n",
			fname, strerror(errno) ));
		goto done;
	}
	if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, user_ent, SMB_ACL_USER_OBJ) == -1) {
		DEBUG(5,("remove_posix_acl: Failed to set user entry for file %s. (%s)\n",
			fname, strerror(errno) ));
		goto done;
	}

	if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &new_file_acl, &group_ent) == -1) {
		DEBUG(5,("remove_posix_acl: Failed to create group entry for file %s. (%s)\n",
			fname, strerror(errno) ));
		goto done;
	}
	if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, group_ent, SMB_ACL_GROUP_OBJ) == -1) {
		DEBUG(5,("remove_posix_acl: Failed to set group entry for file %s. (%s)\n",
			fname, strerror(errno) ));
		goto done;
	}

	if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &new_file_acl, &other_ent) == -1) {
		DEBUG(5,("remove_posix_acl: Failed to create other entry for file %s. (%s)\n",
			fname, strerror(errno) ));
		goto done;
	}
	if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, other_ent, SMB_ACL_OTHER) == -1) {
		DEBUG(5,("remove_posix_acl: Failed to set other entry for file %s. (%s)\n",
			fname, strerror(errno) ));
		goto done;
	}

	/* Get the current file ACL. */
	if (fsp && fsp->fh->fd != -1) {
		file_acl = SMB_VFS_SYS_ACL_GET_FD(fsp);
	} else {
		file_acl = SMB_VFS_SYS_ACL_GET_FILE( conn, fname, SMB_ACL_TYPE_ACCESS);
	}

	if (file_acl == NULL) {
		/* This is only returned if an error occurred. Even for a file with
		   no acl a u/g/w acl should be returned. */
		DEBUG(5,("remove_posix_acl: failed to get ACL from file %s (%s).\n",
			fname, strerror(errno) ));
		goto done;
	}

	while ( SMB_VFS_SYS_ACL_GET_ENTRY(conn, file_acl, entry_id, &entry) == 1) {
		SMB_ACL_TAG_T tagtype;
		SMB_ACL_PERMSET_T permset;

		/* get_next... */
		if (entry_id == SMB_ACL_FIRST_ENTRY)
			entry_id = SMB_ACL_NEXT_ENTRY;

		if (SMB_VFS_SYS_ACL_GET_TAG_TYPE(conn, entry, &tagtype) == -1) {
			DEBUG(5,("remove_posix_acl: failed to get tagtype from ACL on file %s (%s).\n",
				fname, strerror(errno) ));
			goto done;
		}

		if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, entry, &permset) == -1) {
			DEBUG(5,("remove_posix_acl: failed to get permset from ACL on file %s (%s).\n",
				fname, strerror(errno) ));
			goto done;
		}

		if (tagtype == SMB_ACL_USER_OBJ) {
			if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, user_ent, permset) == -1) {
				DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n",
					fname, strerror(errno) ));
			}
		} else if (tagtype == SMB_ACL_GROUP_OBJ) {
			if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, group_ent, permset) == -1) {
				DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n",
					fname, strerror(errno) ));
			}
		} else if (tagtype == SMB_ACL_OTHER) {
			if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, other_ent, permset) == -1) {
				DEBUG(5,("remove_posix_acl: failed to set permset from ACL on file %s (%s).\n",
					fname, strerror(errno) ));
			}
		}
	}

	/* Set the new empty file ACL. */
	if (fsp && fsp->fh->fd != -1) {
		if (SMB_VFS_SYS_ACL_SET_FD(fsp, new_file_acl) == -1) {
			DEBUG(5,("remove_posix_acl: acl_set_file failed on %s (%s)\n",
				fname, strerror(errno) ));
			goto done;
		}
	} else {
		if (SMB_VFS_SYS_ACL_SET_FILE(conn, fname, SMB_ACL_TYPE_ACCESS, new_file_acl) == -1) {
			DEBUG(5,("remove_posix_acl: acl_set_file failed on %s (%s)\n",
				fname, strerror(errno) ));
			goto done;
		}
	}

	ret = True;

 done:

	if (file_acl) {
		SMB_VFS_SYS_ACL_FREE_ACL(conn, file_acl);
	}
	if (new_file_acl) {
		SMB_VFS_SYS_ACL_FREE_ACL(conn, new_file_acl);
	}
	return ret;
}


static unsigned int num_inherited_entries(canon_ace *ace_list)
{
	unsigned int num_entries = 0;

	for (; ace_list; ace_list = ace_list->next)
		if (ace_list->inherited)
			num_entries++;
	return num_entries;
}


static struct pai_val *load_inherited_info(const struct connection_struct *conn,
					   const char *fname)
{
	char *pai_buf;
	size_t pai_buf_size = 1024;
	struct pai_val *paiv = NULL;
	ssize_t ret;

	if (!lp_map_acl_inherit(SNUM(conn))) {
		return NULL;
	}

	if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL) {
		return NULL;
	}

	do {
		ret = SMB_VFS_GETXATTR(conn, fname,
				       SAMBA_POSIX_INHERITANCE_EA_NAME,
				       pai_buf, pai_buf_size);

		if (ret == -1) {
			if (errno != ERANGE) {
				break;
			}
			/* Buffer too small - enlarge it. */
			pai_buf_size *= 2;
			SAFE_FREE(pai_buf);
			if (pai_buf_size > 1024*1024) {
				return NULL; /* Limit malloc to 1mb. */
			}
			if ((pai_buf = (char *)SMB_MALLOC(pai_buf_size)) == NULL)
				return NULL;
		}
	} while (ret == -1);

	DEBUG(10,("load_inherited_info: ret = %lu for file %s\n", (unsigned long)ret, fname));

	if (ret == -1) {
		/* No attribute or not supported. */
#if defined(ENOATTR)
		if (errno != ENOATTR)
			DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
#else
		if (errno != ENOSYS)
			DEBUG(10,("load_inherited_info: Error %s\n", strerror(errno) ));
#endif
		SAFE_FREE(pai_buf);
		return NULL;
	}

	paiv = create_pai_val(pai_buf, ret);

	if (paiv && paiv->pai_protected) {
		DEBUG(10,("load_inherited_info: ACL is protected for file %s\n", fname));
	}

	SAFE_FREE(pai_buf);
	return paiv;
}


static char *create_pai_buf(canon_ace *file_ace_list, canon_ace *dir_ace_list, bool pai_protected, size_t *store_size)
{
	char *pai_buf = NULL;
	canon_ace *ace_list = NULL;
	char *entry_offset = NULL;
	unsigned int num_entries = 0;
	unsigned int num_def_entries = 0;

	for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next)
		if (ace_list->inherited)
			num_entries++;

	for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next)
		if (ace_list->inherited)
			num_def_entries++;

	DEBUG(10,("create_pai_buf: num_entries = %u, num_def_entries = %u\n", num_entries, num_def_entries ));

	*store_size = PAI_ENTRIES_BASE + ((num_entries + num_def_entries)*PAI_ENTRY_LENGTH);

	pai_buf = (char *)SMB_MALLOC(*store_size);
	if (!pai_buf) {
		return NULL;
	}

	/* Set up the header. */
	memset(pai_buf, '\0', PAI_ENTRIES_BASE);
	SCVAL(pai_buf,PAI_VERSION_OFFSET,PAI_VERSION);
	SCVAL(pai_buf,PAI_FLAG_OFFSET,(pai_protected ? PAI_ACL_FLAG_PROTECTED : 0));
	SSVAL(pai_buf,PAI_NUM_ENTRIES_OFFSET,num_entries);
	SSVAL(pai_buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET,num_def_entries);

	entry_offset = pai_buf + PAI_ENTRIES_BASE;

	for (ace_list = file_ace_list; ace_list; ace_list = ace_list->next) {
		if (ace_list->inherited) {
			uint8 type_val = (unsigned char)ace_list->owner_type;
			uint32 entry_val = get_entry_val(ace_list);

			SCVAL(entry_offset,0,type_val);
			SIVAL(entry_offset,1,entry_val);
			entry_offset += PAI_ENTRY_LENGTH;
		}
	}

	for (ace_list = dir_ace_list; ace_list; ace_list = ace_list->next) {
		if (ace_list->inherited) {
			uint8 type_val = (unsigned char)ace_list->owner_type;
			uint32 entry_val = get_entry_val(ace_list);

			SCVAL(entry_offset,0,type_val);
			SIVAL(entry_offset,1,entry_val);
			entry_offset += PAI_ENTRY_LENGTH;
		}
	}

	return pai_buf;
}


static bool check_pai_ok(char *pai_buf, size_t pai_buf_data_size)
{
	uint16 num_entries;
	uint16 num_def_entries;

	if (pai_buf_data_size < PAI_ENTRIES_BASE) {
		/* Corrupted - too small. */
		return False;
	}

	if (CVAL(pai_buf,PAI_VERSION_OFFSET) != PAI_VERSION)
		return False;

	num_entries = SVAL(pai_buf,PAI_NUM_ENTRIES_OFFSET);
	num_def_entries = SVAL(pai_buf,PAI_NUM_DEFAULT_ENTRIES_OFFSET);

	/* Check the entry lists match. */
	/* Each entry is 5 bytes (type plus 4 bytes of uid or gid). */

	if (((num_entries + num_def_entries)*PAI_ENTRY_LENGTH) + PAI_ENTRIES_BASE != pai_buf_data_size)
		return False;

	return True;
}


static SMB_ACL_T create_posix_acl_from_wire(connection_struct *conn, uint16 num_acls, const char *pdata)
{
	unsigned int i;
	SMB_ACL_T the_acl = SMB_VFS_SYS_ACL_INIT(conn, num_acls);

	if (the_acl == NULL) {
		return NULL;
	}

	for (i = 0; i < num_acls; i++) {
		SMB_ACL_ENTRY_T the_entry;
		SMB_ACL_PERMSET_T the_permset;
		SMB_ACL_TAG_T tag_type;

		if (SMB_VFS_SYS_ACL_CREATE_ENTRY(conn, &the_acl, &the_entry) == -1) {
			DEBUG(0,("create_posix_acl_from_wire: Failed to create entry %u. (%s)\n",
				i, strerror(errno) ));
			goto fail;
		}

		if (!unix_ex_wire_to_tagtype(CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)), &tag_type)) {
			DEBUG(0,("create_posix_acl_from_wire: invalid wire tagtype %u on entry %u.\n",
				CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)), i ));
			goto fail;
		}

		if (SMB_VFS_SYS_ACL_SET_TAG_TYPE(conn, the_entry, tag_type) == -1) {
			DEBUG(0,("create_posix_acl_from_wire: Failed to set tagtype on entry %u. (%s)\n",
				i, strerror(errno) ));
			goto fail;
		}

		/* Get the permset pointer from the new ACL entry. */
		if (SMB_VFS_SYS_ACL_GET_PERMSET(conn, the_entry, &the_permset) == -1) {
			DEBUG(0,("create_posix_acl_from_wire: Failed to get permset on entry %u. (%s)\n",
                                i, strerror(errno) ));
                        goto fail;
                }

		/* Map from wire to permissions. */
		if (!unix_ex_wire_to_permset(conn, CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+1), &the_permset)) {
			DEBUG(0,("create_posix_acl_from_wire: invalid permset %u on entry %u.\n",
				CVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE) + 1), i ));
			goto fail;
		}

		/* Now apply to the new ACL entry. */
		if (SMB_VFS_SYS_ACL_SET_PERMSET(conn, the_entry, the_permset) == -1) {
			DEBUG(0,("create_posix_acl_from_wire: Failed to add permset on entry %u. (%s)\n",
				i, strerror(errno) ));
			goto fail;
		}

		if (tag_type == SMB_ACL_USER) {
			uint32 uidval = IVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+2);
			uid_t uid = (uid_t)uidval;
			if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&uid) == -1) {
				DEBUG(0,("create_posix_acl_from_wire: Failed to set uid %u on entry %u. (%s)\n",
					(unsigned int)uid, i, strerror(errno) ));
				goto fail;
			}
		}

		if (tag_type == SMB_ACL_GROUP) {
			uint32 gidval = IVAL(pdata,(i*SMB_POSIX_ACL_ENTRY_SIZE)+2);
			gid_t gid = (uid_t)gidval;
			if (SMB_VFS_SYS_ACL_SET_QUALIFIER(conn, the_entry,(void *)&gid) == -1) {
				DEBUG(0,("create_posix_acl_from_wire: Failed to set gid %u on entry %u. (%s)\n",
					(unsigned int)gid, i, strerror(errno) ));
				goto fail;
			}
		}
	}

	return the_acl;

 fail:

	if (the_acl != NULL) {
		SMB_VFS_SYS_ACL_FREE_ACL(conn, the_acl);
	}
	return NULL;
}


static mode_t convert_permset_to_mode_t(connection_struct *conn, SMB_ACL_PERMSET_T permset)
{
	mode_t ret = 0;

	ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_READ) ? S_IRUSR : 0);
	ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_WRITE) ? S_IWUSR : 0);
	ret |= (SMB_VFS_SYS_ACL_GET_PERM(conn, permset, SMB_ACL_EXECUTE) ? S_IXUSR : 0);

	return ret;
}


static __inline__ ssize_t tun_get_user(struct tun_struct *tun,
				       const struct iovec *iv, size_t count,
				       int noblock)
{
	struct tun_pi pi = { 0, cpu_to_be16(ETH_P_IP) };
	struct sk_buff *skb;
	size_t len = count, align = 0;
	struct virtio_net_hdr gso = { 0 };
	int offset = 0;

	if (!(tun->flags & TUN_NO_PI)) {
		if ((len -= sizeof(pi)) > count)
			return -EINVAL;

		if (memcpy_fromiovecend((void *)&pi, iv, 0, sizeof(pi)))
			return -EFAULT;
		offset += sizeof(pi);
	}

	if (tun->flags & TUN_VNET_HDR) {
		if ((len -= sizeof(gso)) > count)
			return -EINVAL;

		if (memcpy_fromiovecend((void *)&gso, iv, offset, sizeof(gso)))
			return -EFAULT;

		if ((gso.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) &&
		    gso.csum_start + gso.csum_offset + 2 > gso.hdr_len)
			gso.hdr_len = gso.csum_start + gso.csum_offset + 2;

		if (gso.hdr_len > len)
			return -EINVAL;
		offset += sizeof(gso);
	}

	if ((tun->flags & TUN_TYPE_MASK) == TUN_TAP_DEV) {
		align = NET_IP_ALIGN;
		if (unlikely(len < ETH_HLEN ||
			     (gso.hdr_len && gso.hdr_len < ETH_HLEN)))
			return -EINVAL;
	}

	skb = tun_alloc_skb(tun, align, len, gso.hdr_len, noblock);
	if (IS_ERR(skb)) {
		if (PTR_ERR(skb) != -EAGAIN)
			tun->dev->stats.rx_dropped++;
		return PTR_ERR(skb);
	}

	if (skb_copy_datagram_from_iovec(skb, 0, iv, offset, len)) {
		tun->dev->stats.rx_dropped++;
		kfree_skb(skb);
		return -EFAULT;
	}

	if (gso.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
		if (!skb_partial_csum_set(skb, gso.csum_start,
					  gso.csum_offset)) {
			tun->dev->stats.rx_frame_errors++;
			kfree_skb(skb);
			return -EINVAL;
		}
	} else if (tun->flags & TUN_NOCHECKSUM)
		skb->ip_summed = CHECKSUM_UNNECESSARY;

	switch (tun->flags & TUN_TYPE_MASK) {
	case TUN_TUN_DEV:
		if (tun->flags & TUN_NO_PI) {
			switch (skb->data[0] & 0xf0) {
			case 0x40:
				pi.proto = htons(ETH_P_IP);
				break;
			case 0x60:
				pi.proto = htons(ETH_P_IPV6);
				break;
			default:
				tun->dev->stats.rx_dropped++;
				kfree_skb(skb);
				return -EINVAL;
			}
		}

		skb_reset_mac_header(skb);
		skb->protocol = pi.proto;
		skb->dev = tun->dev;
		break;
	case TUN_TAP_DEV:
		skb->protocol = eth_type_trans(skb, tun->dev);
		break;
	};

	if (gso.gso_type != VIRTIO_NET_HDR_GSO_NONE) {
		pr_debug("GSO!\n");
		switch (gso.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
		case VIRTIO_NET_HDR_GSO_TCPV4:
			skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
			break;
		case VIRTIO_NET_HDR_GSO_TCPV6:
			skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
			break;
		default:
			tun->dev->stats.rx_frame_errors++;
			kfree_skb(skb);
			return -EINVAL;
		}

		if (gso.gso_type & VIRTIO_NET_HDR_GSO_ECN)
			skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN;

		skb_shinfo(skb)->gso_size = gso.gso_size;
		if (skb_shinfo(skb)->gso_size == 0) {
			tun->dev->stats.rx_frame_errors++;
			kfree_skb(skb);
			return -EINVAL;
		}

		/* Header must be checked, and gso_segs computed. */
		skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
		skb_shinfo(skb)->gso_segs = 0;
	}

	netif_rx_ni(skb);

	tun->dev->stats.rx_packets++;
	tun->dev->stats.rx_bytes += len;

	return count;
}


static int tun_chr_fasync(int fd, struct file *file, int on)
{
	struct tun_struct *tun = tun_get(file);
	int ret;

	if (!tun)
		return -EBADFD;

	DBG(KERN_INFO "%s: tun_chr_fasync %d\n", tun->dev->name, on);

	lock_kernel();
	if ((ret = fasync_helper(fd, file, on, &tun->fasync)) < 0)
		goto out;

	if (on) {
		ret = __f_setown(file, task_pid(current), PIDTYPE_PID, 0);
		if (ret)
			goto out;
		tun->flags |= TUN_FASYNC;
	} else
		tun->flags &= ~TUN_FASYNC;
	ret = 0;
out:
	unlock_kernel();
	tun_put(tun);
	return ret;
}


static int load_state_from_tss32(struct kvm_vcpu *vcpu,
				  struct tss_segment_32 *tss)
{
	kvm_set_cr3(vcpu, tss->cr3);

	kvm_rip_write(vcpu, tss->eip);
	kvm_x86_ops->set_rflags(vcpu, tss->eflags | 2);

	kvm_register_write(vcpu, VCPU_REGS_RAX, tss->eax);
	kvm_register_write(vcpu, VCPU_REGS_RCX, tss->ecx);
	kvm_register_write(vcpu, VCPU_REGS_RDX, tss->edx);
	kvm_register_write(vcpu, VCPU_REGS_RBX, tss->ebx);
	kvm_register_write(vcpu, VCPU_REGS_RSP, tss->esp);
	kvm_register_write(vcpu, VCPU_REGS_RBP, tss->ebp);
	kvm_register_write(vcpu, VCPU_REGS_RSI, tss->esi);
	kvm_register_write(vcpu, VCPU_REGS_RDI, tss->edi);

	if (kvm_load_segment_descriptor(vcpu, tss->ldt_selector, 0, VCPU_SREG_LDTR))
		return 1;

	if (kvm_load_segment_descriptor(vcpu, tss->es, 1, VCPU_SREG_ES))
		return 1;

	if (kvm_load_segment_descriptor(vcpu, tss->cs, 9, VCPU_SREG_CS))
		return 1;

	if (kvm_load_segment_descriptor(vcpu, tss->ss, 1, VCPU_SREG_SS))
		return 1;

	if (kvm_load_segment_descriptor(vcpu, tss->ds, 1, VCPU_SREG_DS))
		return 1;

	if (kvm_load_segment_descriptor(vcpu, tss->fs, 1, VCPU_SREG_FS))
		return 1;

	if (kvm_load_segment_descriptor(vcpu, tss->gs, 1, VCPU_SREG_GS))
		return 1;
	return 0;
}


static int kvm_vm_ioctl_get_irqchip(struct kvm *kvm, struct kvm_irqchip *chip)
{
	int r;

	r = 0;
	switch (chip->chip_id) {
	case KVM_IRQCHIP_PIC_MASTER:
		memcpy(&chip->chip.pic,
			&pic_irqchip(kvm)->pics[0],
			sizeof(struct kvm_pic_state));
		break;
	case KVM_IRQCHIP_PIC_SLAVE:
		memcpy(&chip->chip.pic,
			&pic_irqchip(kvm)->pics[1],
			sizeof(struct kvm_pic_state));
		break;
	case KVM_IRQCHIP_IOAPIC:
		memcpy(&chip->chip.ioapic,
			ioapic_irqchip(kvm),
			sizeof(struct kvm_ioapic_state));
		break;
	default:
		r = -EINVAL;
		break;
	}
	return r;
}


void kvm_arch_hardware_disable(void *garbage)
{
	kvm_x86_ops->hardware_disable(garbage);
}


void kvm_queue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code)
{
	WARN_ON(vcpu->arch.exception.pending);
	vcpu->arch.exception.pending = true;
	vcpu->arch.exception.has_error_code = true;
	vcpu->arch.exception.nr = nr;
	vcpu->arch.exception.error_code = error_code;
}


int emulate_instruction(struct kvm_vcpu *vcpu,
			struct kvm_run *run,
			unsigned long cr2,
			u16 error_code,
			int emulation_type)
{
	int r;
	struct decode_cache *c;

	kvm_clear_exception_queue(vcpu);
	vcpu->arch.mmio_fault_cr2 = cr2;
	/*
	 * TODO: fix x86_emulate.c to use guest_read/write_register
	 * instead of direct ->regs accesses, can save hundred cycles
	 * on Intel for instructions that don't read/change RSP, for
	 * for example.
	 */
	cache_all_regs(vcpu);

	vcpu->mmio_is_write = 0;
	vcpu->arch.pio.string = 0;

	if (!(emulation_type & EMULTYPE_NO_DECODE)) {
		int cs_db, cs_l;
		kvm_x86_ops->get_cs_db_l_bits(vcpu, &cs_db, &cs_l);

		vcpu->arch.emulate_ctxt.vcpu = vcpu;
		vcpu->arch.emulate_ctxt.eflags = kvm_x86_ops->get_rflags(vcpu);
		vcpu->arch.emulate_ctxt.mode =
			(vcpu->arch.emulate_ctxt.eflags & X86_EFLAGS_VM)
			? X86EMUL_MODE_REAL : cs_l
			? X86EMUL_MODE_PROT64 :	cs_db
			? X86EMUL_MODE_PROT32 : X86EMUL_MODE_PROT16;

		r = x86_decode_insn(&vcpu->arch.emulate_ctxt, &emulate_ops);

		/* Reject the instructions other than VMCALL/VMMCALL when
		 * try to emulate invalid opcode */
		c = &vcpu->arch.emulate_ctxt.decode;
		if ((emulation_type & EMULTYPE_TRAP_UD) &&
		    (!(c->twobyte && c->b == 0x01 &&
		      (c->modrm_reg == 0 || c->modrm_reg == 3) &&
		       c->modrm_mod == 3 && c->modrm_rm == 1)))
			return EMULATE_FAIL;

		++vcpu->stat.insn_emulation;
		if (r)  {
			++vcpu->stat.insn_emulation_fail;
			if (kvm_mmu_unprotect_page_virt(vcpu, cr2))
				return EMULATE_DONE;
			return EMULATE_FAIL;
		}
	}

	if (emulation_type & EMULTYPE_SKIP) {
		kvm_rip_write(vcpu, vcpu->arch.emulate_ctxt.decode.eip);
		return EMULATE_DONE;
	}

	r = x86_emulate_insn(&vcpu->arch.emulate_ctxt, &emulate_ops);

	if (vcpu->arch.pio.string)
		return EMULATE_DO_MMIO;

	if ((r || vcpu->mmio_is_write) && run) {
		run->exit_reason = KVM_EXIT_MMIO;
		run->mmio.phys_addr = vcpu->mmio_phys_addr;
		memcpy(run->mmio.data, vcpu->mmio_data, 8);
		run->mmio.len = vcpu->mmio_size;
		run->mmio.is_write = vcpu->mmio_is_write;
	}

	if (r) {
		if (kvm_mmu_unprotect_page_virt(vcpu, cr2))
			return EMULATE_DONE;
		if (!vcpu->mmio_needed) {
			kvm_report_emulation_failure(vcpu, "mmio");
			return EMULATE_FAIL;
		}
		return EMULATE_DO_MMIO;
	}

	kvm_x86_ops->set_rflags(vcpu, vcpu->arch.emulate_ctxt.eflags);

	if (vcpu->mmio_is_write) {
		vcpu->mmio_needed = 0;
		return EMULATE_DO_MMIO;
	}

	return EMULATE_DONE;
}


unsigned long realmode_get_cr(struct kvm_vcpu *vcpu, int cr)
{
	unsigned long value;

	kvm_x86_ops->decache_cr4_guest_bits(vcpu);
	switch (cr) {
	case 0:
		value = vcpu->arch.cr0;
		break;
	case 2:
		value = vcpu->arch.cr2;
		break;
	case 3:
		value = vcpu->arch.cr3;
		break;
	case 4:
		value = vcpu->arch.cr4;
		break;
	case 8:
		value = kvm_get_cr8(vcpu);
		break;
	default:
		vcpu_printf(vcpu, "%s: unexpected cr %u\n", __func__, cr);
		return 0;
	}
	KVMTRACE_3D(CR_READ, vcpu, (u32)cr, (u32)value,
		    (u32)((u64)value >> 32), handler);

	return value;
}


static int kvm_task_switch_32(struct kvm_vcpu *vcpu, u16 tss_selector,
		       u16 old_tss_sel, u32 old_tss_base,
		       struct desc_struct *nseg_desc)
{
	struct tss_segment_32 tss_segment_32;
	int ret = 0;

	if (kvm_read_guest(vcpu->kvm, old_tss_base, &tss_segment_32,
			   sizeof tss_segment_32))
		goto out;

	save_state_to_tss32(vcpu, &tss_segment_32);

	if (kvm_write_guest(vcpu->kvm, old_tss_base, &tss_segment_32,
			    sizeof tss_segment_32))
		goto out;

	if (kvm_read_guest(vcpu->kvm, get_tss_base_addr(vcpu, nseg_desc),
			   &tss_segment_32, sizeof tss_segment_32))
		goto out;

	if (old_tss_sel != 0xffff) {
		tss_segment_32.prev_task_link = old_tss_sel;

		if (kvm_write_guest(vcpu->kvm,
				    get_tss_base_addr(vcpu, nseg_desc),
				    &tss_segment_32.prev_task_link,
				    sizeof tss_segment_32.prev_task_link))
			goto out;
	}

	if (load_state_from_tss32(vcpu, &tss_segment_32))
		goto out;

	ret = 1;
out:
	return ret;
}


static int dm_request_for_irq_injection(struct kvm_vcpu *vcpu,
					  struct kvm_run *kvm_run)
{
	return (!vcpu->arch.irq_summary &&
		kvm_run->request_interrupt_window &&
		vcpu->arch.interrupt_window_open &&
		(kvm_x86_ops->get_rflags(vcpu) & X86_EFLAGS_IF));
}


static void kvm_write_guest_time(struct kvm_vcpu *v)
{
	struct timespec ts;
	unsigned long flags;
	struct kvm_vcpu_arch *vcpu = &v->arch;
	void *shared_kaddr;
	unsigned long this_tsc_khz;

	if ((!vcpu->time_page))
		return;

	this_tsc_khz = get_cpu_var(cpu_tsc_khz);
	if (unlikely(vcpu->hv_clock_tsc_khz != this_tsc_khz)) {
		kvm_set_time_scale(this_tsc_khz, &vcpu->hv_clock);
		vcpu->hv_clock_tsc_khz = this_tsc_khz;
	}
	put_cpu_var(cpu_tsc_khz);

	/* Keep irq disabled to prevent changes to the clock */
	local_irq_save(flags);
	kvm_get_msr(v, MSR_IA32_TIME_STAMP_COUNTER,
			  &vcpu->hv_clock.tsc_timestamp);
	ktime_get_ts(&ts);
	local_irq_restore(flags);

	/* With all the info we got, fill in the values */

	vcpu->hv_clock.system_time = ts.tv_nsec +
				     (NSEC_PER_SEC * (u64)ts.tv_sec);
	/*
	 * The interface expects us to write an even number signaling that the
	 * update is finished. Since the guest won't see the intermediate
	 * state, we just increase by 2 at the end.
	 */
	vcpu->hv_clock.version += 2;

	shared_kaddr = kmap_atomic(vcpu->time_page, KM_USER0);

	memcpy(shared_kaddr + vcpu->time_offset, &vcpu->hv_clock,
	       sizeof(vcpu->hv_clock));

	kunmap_atomic(shared_kaddr, KM_USER0);

	mark_page_dirty(v->kvm, vcpu->time >> PAGE_SHIFT);
}


void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
{
	kvm_x86_ops->vcpu_put(vcpu);
	kvm_put_guest_fpu(vcpu);
}


int kvm_fix_hypercall(struct kvm_vcpu *vcpu)
{
	char instruction[3];
	int ret = 0;
	unsigned long rip = kvm_rip_read(vcpu);

	/*
	 * Blow out the MMU to ensure that no other VCPU has an active mapping
	 * to ensure that the updated hypercall appears atomically across all
	 * VCPUs.
	 */
	kvm_mmu_zap_all(vcpu->kvm);

	kvm_x86_ops->patch_hypercall(vcpu, instruction);
	if (emulator_write_emulated(rip, instruction, 3, vcpu)
	    != X86EMUL_CONTINUE)
		ret = -EFAULT;

	return ret;
}


int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data)
{
	switch (msr) {
	case MSR_EFER:
		set_efer(vcpu, data);
		break;
	case MSR_IA32_MC0_STATUS:
		pr_unimpl(vcpu, "%s: MSR_IA32_MC0_STATUS 0x%llx, nop\n",
		       __func__, data);
		break;
	case MSR_IA32_MCG_STATUS:
		pr_unimpl(vcpu, "%s: MSR_IA32_MCG_STATUS 0x%llx, nop\n",
			__func__, data);
		break;
	case MSR_IA32_MCG_CTL:
		pr_unimpl(vcpu, "%s: MSR_IA32_MCG_CTL 0x%llx, nop\n",
			__func__, data);
		break;
	case MSR_IA32_DEBUGCTLMSR:
		if (!data) {
			/* We support the non-activated case already */
			break;
		} else if (data & ~(DEBUGCTLMSR_LBR | DEBUGCTLMSR_BTF)) {
			/* Values other than LBR and BTF are vendor-specific,
			   thus reserved and should throw a #GP */
			return 1;
		}
		pr_unimpl(vcpu, "%s: MSR_IA32_DEBUGCTLMSR 0x%llx, nop\n",
			__func__, data);
		break;
	case MSR_IA32_UCODE_REV:
	case MSR_IA32_UCODE_WRITE:
	case MSR_VM_HSAVE_PA:
		break;
	case 0x200 ... 0x2ff:
		return set_msr_mtrr(vcpu, msr, data);
	case MSR_IA32_APICBASE:
		kvm_set_apic_base(vcpu, data);
		break;
	case MSR_IA32_MISC_ENABLE:
		vcpu->arch.ia32_misc_enable_msr = data;
		break;
	case MSR_KVM_WALL_CLOCK:
		vcpu->kvm->arch.wall_clock = data;
		kvm_write_wall_clock(vcpu->kvm, data);
		break;
	case MSR_KVM_SYSTEM_TIME: {
		if (vcpu->arch.time_page) {
			kvm_release_page_dirty(vcpu->arch.time_page);
			vcpu->arch.time_page = NULL;
		}

		vcpu->arch.time = data;

		/* we verify if the enable bit is set... */
		if (!(data & 1))
			break;

		/* ...but clean it before doing the actual write */
		vcpu->arch.time_offset = data & ~(PAGE_MASK | 1);

		vcpu->arch.time_page =
				gfn_to_page(vcpu->kvm, data >> PAGE_SHIFT);

		if (is_error_page(vcpu->arch.time_page)) {
			kvm_release_page_clean(vcpu->arch.time_page);
			vcpu->arch.time_page = NULL;
		}

		kvm_request_guest_time_update(vcpu);
		break;
	}
	default:
		pr_unimpl(vcpu, "unhandled wrmsr: 0x%x data %llx\n", msr, data);
		return 1;
	}
	return 0;
}


static int load_segment_descriptor_to_kvm_desct(struct kvm_vcpu *vcpu,
						u16 selector,
						struct kvm_segment *kvm_seg)
{
	struct desc_struct seg_desc;

	if (load_guest_segment_descriptor(vcpu, selector, &seg_desc))
		return 1;
	seg_desct_to_kvm_desct(&seg_desc, selector, kvm_seg);
	return 0;
}


void kvm_emulate_cpuid(struct kvm_vcpu *vcpu)
{
	u32 function, index;
	struct kvm_cpuid_entry2 *best;

	function = kvm_register_read(vcpu, VCPU_REGS_RAX);
	index = kvm_register_read(vcpu, VCPU_REGS_RCX);
	kvm_register_write(vcpu, VCPU_REGS_RAX, 0);
	kvm_register_write(vcpu, VCPU_REGS_RBX, 0);
	kvm_register_write(vcpu, VCPU_REGS_RCX, 0);
	kvm_register_write(vcpu, VCPU_REGS_RDX, 0);
	best = kvm_find_cpuid_entry(vcpu, function, index);
	if (best) {
		kvm_register_write(vcpu, VCPU_REGS_RAX, best->eax);
		kvm_register_write(vcpu, VCPU_REGS_RBX, best->ebx);
		kvm_register_write(vcpu, VCPU_REGS_RCX, best->ecx);
		kvm_register_write(vcpu, VCPU_REGS_RDX, best->edx);
	}
	kvm_x86_ops->skip_emulated_instruction(vcpu);
	KVMTRACE_5D(CPUID, vcpu, function,
		    (u32)kvm_register_read(vcpu, VCPU_REGS_RAX),
		    (u32)kvm_register_read(vcpu, VCPU_REGS_RBX),
		    (u32)kvm_register_read(vcpu, VCPU_REGS_RCX),
		    (u32)kvm_register_read(vcpu, VCPU_REGS_RDX), handler);
}


void kvm_queue_exception(struct kvm_vcpu *vcpu, unsigned nr)
{
	WARN_ON(vcpu->arch.exception.pending);
	vcpu->arch.exception.pending = true;
	vcpu->arch.exception.has_error_code = false;
	vcpu->arch.exception.nr = nr;
}


static int kvm_vm_ioctl_set_nr_mmu_pages(struct kvm *kvm,
					  u32 kvm_nr_mmu_pages)
{
	if (kvm_nr_mmu_pages < KVM_MIN_ALLOC_MMU_PAGES)
		return -EINVAL;

	down_write(&kvm->slots_lock);

	kvm_mmu_change_mmu_pages(kvm, kvm_nr_mmu_pages);
	kvm->arch.n_requested_mmu_pages = kvm_nr_mmu_pages;

	up_write(&kvm->slots_lock);
	return 0;
}


parse_tag_67_packet(struct ecryptfs_key_record *key_rec,
		    struct ecryptfs_message *msg)
{
	size_t i = 0;
	char *data;
	size_t data_len;
	size_t message_len;
	int rc;

	/*
	 *              ***** TAG 65 Packet Format *****
	 *    | Content Type                       | 1 byte       |
	 *    | Status Indicator                   | 1 byte       |
	 *    | Encrypted File Encryption Key Size | 1 or 2 bytes |
	 *    | Encrypted File Encryption Key      | arbitrary    |
	 */
	message_len = msg->data_len;
	data = msg->data;
	/* verify that everything through the encrypted FEK size is present */
	if (message_len < 4) {
		rc = -EIO;
		printk(KERN_ERR "%s: message_len is [%zd]; minimum acceptable "
		       "message length is [%d]\n", __func__, message_len, 4);
		goto out;
	}
	if (data[i++] != ECRYPTFS_TAG_67_PACKET_TYPE) {
		rc = -EIO;
		printk(KERN_ERR "%s: Type should be ECRYPTFS_TAG_67\n",
		       __func__);
		goto out;
	}
	if (data[i++]) {
		rc = -EIO;
		printk(KERN_ERR "%s: Status indicator has non zero "
		       "value [%d]\n", __func__, data[i-1]);

		goto out;
	}
	rc = ecryptfs_parse_packet_length(&data[i], &key_rec->enc_key_size,
					  &data_len);
	if (rc) {
		ecryptfs_printk(KERN_WARNING, "Error parsing packet length; "
				"rc = [%d]\n", rc);
		goto out;
	}
	i += data_len;
	if (message_len < (i + key_rec->enc_key_size)) {
		rc = -EIO;
		printk(KERN_ERR "%s: message_len [%zd]; max len is [%zd]\n",
		       __func__, message_len, (i + key_rec->enc_key_size));
		goto out;
	}
	if (key_rec->enc_key_size > ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES) {
		rc = -EIO;
		printk(KERN_ERR "%s: Encrypted key_size [%zd] larger than "
		       "the maximum key size [%d]\n", __func__,
		       key_rec->enc_key_size,
		       ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES);
		goto out;
	}
	memcpy(key_rec->enc_key, &data[i], key_rec->enc_key_size);
out:
	return rc;
}


write_tag_11_packet(char *dest, size_t *remaining_bytes, char *contents,
		    size_t contents_length, size_t *packet_length)
{
	size_t packet_size_length;
	size_t max_packet_size;
	int rc = 0;

	(*packet_length) = 0;
	/* This format is inspired by OpenPGP; see RFC 2440
	 * packet tag 11 */
	max_packet_size = (1                   /* Tag 11 identifier */
			   + 3                 /* Max Tag 11 packet size */
			   + 1                 /* Binary format specifier */
			   + 1                 /* Filename length */
			   + 8                 /* Filename ("_CONSOLE") */
			   + 4                 /* Modification date */
			   + contents_length); /* Literal data */
	if (max_packet_size > (*remaining_bytes)) {
		printk(KERN_ERR "Packet length larger than maximum allowable; "
		       "need up to [%td] bytes, but there are only [%td] "
		       "available\n", max_packet_size, (*remaining_bytes));
		rc = -EINVAL;
		goto out;
	}
	dest[(*packet_length)++] = ECRYPTFS_TAG_11_PACKET_TYPE;
	rc = ecryptfs_write_packet_length(&dest[(*packet_length)],
					  (max_packet_size - 4),
					  &packet_size_length);
	if (rc) {
		printk(KERN_ERR "Error generating tag 11 packet header; cannot "
		       "generate packet length. rc = [%d]\n", rc);
		goto out;
	}
	(*packet_length) += packet_size_length;
	dest[(*packet_length)++] = 0x62; /* binary data format specifier */
	dest[(*packet_length)++] = 8;
	memcpy(&dest[(*packet_length)], "_CONSOLE", 8);
	(*packet_length) += 8;
	memset(&dest[(*packet_length)], 0x00, 4);
	(*packet_length) += 4;
	memcpy(&dest[(*packet_length)], contents, contents_length);
	(*packet_length) += contents_length;
 out:
	if (rc)
		(*packet_length) = 0;
	else
		(*remaining_bytes) -= (*packet_length);
	return rc;
}


int ecryptfs_add_keysig(struct ecryptfs_crypt_stat *crypt_stat, char *sig)
{
	struct ecryptfs_key_sig *new_key_sig;
	int rc = 0;

	new_key_sig = kmem_cache_alloc(ecryptfs_key_sig_cache, GFP_KERNEL);
	if (!new_key_sig) {
		rc = -ENOMEM;
		printk(KERN_ERR
		       "Error allocating from ecryptfs_key_sig_cache\n");
		goto out;
	}
	memcpy(new_key_sig->keysig, sig, ECRYPTFS_SIG_SIZE_HEX);
	mutex_lock(&crypt_stat->keysig_list_mutex);
	list_add(&new_key_sig->crypt_stat_list, &crypt_stat->keysig_list);
	mutex_unlock(&crypt_stat->keysig_list_mutex);
out:
	return rc;
}


ecryptfs_add_global_auth_tok(struct ecryptfs_mount_crypt_stat *mount_crypt_stat,
			     char *sig, u32 global_auth_tok_flags)
{
	struct ecryptfs_global_auth_tok *new_auth_tok;
	int rc = 0;

	new_auth_tok = kmem_cache_zalloc(ecryptfs_global_auth_tok_cache,
					GFP_KERNEL);
	if (!new_auth_tok) {
		rc = -ENOMEM;
		printk(KERN_ERR "Error allocating from "
		       "ecryptfs_global_auth_tok_cache\n");
		goto out;
	}
	memcpy(new_auth_tok->sig, sig, ECRYPTFS_SIG_SIZE_HEX);
	new_auth_tok->flags = global_auth_tok_flags;
	new_auth_tok->sig[ECRYPTFS_SIG_SIZE_HEX] = '\0';
	mutex_lock(&mount_crypt_stat->global_auth_tok_list_mutex);
	list_add(&new_auth_tok->mount_crypt_stat_list,
		 &mount_crypt_stat->global_auth_tok_list);
	mount_crypt_stat->num_global_auth_toks++;
	mutex_unlock(&mount_crypt_stat->global_auth_tok_list_mutex);
out:
	return rc;
}


int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat,
			      unsigned char *src,
			      struct dentry *ecryptfs_dentry)
{
	size_t i = 0;
	size_t found_auth_tok;
	size_t next_packet_is_auth_tok_packet;
	struct list_head auth_tok_list;
	struct ecryptfs_auth_tok *matching_auth_tok;
	struct ecryptfs_auth_tok *candidate_auth_tok;
	char *candidate_auth_tok_sig;
	size_t packet_size;
	struct ecryptfs_auth_tok *new_auth_tok;
	unsigned char sig_tmp_space[ECRYPTFS_SIG_SIZE];
	struct ecryptfs_auth_tok_list_item *auth_tok_list_item;
	size_t tag_11_contents_size;
	size_t tag_11_packet_size;
	int rc = 0;

	INIT_LIST_HEAD(&auth_tok_list);
	/* Parse the header to find as many packets as we can; these will be
	 * added the our &auth_tok_list */
	next_packet_is_auth_tok_packet = 1;
	while (next_packet_is_auth_tok_packet) {
		size_t max_packet_size = ((PAGE_CACHE_SIZE - 8) - i);

		switch (src[i]) {
		case ECRYPTFS_TAG_3_PACKET_TYPE:
			rc = parse_tag_3_packet(crypt_stat,
						(unsigned char *)&src[i],
						&auth_tok_list, &new_auth_tok,
						&packet_size, max_packet_size);
			if (rc) {
				ecryptfs_printk(KERN_ERR, "Error parsing "
						"tag 3 packet\n");
				rc = -EIO;
				goto out_wipe_list;
			}
			i += packet_size;
			rc = parse_tag_11_packet((unsigned char *)&src[i],
						 sig_tmp_space,
						 ECRYPTFS_SIG_SIZE,
						 &tag_11_contents_size,
						 &tag_11_packet_size,
						 max_packet_size);
			if (rc) {
				ecryptfs_printk(KERN_ERR, "No valid "
						"(ecryptfs-specific) literal "
						"packet containing "
						"authentication token "
						"signature found after "
						"tag 3 packet\n");
				rc = -EIO;
				goto out_wipe_list;
			}
			i += tag_11_packet_size;
			if (ECRYPTFS_SIG_SIZE != tag_11_contents_size) {
				ecryptfs_printk(KERN_ERR, "Expected "
						"signature of size [%d]; "
						"read size [%d]\n",
						ECRYPTFS_SIG_SIZE,
						tag_11_contents_size);
				rc = -EIO;
				goto out_wipe_list;
			}
			ecryptfs_to_hex(new_auth_tok->token.password.signature,
					sig_tmp_space, tag_11_contents_size);
			new_auth_tok->token.password.signature[
				ECRYPTFS_PASSWORD_SIG_SIZE] = '\0';
			crypt_stat->flags |= ECRYPTFS_ENCRYPTED;
			break;
		case ECRYPTFS_TAG_1_PACKET_TYPE:
			rc = parse_tag_1_packet(crypt_stat,
						(unsigned char *)&src[i],
						&auth_tok_list, &new_auth_tok,
						&packet_size, max_packet_size);
			if (rc) {
				ecryptfs_printk(KERN_ERR, "Error parsing "
						"tag 1 packet\n");
				rc = -EIO;
				goto out_wipe_list;
			}
			i += packet_size;
			crypt_stat->flags |= ECRYPTFS_ENCRYPTED;
			break;
		case ECRYPTFS_TAG_11_PACKET_TYPE:
			ecryptfs_printk(KERN_WARNING, "Invalid packet set "
					"(Tag 11 not allowed by itself)\n");
			rc = -EIO;
			goto out_wipe_list;
			break;
		default:
			ecryptfs_printk(KERN_DEBUG, "No packet at offset "
					"[%d] of the file header; hex value of "
					"character is [0x%.2x]\n", i, src[i]);
			next_packet_is_auth_tok_packet = 0;
		}
	}
	if (list_empty(&auth_tok_list)) {
		printk(KERN_ERR "The lower file appears to be a non-encrypted "
		       "eCryptfs file; this is not supported in this version "
		       "of the eCryptfs kernel module\n");
		rc = -EINVAL;
		goto out;
	}
	/* auth_tok_list contains the set of authentication tokens
	 * parsed from the metadata. We need to find a matching
	 * authentication token that has the secret component(s)
	 * necessary to decrypt the EFEK in the auth_tok parsed from
	 * the metadata. There may be several potential matches, but
	 * just one will be sufficient to decrypt to get the FEK. */
find_next_matching_auth_tok:
	found_auth_tok = 0;
	list_for_each_entry(auth_tok_list_item, &auth_tok_list, list) {
		candidate_auth_tok = &auth_tok_list_item->auth_tok;
		if (unlikely(ecryptfs_verbosity > 0)) {
			ecryptfs_printk(KERN_DEBUG,
					"Considering cadidate auth tok:\n");
			ecryptfs_dump_auth_tok(candidate_auth_tok);
		}
		rc = ecryptfs_get_auth_tok_sig(&candidate_auth_tok_sig,
					       candidate_auth_tok);
		if (rc) {
			printk(KERN_ERR
			       "Unrecognized candidate auth tok type: [%d]\n",
			       candidate_auth_tok->token_type);
			rc = -EINVAL;
			goto out_wipe_list;
		}
		ecryptfs_find_auth_tok_for_sig(&matching_auth_tok,
					       crypt_stat->mount_crypt_stat,
					       candidate_auth_tok_sig);
		if (matching_auth_tok) {
			found_auth_tok = 1;
			goto found_matching_auth_tok;
		}
	}
	if (!found_auth_tok) {
		ecryptfs_printk(KERN_ERR, "Could not find a usable "
				"authentication token\n");
		rc = -EIO;
		goto out_wipe_list;
	}
found_matching_auth_tok:
	if (candidate_auth_tok->token_type == ECRYPTFS_PRIVATE_KEY) {
		memcpy(&(candidate_auth_tok->token.private_key),
		       &(matching_auth_tok->token.private_key),
		       sizeof(struct ecryptfs_private_key));
		rc = decrypt_pki_encrypted_session_key(candidate_auth_tok,
						       crypt_stat);
	} else if (candidate_auth_tok->token_type == ECRYPTFS_PASSWORD) {
		memcpy(&(candidate_auth_tok->token.password),
		       &(matching_auth_tok->token.password),
		       sizeof(struct ecryptfs_password));
		rc = decrypt_passphrase_encrypted_session_key(
			candidate_auth_tok, crypt_stat);
	}
	if (rc) {
		struct ecryptfs_auth_tok_list_item *auth_tok_list_item_tmp;

		ecryptfs_printk(KERN_WARNING, "Error decrypting the "
				"session key for authentication token with sig "
				"[%.*s]; rc = [%d]. Removing auth tok "
				"candidate from the list and searching for "
				"the next match.\n", candidate_auth_tok_sig,
				ECRYPTFS_SIG_SIZE_HEX, rc);
		list_for_each_entry_safe(auth_tok_list_item,
					 auth_tok_list_item_tmp,
					 &auth_tok_list, list) {
			if (candidate_auth_tok
			    == &auth_tok_list_item->auth_tok) {
				list_del(&auth_tok_list_item->list);
				kmem_cache_free(
					ecryptfs_auth_tok_list_item_cache,
					auth_tok_list_item);
				goto find_next_matching_auth_tok;
			}
		}
		BUG();
	}
	rc = ecryptfs_compute_root_iv(crypt_stat);
	if (rc) {
		ecryptfs_printk(KERN_ERR, "Error computing "
				"the root IV\n");
		goto out_wipe_list;
	}
	rc = ecryptfs_init_crypt_ctx(crypt_stat);
	if (rc) {
		ecryptfs_printk(KERN_ERR, "Error initializing crypto "
				"context for cipher [%s]; rc = [%d]\n",
				crypt_stat->cipher, rc);
	}
out_wipe_list:
	wipe_auth_tok_list(&auth_tok_list);
out:
	return rc;
}


int inflate_codes(tl, td, bl, bd)
struct huft *tl, *td;   /* literal/length and distance decoder tables */
int bl, bd;             /* number of bits decoded by tl[] and td[] */
/* inflate (decompress) the codes in a deflated (compressed) block.
   Return an error code or zero if it all goes ok. */
{
  register unsigned e;  /* table entry flag/number of extra bits */
  unsigned n, d;        /* length and index for copy */
  unsigned w;           /* current window position */
  struct huft *t;       /* pointer to table entry */
  unsigned ml, md;      /* masks for bl and bd bits */
  register ulg b;       /* bit buffer */
  register unsigned k;  /* number of bits in bit buffer */

  /* make local copies of globals */
  b = bb;                       /* initialize bit buffer */
  k = bk;
  w = wp;                       /* initialize window position */

  /* inflate the coded data */
  ml = mask_bits[bl];           /* precompute masks for speed */
  md = mask_bits[bd];
  for (;;)                      /* do until end of block */
  {
    NEEDBITS((unsigned)bl)
    if ((e = (t = tl + ((unsigned)b & ml))->e) > 16)
      do {
        if (e == 99)
          return 1;
        DUMPBITS(t->b)
        e -= 16;
        NEEDBITS(e)
      } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16);
    DUMPBITS(t->b)
    if (e == 16)                /* then it's a literal */
    {
      slide[w++] = (uch)t->v.n;
      Tracevv((stderr, "%c", slide[w-1]));
      if (w == WSIZE)
      {
        flush_output(w);
        w = 0;
      }
    }
    else                        /* it's an EOB or a length */
    {
      /* exit if end of block */
      if (e == 15)
        break;

      /* get length of block to copy */
      NEEDBITS(e)
      n = t->v.n + ((unsigned)b & mask_bits[e]);
      DUMPBITS(e);

      /* decode distance of block to copy */
      NEEDBITS((unsigned)bd)
      if ((e = (t = td + ((unsigned)b & md))->e) > 16)
        do {
          if (e == 99)
            return 1;
          DUMPBITS(t->b)
          e -= 16;
          NEEDBITS(e)
        } while ((e = (t = t->v.t + ((unsigned)b & mask_bits[e]))->e) > 16);
      DUMPBITS(t->b)
      NEEDBITS(e)
      d = w - t->v.n - ((unsigned)b & mask_bits[e]);
      DUMPBITS(e)
      Tracevv((stderr,"\\[%d,%d]", w-d, n));

      /* do the copy */
      do {
        n -= (e = (e = WSIZE - ((d &= WSIZE-1) > w ? d : w)) > n ? n : e);
#if !defined(NOMEMCPY) && !defined(DEBUG)
        if (w - d >= e)         /* (this test assumes unsigned comparison) */
        {
          memcpy(slide + w, slide + d, e);
          w += e;
          d += e;
        }
        else                      /* do it slow to avoid memcpy() overlap */
#endif /* !NOMEMCPY */
          do {
            slide[w++] = slide[d++];
	    Tracevv((stderr, "%c", slide[w-1]));
          } while (--e);
        if (w == WSIZE)
        {
          flush_output(w);
          w = 0;
        }
      } while (n);
    }
  }

  /* restore the globals from the locals */
  wp = w;                       /* restore global window pointer */
  bb = b;                       /* restore global bit buffer */
  bk = k;

  /* done */
  return 0;
}


int inflate_fixed()
/* decompress an inflated type 1 (fixed Huffman codes) block.  We should
   either replace this with a custom decoder, or at least precompute the
   Huffman tables. */
{
  int i;                /* temporary variable */
  struct huft *tl;      /* literal/length code table */
  struct huft *td;      /* distance code table */
  int bl;               /* lookup bits for tl */
  int bd;               /* lookup bits for td */
  unsigned l[288];      /* length list for huft_build */

  /* set up literal table */
  for (i = 0; i < 144; i++)
    l[i] = 8;
  for (; i < 256; i++)
    l[i] = 9;
  for (; i < 280; i++)
    l[i] = 7;
  for (; i < 288; i++)          /* make a complete, but wrong code set */
    l[i] = 8;
  bl = 7;
  if ((i = huft_build(l, 288, 257, cplens, cplext, &tl, &bl)) != 0)
    return i;

  /* set up distance table */
  for (i = 0; i < 30; i++)      /* make an incomplete code set */
    l[i] = 5;
  bd = 5;
  if ((i = huft_build(l, 30, 0, cpdist, cpdext, &td, &bd)) > 1)
  {
    huft_free(tl);
    return i;
  }

  /* decompress until an end-of-block code */
  if (inflate_codes(tl, td, bl, bd))
    return 1;

  /* free the decoding tables, return */
  huft_free(tl);
  huft_free(td);
  return 0;
}


void zap_other_threads(struct task_struct *p)
{
	struct task_struct *t;

	p->signal->group_stop_count = 0;

	for (t = next_thread(p); t != p; t = next_thread(t)) {
		/*
		 * Don't bother with already dead threads
		 */
		if (t->exit_state)
			continue;

		/* SIGKILL will be handled before any pending SIGSTOP */
		sigaddset(&t->pending.signal, SIGKILL);
		signal_wake_up(t, 1);
	}
}


static void ptrace_stop(int exit_code, int clear_code, siginfo_t *info)
{
	if (arch_ptrace_stop_needed(exit_code, info)) {
		/*
		 * The arch code has something special to do before a
		 * ptrace stop.  This is allowed to block, e.g. for faults
		 * on user stack pages.  We can't keep the siglock while
		 * calling arch_ptrace_stop, so we must release it now.
		 * To preserve proper semantics, we must do this before
		 * any signal bookkeeping like checking group_stop_count.
		 * Meanwhile, a SIGKILL could come in before we retake the
		 * siglock.  That must prevent us from sleeping in TASK_TRACED.
		 * So after regaining the lock, we must check for SIGKILL.
		 */
		spin_unlock_irq(&current->sighand->siglock);
		arch_ptrace_stop(exit_code, info);
		spin_lock_irq(&current->sighand->siglock);
		if (sigkill_pending(current))
			return;
	}

	/*
	 * If there is a group stop in progress,
	 * we must participate in the bookkeeping.
	 */
	if (current->signal->group_stop_count > 0)
		--current->signal->group_stop_count;

	current->last_siginfo = info;
	current->exit_code = exit_code;

	/* Let the debugger run.  */
	__set_current_state(TASK_TRACED);
	spin_unlock_irq(&current->sighand->siglock);
	read_lock(&tasklist_lock);
	if (may_ptrace_stop()) {
		do_notify_parent_cldstop(current, CLD_TRAPPED);
		/*
		 * Don't want to allow preemption here, because
		 * sys_ptrace() needs this task to be inactive.
		 *
		 * XXX: implement read_unlock_no_resched().
		 */
		preempt_disable();
		read_unlock(&tasklist_lock);
		preempt_enable_no_resched();
		schedule();
	} else {
		/*
		 * By the time we got the lock, our tracer went away.
		 * Don't drop the lock yet, another tracer may come.
		 */
		__set_current_state(TASK_RUNNING);
		if (clear_code)
			current->exit_code = 0;
		read_unlock(&tasklist_lock);
	}

	/*
	 * While in TASK_TRACED, we were considered "frozen enough".
	 * Now that we woke up, it's crucial if we're supposed to be
	 * frozen that we freeze now before running anything substantial.
	 */
	try_to_freeze();

	/*
	 * We are back.  Now reacquire the siglock before touching
	 * last_siginfo, so that we are sure to have synchronized with
	 * any signal-sending on another CPU that wants to examine it.
	 */
	spin_lock_irq(&current->sighand->siglock);
	current->last_siginfo = NULL;

	/*
	 * Queued signals ignored us while we were stopped for tracing.
	 * So check for any that we should take before resuming user mode.
	 * This sets TIF_SIGPENDING, but never clears it.
	 */
	recalc_sigpending_tsk(current);
}


static void print_fatal_signal(struct pt_regs *regs, int signr)
{
	printk("%s/%d: potentially unexpected fatal signal %d.\n",
		current->comm, task_pid_nr(current), signr);

#if defined(__i386__) && !defined(__arch_um__)
	printk("code at %08lx: ", regs->ip);
	{
		int i;
		for (i = 0; i < 16; i++) {
			unsigned char insn;

			__get_user(insn, (unsigned char *)(regs->ip + i));
			printk("%02x ", insn);
		}
	}
#endif
	printk("\n");
	preempt_disable();
	show_regs(regs);
	preempt_enable();
}


static struct sock *udp_get_first(struct seq_file *seq)
{
	struct sock *sk;
	struct udp_iter_state *state = seq->private;

	for (state->bucket = 0; state->bucket < UDP_HTABLE_SIZE; ++state->bucket) {
		struct hlist_node *node;
		sk_for_each(sk, node, &udp_hash[state->bucket]) {
			if (sk->sk_family == state->family)
				goto found;
		}
	}
	sk = NULL;
found:
	return sk;
}


static int compat_udp_setsockopt(struct sock *sk, int level, int optname,
				 char __user *optval, int optlen)
{
	if (level != SOL_UDP)
		return compat_ip_setsockopt(sk, level, optname, optval, optlen);
	return do_udp_setsockopt(sk, level, optname, optval, optlen);
}


static int compat_udpv6_setsockopt(struct sock *sk, int level, int optname,
				   char __user *optval, int optlen)
{
	if (level != SOL_UDP)
		return compat_ipv6_setsockopt(sk, level, optname,
					      optval, optlen);
	return do_udpv6_setsockopt(sk, level, optname, optval, optlen);
}


static int udp_sendpage(struct sock *sk, struct page *page, int offset,
			size_t size, int flags)
{
	struct udp_sock *up = udp_sk(sk);
	int ret;

	if (!up->pending) {
		struct msghdr msg = {	.msg_flags = flags|MSG_MORE };

		/* Call udp_sendmsg to specify destination address which
		 * sendpage interface can't pass.
		 * This will succeed only when the socket is connected.
		 */
		ret = udp_sendmsg(NULL, sk, &msg, 0);
		if (ret < 0)
			return ret;
	}

	lock_sock(sk);

	if (unlikely(!up->pending)) {
		release_sock(sk);

		LIMIT_NETDEBUG(KERN_DEBUG "udp cork app bug 3\n");
		return -EINVAL;
	}

	ret = ip_append_page(sk, page, offset, size, flags);
	if (ret == -EOPNOTSUPP) {
		release_sock(sk);
		return sock_no_sendpage(sk->sk_socket, page, offset,
					size, flags);
	}
	if (ret < 0) {
		udp_flush_pending_frames(sk);
		goto out;
	}

	up->len += size;
	if (!(up->corkflag || (flags&MSG_MORE)))
		ret = udp_push_pending_frames(sk, up);
	if (!ret)
		ret = size;
out:
	release_sock(sk);
	return ret;
}


static void udpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
	       int type, int code, int offset, __u32 info)
{
	struct ipv6_pinfo *np;
	struct ipv6hdr *hdr = (struct ipv6hdr*)skb->data;
	struct net_device *dev = skb->dev;
	struct in6_addr *saddr = &hdr->saddr;
	struct in6_addr *daddr = &hdr->daddr;
	struct udphdr *uh = (struct udphdr*)(skb->data+offset);
	struct sock *sk;
	int err;

	sk = udp_v6_lookup(daddr, uh->dest, saddr, uh->source, dev->ifindex);

	if (sk == NULL)
		return;

	np = inet6_sk(sk);

	if (!icmpv6_err_convert(type, code, &err) && !np->recverr)
		goto out;

	if (sk->sk_state != TCP_ESTABLISHED && !np->recverr)
		goto out;

	if (np->recverr)
		ipv6_icmp_error(sk, skb, err, uh->dest, ntohl(info), (u8 *)(uh+1));

	sk->sk_err = err;
	sk->sk_error_report(sk);
out:
	sock_put(sk);
}


static int irda_connect(struct socket *sock, struct sockaddr *uaddr,
			int addr_len, int flags)
{
	struct sock *sk = sock->sk;
	struct sockaddr_irda *addr = (struct sockaddr_irda *) uaddr;
	struct irda_sock *self = irda_sk(sk);
	int err;

	IRDA_DEBUG(2, "%s(%p)\n", __func__, self);

	/* Don't allow connect for Ultra sockets */
	if ((sk->sk_type == SOCK_DGRAM) && (sk->sk_protocol == IRDAPROTO_ULTRA))
		return -ESOCKTNOSUPPORT;

	if (sk->sk_state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
		sock->state = SS_CONNECTED;
		return 0;   /* Connect completed during a ERESTARTSYS event */
	}

	if (sk->sk_state == TCP_CLOSE && sock->state == SS_CONNECTING) {
		sock->state = SS_UNCONNECTED;
		return -ECONNREFUSED;
	}

	if (sk->sk_state == TCP_ESTABLISHED)
		return -EISCONN;      /* No reconnect on a seqpacket socket */

	sk->sk_state   = TCP_CLOSE;
	sock->state = SS_UNCONNECTED;

	if (addr_len != sizeof(struct sockaddr_irda))
		return -EINVAL;

	/* Check if user supplied any destination device address */
	if ((!addr->sir_addr) || (addr->sir_addr == DEV_ADDR_ANY)) {
		/* Try to find one suitable */
		err = irda_discover_daddr_and_lsap_sel(self, addr->sir_name);
		if (err) {
			IRDA_DEBUG(0, "%s(), auto-connect failed!\n", __func__);
			return err;
		}
	} else {
		/* Use the one provided by the user */
		self->daddr = addr->sir_addr;
		IRDA_DEBUG(1, "%s(), daddr = %08x\n", __func__, self->daddr);

		/* If we don't have a valid service name, we assume the
		 * user want to connect on a specific LSAP. Prevent
		 * the use of invalid LSAPs (IrLMP 1.1 p10). Jean II */
		if((addr->sir_name[0] != '\0') ||
		   (addr->sir_lsap_sel >= 0x70)) {
			/* Query remote LM-IAS using service name */
			err = irda_find_lsap_sel(self, addr->sir_name);
			if (err) {
				IRDA_DEBUG(0, "%s(), connect failed!\n", __func__);
				return err;
			}
		} else {
			/* Directly connect to the remote LSAP
			 * specified by the sir_lsap field.
			 * Please use with caution, in IrDA LSAPs are
			 * dynamic and there is no "well-known" LSAP. */
			self->dtsap_sel = addr->sir_lsap_sel;
		}
	}

	/* Check if we have opened a local TSAP */
	if (!self->tsap)
		irda_open_tsap(self, LSAP_ANY, addr->sir_name);

	/* Move to connecting socket, start sending Connect Requests */
	sock->state = SS_CONNECTING;
	sk->sk_state   = TCP_SYN_SENT;

	/* Connect to remote device */
	err = irttp_connect_request(self->tsap, self->dtsap_sel,
				    self->saddr, self->daddr, NULL,
				    self->max_sdu_size_rx, NULL);
	if (err) {
		IRDA_DEBUG(0, "%s(), connect failed!\n", __func__);
		return err;
	}

	/* Now the loop */
	if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
		return -EINPROGRESS;

	if (wait_event_interruptible(*(sk->sk_sleep),
				     (sk->sk_state != TCP_SYN_SENT)))
		return -ERESTARTSYS;

	if (sk->sk_state != TCP_ESTABLISHED) {
		sock->state = SS_UNCONNECTED;
		err = sock_error(sk);
		return err? err : -ECONNRESET;
	}

	sock->state = SS_CONNECTED;

	/* At this point, IrLMP has assigned our source address */
	self->saddr = irttp_get_saddr(self->tsap);

	return 0;
}


static void irda_flow_indication(void *instance, void *sap, LOCAL_FLOW flow)
{
	struct irda_sock *self;
	struct sock *sk;

	IRDA_DEBUG(2, "%s()\n", __func__);

	self = instance;
	sk = instance;
	BUG_ON(sk == NULL);

	switch (flow) {
	case FLOW_STOP:
		IRDA_DEBUG(1, "%s(), IrTTP wants us to slow down\n",
			   __func__);
		self->tx_flow = flow;
		break;
	case FLOW_START:
		self->tx_flow = flow;
		IRDA_DEBUG(1, "%s(), IrTTP wants us to start again\n",
			   __func__);
		wake_up_interruptible(sk->sk_sleep);
		break;
	default:
		IRDA_DEBUG(0, "%s(), Unknown flow command!\n", __func__);
		/* Unknown flow command, better stop */
		self->tx_flow = flow;
		break;
	}
}


static int irda_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
	struct sock *sk = sock->sk;

	IRDA_DEBUG(4, "%s(), cmd=%#x\n", __func__, cmd);

	switch (cmd) {
	case TIOCOUTQ: {
		long amount;

		amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk);
		if (amount < 0)
			amount = 0;
		if (put_user(amount, (unsigned int __user *)arg))
			return -EFAULT;
		return 0;
	}

	case TIOCINQ: {
		struct sk_buff *skb;
		long amount = 0L;
		/* These two are safe on a single CPU system as only user tasks fiddle here */
		if ((skb = skb_peek(&sk->sk_receive_queue)) != NULL)
			amount = skb->len;
		if (put_user(amount, (unsigned int __user *)arg))
			return -EFAULT;
		return 0;
	}

	case SIOCGSTAMP:
		if (sk != NULL)
			return sock_get_timestamp(sk, (struct timeval __user *)arg);
		return -EINVAL;

	case SIOCGIFADDR:
	case SIOCSIFADDR:
	case SIOCGIFDSTADDR:
	case SIOCSIFDSTADDR:
	case SIOCGIFBRDADDR:
	case SIOCSIFBRDADDR:
	case SIOCGIFNETMASK:
	case SIOCSIFNETMASK:
	case SIOCGIFMETRIC:
	case SIOCSIFMETRIC:
		return -EINVAL;
	default:
		IRDA_DEBUG(1, "%s(), doing device ioctl!\n", __func__);
		return -ENOIOCTLCMD;
	}

	/*NOTREACHED*/
	return 0;
}


static int econet_getname(struct socket *sock, struct sockaddr *uaddr,
			  int *uaddr_len, int peer)
{
	struct sock *sk;
	struct econet_sock *eo;
	struct sockaddr_ec *sec = (struct sockaddr_ec *)uaddr;

	if (peer)
		return -EOPNOTSUPP;

	memset(sec, 0, sizeof(*sec));
	mutex_lock(&econet_mutex);

	sk = sock->sk;
	eo = ec_sk(sk);

	sec->sec_family	  = AF_ECONET;
	sec->port	  = eo->port;
	sec->addr.station = eo->station;
	sec->addr.net	  = eo->net;

	mutex_unlock(&econet_mutex);

	*uaddr_len = sizeof(*sec);
	return 0;
}


static int econet_create(struct net *net, struct socket *sock, int protocol)
{
	struct sock *sk;
	struct econet_sock *eo;
	int err;

	if (net != &init_net)
		return -EAFNOSUPPORT;

	/* Econet only provides datagram services. */
	if (sock->type != SOCK_DGRAM)
		return -ESOCKTNOSUPPORT;

	sock->state = SS_UNCONNECTED;

	err = -ENOBUFS;
	sk = sk_alloc(net, PF_ECONET, GFP_KERNEL, &econet_proto);
	if (sk == NULL)
		goto out;

	sk->sk_reuse = 1;
	sock->ops = &econet_ops;
	sock_init_data(sock, sk);

	eo = ec_sk(sk);
	sock_reset_flag(sk, SOCK_ZAPPED);
	sk->sk_family = PF_ECONET;
	eo->num = protocol;

	econet_insert_socket(&econet_sklist, sk);
	return(0);
out:
	return err;
}