struct file *dentry_open(const struct path *path, int flags,
			 const struct cred *cred)
{
	int error;
	struct file *f;

	validate_creds(cred);

	/* We must always pass in a valid mount pointer. */
	BUG_ON(!path->mnt);

	f = get_empty_filp();
	if (!IS_ERR(f)) {
		f->f_flags = flags;
		f->f_path = *path;
		error = do_dentry_open(f, NULL, cred);
		if (!error) {
			/* from now on we need fput() to dispose of f */
			error = open_check_o_direct(f);
			if (error) {
				fput(f);
				f = ERR_PTR(error);
			}
		} else {
			put_filp(f);
			f = ERR_PTR(error);
		}
	}
	return f;
}


Safer ABI for O_TMPFILE

[suggested by Rasmus Villemoes] make O_DIRECTORY | O_RDWR part of O_TMPFILE;
that will fail on old kernels in a lot more cases than what I came up with.
And make sure O_CREAT doesn't get there...

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

void dentry_unhash(struct dentry *dentry)
{
	shrink_dcache_parent(dentry);
	spin_lock(&dentry->d_lock);
	if (dentry->d_count == 1)
		__d_drop(dentry);
	spin_unlock(&dentry->d_lock);
}

Use-after-free vulnerability in the path_openat function in fs/namei.c in the Linux kernel 3.x and 4.x before 4.0.4 allows local users to cause a denial of service or possibly have unspecified other impact via O_TMPFILE filesystem operations that leverage a duplicate cleanup operation.


int finish_no_open(struct file *file, struct dentry *dentry)
{
	file->f_path.dentry = dentry;
	return 1;
}


int nonseekable_open(struct inode *inode, struct file *filp)
{
	filp->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
	return 0;
}


SYSCALL_DEFINE1(close, unsigned int, fd)
{
	int retval = __close_fd(current->files, fd);

	/* can't restart close syscall because file table entry was cleared */
	if (unlikely(retval == -ERESTARTSYS ||
		     retval == -ERESTARTNOINTR ||
		     retval == -ERESTARTNOHAND ||
		     retval == -ERESTART_RESTARTBLOCK))
		retval = -EINTR;

	return retval;
}


SYSCALL_DEFINE2(ftruncate, unsigned int, fd, unsigned long, length)
{
	return do_sys_ftruncate(fd, length, 1);
}


static long do_sys_truncate(const char __user *pathname, loff_t length)
{
	unsigned int lookup_flags = LOOKUP_FOLLOW;
	struct path path;
	int error;

	if (length < 0)	/* sorry, but loff_t says... */
		return -EINVAL;

retry:
	error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
	if (!error) {
		error = vfs_truncate(&path, length);
		path_put(&path);
	}
	if (retry_estale(error, lookup_flags)) {
		lookup_flags |= LOOKUP_REVAL;
		goto retry;
	}
	return error;
}


static int unlazy_walk(struct nameidata *nd, struct dentry *dentry)
{
	struct fs_struct *fs = current->fs;
	struct dentry *parent = nd->path.dentry;
	int want_root = 0;

	BUG_ON(!(nd->flags & LOOKUP_RCU));
	if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) {
		want_root = 1;
		spin_lock(&fs->lock);
		if (nd->root.mnt != fs->root.mnt ||
				nd->root.dentry != fs->root.dentry)
			goto err_root;
	}
	spin_lock(&parent->d_lock);
	if (!dentry) {
		if (!__d_rcu_to_refcount(parent, nd->seq))
			goto err_parent;
		BUG_ON(nd->inode != parent->d_inode);
	} else {
		if (dentry->d_parent != parent)
			goto err_parent;
		spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
		if (!__d_rcu_to_refcount(dentry, nd->seq))
			goto err_child;
		/*
		 * If the sequence check on the child dentry passed, then
		 * the child has not been removed from its parent. This
		 * means the parent dentry must be valid and able to take
		 * a reference at this point.
		 */
		BUG_ON(!IS_ROOT(dentry) && dentry->d_parent != parent);
		BUG_ON(!parent->d_count);
		parent->d_count++;
		spin_unlock(&dentry->d_lock);
	}
	spin_unlock(&parent->d_lock);
	if (want_root) {
		path_get(&nd->root);
		spin_unlock(&fs->lock);
	}
	mntget(nd->path.mnt);

	unlock_rcu_walk();
	nd->flags &= ~LOOKUP_RCU;
	return 0;

err_child:
	spin_unlock(&dentry->d_lock);
err_parent:
	spin_unlock(&parent->d_lock);
err_root:
	if (want_root)
		spin_unlock(&fs->lock);
	return -ECHILD;
}


static inline void unlock_rcu_walk(void)
{
	rcu_read_unlock();
	br_read_unlock(&vfsmount_lock);
}


SYSCALL_DEFINE2(fchmod, unsigned int, fd, umode_t, mode)
{
	struct file * file;
	int err = -EBADF;

	file = fget(fd);
	if (file) {
		audit_inode(NULL, file->f_path.dentry, 0);
		err = chmod_common(&file->f_path, mode);
		fput(file);
	}
	return err;
}


int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
{
	struct inode *inode = file_inode(file);
	long ret;

	if (offset < 0 || len <= 0)
		return -EINVAL;

	/* Return error if mode is not supported */
	if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
		return -EOPNOTSUPP;

	/* Punch hole must have keep size set */
	if ((mode & FALLOC_FL_PUNCH_HOLE) &&
	    !(mode & FALLOC_FL_KEEP_SIZE))
		return -EOPNOTSUPP;

	if (!(file->f_mode & FMODE_WRITE))
		return -EBADF;

	/* It's not possible punch hole on append only file */
	if (mode & FALLOC_FL_PUNCH_HOLE && IS_APPEND(inode))
		return -EPERM;

	if (IS_IMMUTABLE(inode))
		return -EPERM;

	/*
	 * Revalidate the write permissions, in case security policy has
	 * changed since the files were opened.
	 */
	ret = security_file_permission(file, MAY_WRITE);
	if (ret)
		return ret;

	if (S_ISFIFO(inode->i_mode))
		return -ESPIPE;

	/*
	 * Let individual file system decide if it supports preallocation
	 * for directories or not.
	 */
	if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
		return -ENODEV;

	/* Check for wrap through zero too */
	if (((offset + len) > inode->i_sb->s_maxbytes) || ((offset + len) < 0))
		return -EFBIG;

	if (!file->f_op->fallocate)
		return -EOPNOTSUPP;

	sb_start_write(inode->i_sb);
	ret = file->f_op->fallocate(file, mode, offset, len);
	sb_end_write(inode->i_sb);
	return ret;
}


SYSCALL_DEFINE2(ftruncate64, unsigned int, fd, loff_t, length)
{
	return do_sys_ftruncate(fd, length, 0);
}


struct file *file_open_name(struct filename *name, int flags, umode_t mode)
{
	struct open_flags op;
	int err = build_open_flags(flags, mode, &op);
	return err ? ERR_PTR(err) : do_filp_open(AT_FDCWD, name, &op);
}


static int do_last(struct nameidata *nd, struct path *path,
		   struct file *file, const struct open_flags *op,
		   int *opened, struct filename *name)
{
	struct dentry *dir = nd->path.dentry;
	int open_flag = op->open_flag;
	bool will_truncate = (open_flag & O_TRUNC) != 0;
	bool got_write = false;
	int acc_mode = op->acc_mode;
	struct inode *inode;
	bool symlink_ok = false;
	struct path save_parent = { .dentry = NULL, .mnt = NULL };
	bool retried = false;
	int error;

	nd->flags &= ~LOOKUP_PARENT;
	nd->flags |= op->intent;

	if (nd->last_type != LAST_NORM) {
		error = handle_dots(nd, nd->last_type);
		if (error)
			return error;
		goto finish_open;
	}

	if (!(open_flag & O_CREAT)) {
		if (nd->last.name[nd->last.len])
			nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
		if (open_flag & O_PATH && !(nd->flags & LOOKUP_FOLLOW))
			symlink_ok = true;
		/* we _can_ be in RCU mode here */
		error = lookup_fast(nd, path, &inode);
		if (likely(!error))
			goto finish_lookup;

		if (error < 0)
			goto out;

		BUG_ON(nd->inode != dir->d_inode);
	} else {
		/* create side of things */
		/*
		 * This will *only* deal with leaving RCU mode - LOOKUP_JUMPED
		 * has been cleared when we got to the last component we are
		 * about to look up
		 */
		error = complete_walk(nd);
		if (error)
			return error;

		audit_inode(name, dir, LOOKUP_PARENT);
		error = -EISDIR;
		/* trailing slashes? */
		if (nd->last.name[nd->last.len])
			goto out;
	}

retry_lookup:
	if (op->open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) {
		error = mnt_want_write(nd->path.mnt);
		if (!error)
			got_write = true;
		/*
		 * do _not_ fail yet - we might not need that or fail with
		 * a different error; let lookup_open() decide; we'll be
		 * dropping this one anyway.
		 */
	}
	mutex_lock(&dir->d_inode->i_mutex);
	error = lookup_open(nd, path, file, op, got_write, opened);
	mutex_unlock(&dir->d_inode->i_mutex);

	if (error <= 0) {
		if (error)
			goto out;

		if ((*opened & FILE_CREATED) ||
		    !S_ISREG(file_inode(file)->i_mode))
			will_truncate = false;

		audit_inode(name, file->f_path.dentry, 0);
		goto opened;
	}

	if (*opened & FILE_CREATED) {
		/* Don't check for write permission, don't truncate */
		open_flag &= ~O_TRUNC;
		will_truncate = false;
		acc_mode = MAY_OPEN;
		path_to_nameidata(path, nd);
		goto finish_open_created;
	}

	/*
	 * create/update audit record if it already exists.
	 */
	if (path->dentry->d_inode)
		audit_inode(name, path->dentry, 0);

	/*
	 * If atomic_open() acquired write access it is dropped now due to
	 * possible mount and symlink following (this might be optimized away if
	 * necessary...)
	 */
	if (got_write) {
		mnt_drop_write(nd->path.mnt);
		got_write = false;
	}

	error = -EEXIST;
	if ((open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT))
		goto exit_dput;

	error = follow_managed(path, nd->flags);
	if (error < 0)
		goto exit_dput;

	if (error)
		nd->flags |= LOOKUP_JUMPED;

	BUG_ON(nd->flags & LOOKUP_RCU);
	inode = path->dentry->d_inode;
finish_lookup:
	/* we _can_ be in RCU mode here */
	error = -ENOENT;
	if (!inode) {
		path_to_nameidata(path, nd);
		goto out;
	}

	if (should_follow_link(inode, !symlink_ok)) {
		if (nd->flags & LOOKUP_RCU) {
			if (unlikely(unlazy_walk(nd, path->dentry))) {
				error = -ECHILD;
				goto out;
			}
		}
		BUG_ON(inode != path->dentry->d_inode);
		return 1;
	}

	if ((nd->flags & LOOKUP_RCU) || nd->path.mnt != path->mnt) {
		path_to_nameidata(path, nd);
	} else {
		save_parent.dentry = nd->path.dentry;
		save_parent.mnt = mntget(path->mnt);
		nd->path.dentry = path->dentry;

	}
	nd->inode = inode;
	/* Why this, you ask?  _Now_ we might have grown LOOKUP_JUMPED... */
finish_open:
	error = complete_walk(nd);
	if (error) {
		path_put(&save_parent);
		return error;
	}
	audit_inode(name, nd->path.dentry, 0);
	error = -EISDIR;
	if ((open_flag & O_CREAT) && S_ISDIR(nd->inode->i_mode))
		goto out;
	error = -ENOTDIR;
	if ((nd->flags & LOOKUP_DIRECTORY) && !can_lookup(nd->inode))
		goto out;
	if (!S_ISREG(nd->inode->i_mode))
		will_truncate = false;

	if (will_truncate) {
		error = mnt_want_write(nd->path.mnt);
		if (error)
			goto out;
		got_write = true;
	}
finish_open_created:
	error = may_open(&nd->path, acc_mode, open_flag);
	if (error)
		goto out;
	file->f_path.mnt = nd->path.mnt;
	error = finish_open(file, nd->path.dentry, NULL, opened);
	if (error) {
		if (error == -EOPENSTALE)
			goto stale_open;
		goto out;
	}
opened:
	error = open_check_o_direct(file);
	if (error)
		goto exit_fput;
	error = ima_file_check(file, op->acc_mode);
	if (error)
		goto exit_fput;

	if (will_truncate) {
		error = handle_truncate(file);
		if (error)
			goto exit_fput;
	}
out:
	if (got_write)
		mnt_drop_write(nd->path.mnt);
	path_put(&save_parent);
	terminate_walk(nd);
	return error;

exit_dput:
	path_put_conditional(path, nd);
	goto out;
exit_fput:
	fput(file);
	goto out;

stale_open:
	/* If no saved parent or already retried then can't retry */
	if (!save_parent.dentry || retried)
		goto out;

	BUG_ON(save_parent.dentry != dir);
	path_put(&nd->path);
	nd->path = save_parent;
	nd->inode = dir->d_inode;
	save_parent.mnt = NULL;
	save_parent.dentry = NULL;
	if (got_write) {
		mnt_drop_write(nd->path.mnt);
		got_write = false;
	}
	retried = true;
	goto retry_lookup;
}


follow_link(struct path *link, struct nameidata *nd, void **p)
{
	struct dentry *dentry = link->dentry;
	int error;
	char *s;

	BUG_ON(nd->flags & LOOKUP_RCU);

	if (link->mnt == nd->path.mnt)
		mntget(link->mnt);

	error = -ELOOP;
	if (unlikely(current->total_link_count >= 40))
		goto out_put_nd_path;

	cond_resched();
	current->total_link_count++;

	touch_atime(link);
	nd_set_link(nd, NULL);

	error = security_inode_follow_link(link->dentry, nd);
	if (error)
		goto out_put_nd_path;

	nd->last_type = LAST_BIND;
	*p = dentry->d_inode->i_op->follow_link(dentry, nd);
	error = PTR_ERR(*p);
	if (IS_ERR(*p))
		goto out_put_nd_path;

	error = 0;
	s = nd_get_link(nd);
	if (s) {
		error = __vfs_follow_link(nd, s);
		if (unlikely(error))
			put_link(nd, link, *p);
	}

	return error;

out_put_nd_path:
	*p = NULL;
	path_put(&nd->path);
	path_put(link);
	return error;
}


SYSCALL_DEFINE1(chroot, const char __user *, filename)
{
	struct path path;
	int error;
	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
retry:
	error = user_path_at(AT_FDCWD, filename, lookup_flags, &path);
	if (error)
		goto out;

	error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
	if (error)
		goto dput_and_out;

	error = -EPERM;
	if (!nsown_capable(CAP_SYS_CHROOT))
		goto dput_and_out;
	error = security_path_chroot(&path);
	if (error)
		goto dput_and_out;

	set_fs_root(current->fs, &path);
	error = 0;
dput_and_out:
	path_put(&path);
	if (retry_estale(error, lookup_flags)) {
		lookup_flags |= LOOKUP_REVAL;
		goto retry;
	}
out:
	return error;
}


int filp_close(struct file *filp, fl_owner_t id)
{
	int retval = 0;

	if (!file_count(filp)) {
		printk(KERN_ERR "VFS: Close: file count is 0\n");
		return 0;
	}

	if (filp->f_op && filp->f_op->flush)
		retval = filp->f_op->flush(filp, id);

	if (likely(!(filp->f_mode & FMODE_PATH))) {
		dnotify_flush(filp, id);
		locks_remove_posix(filp, id);
	}
	fput(filp);
	return retval;
}


int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
	struct file *filp)
{
	int ret;
	struct iattr newattrs;

	/* Not pretty: "inode->i_size" shouldn't really be signed. But it is. */
	if (length < 0)
		return -EINVAL;

	newattrs.ia_size = length;
	newattrs.ia_valid = ATTR_SIZE | time_attrs;
	if (filp) {
		newattrs.ia_file = filp;
		newattrs.ia_valid |= ATTR_FILE;
	}

	/* Remove suid/sgid on truncate too */
	ret = should_remove_suid(dentry);
	if (ret)
		newattrs.ia_valid |= ret | ATTR_FORCE;

	mutex_lock(&dentry->d_inode->i_mutex);
	ret = notify_change(dentry, &newattrs);
	mutex_unlock(&dentry->d_inode->i_mutex);
	return ret;
}


SYSCALL_DEFINE3(fchmodat, int, dfd, const char __user *, filename, umode_t, mode)
{
	struct path path;
	int error;
	unsigned int lookup_flags = LOOKUP_FOLLOW;
retry:
	error = user_path_at(dfd, filename, lookup_flags, &path);
	if (!error) {
		error = chmod_common(&path, mode);
		path_put(&path);
		if (retry_estale(error, lookup_flags)) {
			lookup_flags |= LOOKUP_REVAL;
			goto retry;
		}
	}
	return error;
}


COMPAT_SYSCALL_DEFINE2(truncate, const char __user *, path, compat_off_t, length)
{
	return do_sys_truncate(path, length);
}


static long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
{
	struct inode *inode;
	struct dentry *dentry;
	struct fd f;
	int error;

	error = -EINVAL;
	if (length < 0)
		goto out;
	error = -EBADF;
	f = fdget(fd);
	if (!f.file)
		goto out;

	/* explicitly opened as large or we are on 64-bit box */
	if (f.file->f_flags & O_LARGEFILE)
		small = 0;

	dentry = f.file->f_path.dentry;
	inode = dentry->d_inode;
	error = -EINVAL;
	if (!S_ISREG(inode->i_mode) || !(f.file->f_mode & FMODE_WRITE))
		goto out_putf;

	error = -EINVAL;
	/* Cannot ftruncate over 2^31 bytes without large file support */
	if (small && length > MAX_NON_LFS)
		goto out_putf;

	error = -EPERM;
	if (IS_APPEND(inode))
		goto out_putf;

	sb_start_write(inode->i_sb);
	error = locks_verify_truncate(inode, f.file, length);
	if (!error)
		error = security_path_truncate(&f.file->f_path);
	if (!error)
		error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, f.file);
	sb_end_write(inode->i_sb);
out_putf:
	fdput(f);
out:
	return error;
}


static int follow_managed(struct path *path, unsigned flags)
{
	struct vfsmount *mnt = path->mnt; /* held by caller, must be left alone */
	unsigned managed;
	bool need_mntput = false;
	int ret = 0;

	/* Given that we're not holding a lock here, we retain the value in a
	 * local variable for each dentry as we look at it so that we don't see
	 * the components of that value change under us */
	while (managed = ACCESS_ONCE(path->dentry->d_flags),
	       managed &= DCACHE_MANAGED_DENTRY,
	       unlikely(managed != 0)) {
		/* Allow the filesystem to manage the transit without i_mutex
		 * being held. */
		if (managed & DCACHE_MANAGE_TRANSIT) {
			BUG_ON(!path->dentry->d_op);
			BUG_ON(!path->dentry->d_op->d_manage);
			ret = path->dentry->d_op->d_manage(path->dentry, false);
			if (ret < 0)
				break;
		}

		/* Transit to a mounted filesystem. */
		if (managed & DCACHE_MOUNTED) {
			struct vfsmount *mounted = lookup_mnt(path);
			if (mounted) {
				dput(path->dentry);
				if (need_mntput)
					mntput(path->mnt);
				path->mnt = mounted;
				path->dentry = dget(mounted->mnt_root);
				need_mntput = true;
				continue;
			}

			/* Something is mounted on this dentry in another
			 * namespace and/or whatever was mounted there in this
			 * namespace got unmounted before we managed to get the
			 * vfsmount_lock */
		}

		/* Handle an automount point */
		if (managed & DCACHE_NEED_AUTOMOUNT) {
			ret = follow_automount(path, flags, &need_mntput);
			if (ret < 0)
				break;
			continue;
		}

		/* We didn't change the current path point */
		break;
	}

	if (need_mntput && path->mnt == mnt)
		mntput(path->mnt);
	if (ret == -EISDIR)
		ret = 0;
	return ret < 0 ? ret : need_mntput;
}


int generic_permission(struct inode *inode, int mask)
{
	int ret;

	/*
	 * Do the basic permission checks.
	 */
	ret = acl_permission_check(inode, mask);
	if (ret != -EACCES)
		return ret;

	if (S_ISDIR(inode->i_mode)) {
		/* DACs are overridable for directories */
		if (inode_capable(inode, CAP_DAC_OVERRIDE))
			return 0;
		if (!(mask & MAY_WRITE))
			if (inode_capable(inode, CAP_DAC_READ_SEARCH))
				return 0;
		return -EACCES;
	}
	/*
	 * Read/write DACs are always overridable.
	 * Executable DACs are overridable when there is
	 * at least one exec bit set.
	 */
	if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO))
		if (inode_capable(inode, CAP_DAC_OVERRIDE))
			return 0;

	/*
	 * Searching includes executable on directories, else just read.
	 */
	mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
	if (mask == MAY_READ)
		if (inode_capable(inode, CAP_DAC_READ_SEARCH))
			return 0;

	return -EACCES;
}


int finish_open(struct file *file, struct dentry *dentry,
		int (*open)(struct inode *, struct file *),
		int *opened)
{
	int error;
	BUG_ON(*opened & FILE_OPENED); /* once it's opened, it's opened */

	file->f_path.dentry = dentry;
	error = do_dentry_open(file, open, current_cred());
	if (!error)
		*opened |= FILE_OPENED;

	return error;
}


static void terminate_walk(struct nameidata *nd)
{
	if (!(nd->flags & LOOKUP_RCU)) {
		path_put(&nd->path);
	} else {
		nd->flags &= ~LOOKUP_RCU;
		if (!(nd->flags & LOOKUP_ROOT))
			nd->root.mnt = NULL;
		unlock_rcu_walk();
	}
}


SYSCALL_DEFINE5(fchownat, int, dfd, const char __user *, filename, uid_t, user,
		gid_t, group, int, flag)
{
	struct path path;
	int error = -EINVAL;
	int lookup_flags;

	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0)
		goto out;

	lookup_flags = (flag & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW;
	if (flag & AT_EMPTY_PATH)
		lookup_flags |= LOOKUP_EMPTY;
retry:
	error = user_path_at(dfd, filename, lookup_flags, &path);
	if (error)
		goto out;
	error = mnt_want_write(path.mnt);
	if (error)
		goto out_release;
	error = chown_common(&path, user, group);
	mnt_drop_write(path.mnt);
out_release:
	path_put(&path);
	if (retry_estale(error, lookup_flags)) {
		lookup_flags |= LOOKUP_REVAL;
		goto retry;
	}
out:
	return error;
}


static int complete_walk(struct nameidata *nd)
{
	struct dentry *dentry = nd->path.dentry;
	int status;

	if (nd->flags & LOOKUP_RCU) {
		nd->flags &= ~LOOKUP_RCU;
		if (!(nd->flags & LOOKUP_ROOT))
			nd->root.mnt = NULL;
		spin_lock(&dentry->d_lock);
		if (unlikely(!__d_rcu_to_refcount(dentry, nd->seq))) {
			spin_unlock(&dentry->d_lock);
			unlock_rcu_walk();
			return -ECHILD;
		}
		BUG_ON(nd->inode != dentry->d_inode);
		spin_unlock(&dentry->d_lock);
		mntget(nd->path.mnt);
		unlock_rcu_walk();
	}

	if (likely(!(nd->flags & LOOKUP_JUMPED)))
		return 0;

	if (likely(!(dentry->d_flags & DCACHE_OP_WEAK_REVALIDATE)))
		return 0;

	status = dentry->d_op->d_weak_revalidate(dentry, nd->flags);
	if (status > 0)
		return 0;

	if (!status)
		status = -ESTALE;

	path_put(&nd->path);
	return status;
}


static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
			    struct inode *new_dir, struct dentry *new_dentry)
{
	struct inode *target = new_dentry->d_inode;
	int error;

	error = security_inode_rename(old_dir, old_dentry, new_dir, new_dentry);
	if (error)
		return error;

	dget(new_dentry);
	if (target)
		mutex_lock(&target->i_mutex);

	error = -EBUSY;
	if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
		goto out;

	error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
	if (error)
		goto out;

	if (target)
		dont_mount(new_dentry);
	if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
		d_move(old_dentry, new_dentry);
out:
	if (target)
		mutex_unlock(&target->i_mutex);
	dput(new_dentry);
	return error;
}


struct file *do_file_open_root(struct dentry *dentry, struct vfsmount *mnt,
		const char *name, const struct open_flags *op)
{
	struct nameidata nd;
	struct file *file;
	struct filename filename = { .name = name };
	int flags = op->lookup_flags | LOOKUP_ROOT;

	nd.root.mnt = mnt;
	nd.root.dentry = dentry;

	if (dentry->d_inode->i_op->follow_link && op->intent & LOOKUP_OPEN)
		return ERR_PTR(-ELOOP);

	file = path_openat(-1, &filename, &nd, op, flags | LOOKUP_RCU);
	if (unlikely(file == ERR_PTR(-ECHILD)))
		file = path_openat(-1, &filename, &nd, op, flags);
	if (unlikely(file == ERR_PTR(-ESTALE)))
		file = path_openat(-1, &filename, &nd, op, flags | LOOKUP_REVAL);
	return file;
}


SYSCALL_DEFINE0(vhangup)
{
	if (capable(CAP_SYS_TTY_CONFIG)) {
		tty_vhangup_self();
		return 0;
	}
	return -EPERM;
}


static inline int build_open_flags(int flags, umode_t mode, struct open_flags *op)
{
	int lookup_flags = 0;
	int acc_mode;

	if (flags & O_CREAT)
		op->mode = (mode & S_IALLUGO) | S_IFREG;
	else
		op->mode = 0;

	/* Must never be set by userspace */
	flags &= ~FMODE_NONOTIFY & ~O_CLOEXEC;

	/*
	 * O_SYNC is implemented as __O_SYNC|O_DSYNC.  As many places only
	 * check for O_DSYNC if the need any syncing at all we enforce it's
	 * always set instead of having to deal with possibly weird behaviour
	 * for malicious applications setting only __O_SYNC.
	 */
	if (flags & __O_SYNC)
		flags |= O_DSYNC;

	if (flags & __O_TMPFILE) {
		if ((flags & O_TMPFILE_MASK) != O_TMPFILE)
			return -EINVAL;
		acc_mode = MAY_OPEN | ACC_MODE(flags);
	} else if (flags & O_PATH) {
		/*
		 * If we have O_PATH in the open flag. Then we
		 * cannot have anything other than the below set of flags
		 */
		flags &= O_DIRECTORY | O_NOFOLLOW | O_PATH;
		acc_mode = 0;
	} else {
		acc_mode = MAY_OPEN | ACC_MODE(flags);
	}

	op->open_flag = flags;

	/* O_TRUNC implies we need access checks for write permissions */
	if (flags & O_TRUNC)
		acc_mode |= MAY_WRITE;

	/* Allow the LSM permission hook to distinguish append
	   access from general write access. */
	if (flags & O_APPEND)
		acc_mode |= MAY_APPEND;

	op->acc_mode = acc_mode;

	op->intent = flags & O_PATH ? 0 : LOOKUP_OPEN;

	if (flags & O_CREAT) {
		op->intent |= LOOKUP_CREATE;
		if (flags & O_EXCL)
			op->intent |= LOOKUP_EXCL;
	}

	if (flags & O_DIRECTORY)
		lookup_flags |= LOOKUP_DIRECTORY;
	if (!(flags & O_NOFOLLOW))
		lookup_flags |= LOOKUP_FOLLOW;
	op->lookup_flags = lookup_flags;
	return 0;
}


static long do_unlinkat(int dfd, const char __user *pathname)
{
	int error;
	struct filename *name;
	struct dentry *dentry;
	struct nameidata nd;
	struct inode *inode = NULL;
	unsigned int lookup_flags = 0;
retry:
	name = user_path_parent(dfd, pathname, &nd, lookup_flags);
	if (IS_ERR(name))
		return PTR_ERR(name);

	error = -EISDIR;
	if (nd.last_type != LAST_NORM)
		goto exit1;

	nd.flags &= ~LOOKUP_PARENT;
	error = mnt_want_write(nd.path.mnt);
	if (error)
		goto exit1;

	mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
	dentry = lookup_hash(&nd);
	error = PTR_ERR(dentry);
	if (!IS_ERR(dentry)) {
		/* Why not before? Because we want correct error value */
		if (nd.last.name[nd.last.len])
			goto slashes;
		inode = dentry->d_inode;
		if (!inode)
			goto slashes;
		ihold(inode);
		error = security_path_unlink(&nd.path, dentry);
		if (error)
			goto exit2;
		error = vfs_unlink(nd.path.dentry->d_inode, dentry);
exit2:
		dput(dentry);
	}
	mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
	if (inode)
		iput(inode);	/* truncate the inode here */
	mnt_drop_write(nd.path.mnt);
exit1:
	path_put(&nd.path);
	putname(name);
	if (retry_estale(error, lookup_flags)) {
		lookup_flags |= LOOKUP_REVAL;
		inode = NULL;
		goto retry;
	}
	return error;

slashes:
	error = !dentry->d_inode ? -ENOENT :
		S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
	goto exit2;
}


static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
{
	int error;

	if (!victim->d_inode)
		return -ENOENT;

	BUG_ON(victim->d_parent->d_inode != dir);
	audit_inode_child(dir, victim, AUDIT_TYPE_CHILD_DELETE);

	error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
	if (error)
		return error;
	if (IS_APPEND(dir))
		return -EPERM;
	if (check_sticky(dir, victim->d_inode)||IS_APPEND(victim->d_inode)||
	    IS_IMMUTABLE(victim->d_inode) || IS_SWAPFILE(victim->d_inode))
		return -EPERM;
	if (isdir) {
		if (!S_ISDIR(victim->d_inode->i_mode))
			return -ENOTDIR;
		if (IS_ROOT(victim))
			return -EBUSY;
	} else if (S_ISDIR(victim->d_inode->i_mode))
		return -EISDIR;
	if (IS_DEADDIR(dir))
		return -ENOENT;
	if (victim->d_flags & DCACHE_NFSFS_RENAMED)
		return -EBUSY;
	return 0;
}


static inline int walk_component(struct nameidata *nd, struct path *path,
		int follow)
{
	struct inode *inode;
	int err;
	/*
	 * "." and ".." are special - ".." especially so because it has
	 * to be able to know about the current root directory and
	 * parent relationships.
	 */
	if (unlikely(nd->last_type != LAST_NORM))
		return handle_dots(nd, nd->last_type);
	err = lookup_fast(nd, path, &inode);
	if (unlikely(err)) {
		if (err < 0)
			goto out_err;

		err = lookup_slow(nd, path);
		if (err < 0)
			goto out_err;

		inode = path->dentry->d_inode;
	}
	err = -ENOENT;
	if (!inode)
		goto out_path_put;

	if (should_follow_link(inode, follow)) {
		if (nd->flags & LOOKUP_RCU) {
			if (unlikely(unlazy_walk(nd, path->dentry))) {
				err = -ECHILD;
				goto out_err;
			}
		}
		BUG_ON(inode != path->dentry->d_inode);
		return 1;
	}
	path_to_nameidata(path, nd);
	nd->inode = inode;
	return 0;

out_path_put:
	path_to_nameidata(path, nd);
out_err:
	terminate_walk(nd);
	return err;
}


static inline void lock_rcu_walk(void)
{
	br_read_lock(&vfsmount_lock);
	rcu_read_lock();
}


struct file *filp_open(const char *filename, int flags, umode_t mode)
{
	struct filename name = {.name = filename};
	return file_open_name(&name, flags, mode);
}


static int check_acl(struct inode *inode, int mask)
{
#ifdef CONFIG_FS_POSIX_ACL
	struct posix_acl *acl;

	if (mask & MAY_NOT_BLOCK) {
		acl = get_cached_acl_rcu(inode, ACL_TYPE_ACCESS);
	        if (!acl)
	                return -EAGAIN;
		/* no ->get_acl() calls in RCU mode... */
		if (acl == ACL_NOT_CACHED)
			return -ECHILD;
	        return posix_acl_permission(inode, acl, mask & ~MAY_NOT_BLOCK);
	}

	acl = get_cached_acl(inode, ACL_TYPE_ACCESS);

	/*
	 * A filesystem can force a ACL callback by just never filling the
	 * ACL cache. But normally you'd fill the cache either at inode
	 * instantiation time, or on the first ->get_acl call.
	 *
	 * If the filesystem doesn't have a get_acl() function at all, we'll
	 * just create the negative cache entry.
	 */
	if (acl == ACL_NOT_CACHED) {
	        if (inode->i_op->get_acl) {
			acl = inode->i_op->get_acl(inode, ACL_TYPE_ACCESS);
			if (IS_ERR(acl))
				return PTR_ERR(acl);
		} else {
		        set_cached_acl(inode, ACL_TYPE_ACCESS, NULL);
		        return -EAGAIN;
		}
	}

	if (acl) {
	        int error = posix_acl_permission(inode, acl, mask);
	        posix_acl_release(acl);
	        return error;
	}
#endif

	return -EAGAIN;
}


static int path_lookupat(int dfd, const char *name,
				unsigned int flags, struct nameidata *nd)
{
	struct file *base = NULL;
	struct path path;
	int err;

	/*
	 * Path walking is largely split up into 2 different synchronisation
	 * schemes, rcu-walk and ref-walk (explained in
	 * Documentation/filesystems/path-lookup.txt). These share much of the
	 * path walk code, but some things particularly setup, cleanup, and
	 * following mounts are sufficiently divergent that functions are
	 * duplicated. Typically there is a function foo(), and its RCU
	 * analogue, foo_rcu().
	 *
	 * -ECHILD is the error number of choice (just to avoid clashes) that
	 * is returned if some aspect of an rcu-walk fails. Such an error must
	 * be handled by restarting a traditional ref-walk (which will always
	 * be able to complete).
	 */
	err = path_init(dfd, name, flags | LOOKUP_PARENT, nd, &base);

	if (unlikely(err))
		return err;

	current->total_link_count = 0;
	err = link_path_walk(name, nd);

	if (!err && !(flags & LOOKUP_PARENT)) {
		err = lookup_last(nd, &path);
		while (err > 0) {
			void *cookie;
			struct path link = path;
			err = may_follow_link(&link, nd);
			if (unlikely(err))
				break;
			nd->flags |= LOOKUP_PARENT;
			err = follow_link(&link, nd, &cookie);
			if (err)
				break;
			err = lookup_last(nd, &path);
			put_link(nd, &link, cookie);
		}
	}

	if (!err)
		err = complete_walk(nd);

	if (!err && nd->flags & LOOKUP_DIRECTORY) {
		if (!can_lookup(nd->inode)) {
			path_put(&nd->path);
			err = -ENOTDIR;
		}
	}

	if (base)
		fput(base);

	if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) {
		path_put(&nd->root);
		nd->root.mnt = NULL;
	}
	return err;
}


SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags,
		umode_t, mode)
{
	if (force_o_largefile())
		flags |= O_LARGEFILE;

	return do_sys_open(dfd, filename, flags, mode);
}


static int handle_truncate(struct file *filp)
{
	struct path *path = &filp->f_path;
	struct inode *inode = path->dentry->d_inode;
	int error = get_write_access(inode);
	if (error)
		return error;
	/*
	 * Refuse to truncate files with mandatory locks held on them.
	 */
	error = locks_verify_locked(inode);
	if (!error)
		error = security_path_truncate(path);
	if (!error) {
		error = do_truncate(path->dentry, 0,
				    ATTR_MTIME|ATTR_CTIME|ATTR_OPEN,
				    filp);
	}
	put_write_access(inode);
	return error;
}


int open_check_o_direct(struct file *f)
{
	/* NB: we're sure to have correct a_ops only after f_op->open */
	if (f->f_flags & O_DIRECT) {
		if (!f->f_mapping->a_ops ||
		    ((!f->f_mapping->a_ops->direct_IO) &&
		    (!f->f_mapping->a_ops->get_xip_mem))) {
			return -EINVAL;
		}
	}
	return 0;
}


SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
{
	const struct cred *old_cred;
	struct cred *override_cred;
	struct path path;
	struct inode *inode;
	int res;
	unsigned int lookup_flags = LOOKUP_FOLLOW;

	if (mode & ~S_IRWXO)	/* where's F_OK, X_OK, W_OK, R_OK? */
		return -EINVAL;

	override_cred = prepare_creds();
	if (!override_cred)
		return -ENOMEM;

	override_cred->fsuid = override_cred->uid;
	override_cred->fsgid = override_cred->gid;

	if (!issecure(SECURE_NO_SETUID_FIXUP)) {
		/* Clear the capabilities if we switch to a non-root user */
		kuid_t root_uid = make_kuid(override_cred->user_ns, 0);
		if (!uid_eq(override_cred->uid, root_uid))
			cap_clear(override_cred->cap_effective);
		else
			override_cred->cap_effective =
				override_cred->cap_permitted;
	}

	old_cred = override_creds(override_cred);
retry:
	res = user_path_at(dfd, filename, lookup_flags, &path);
	if (res)
		goto out;

	inode = path.dentry->d_inode;

	if ((mode & MAY_EXEC) && S_ISREG(inode->i_mode)) {
		/*
		 * MAY_EXEC on regular files is denied if the fs is mounted
		 * with the "noexec" flag.
		 */
		res = -EACCES;
		if (path.mnt->mnt_flags & MNT_NOEXEC)
			goto out_path_release;
	}

	res = inode_permission(inode, mode | MAY_ACCESS);
	/* SuS v2 requires we report a read only fs too */
	if (res || !(mode & S_IWOTH) || special_file(inode->i_mode))
		goto out_path_release;
	/*
	 * This is a rare case where using __mnt_is_readonly()
	 * is OK without a mnt_want/drop_write() pair.  Since
	 * no actual write to the fs is performed here, we do
	 * not need to telegraph to that to anyone.
	 *
	 * By doing this, we accept that this access is
	 * inherently racy and know that the fs may change
	 * state before we even see this result.
	 */
	if (__mnt_is_readonly(path.mnt))
		res = -EROFS;

out_path_release:
	path_put(&path);
	if (retry_estale(res, lookup_flags)) {
		lookup_flags |= LOOKUP_REVAL;
		goto retry;
	}
out:
	revert_creds(old_cred);
	put_cred(override_cred);
	return res;
}


SYSCALL_DEFINE2(creat, const char __user *, pathname, umode_t, mode)
{
	return sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);
}


static inline int __get_file_write_access(struct inode *inode,
					  struct vfsmount *mnt)
{
	int error;
	error = get_write_access(inode);
	if (error)
		return error;
	/*
	 * Do not take mount writer counts on
	 * special files since no writes to
	 * the mount itself will occur.
	 */
	if (!special_file(inode->i_mode)) {
		/*
		 * Balanced in __fput()
		 */
		error = __mnt_want_write(mnt);
		if (error)
			put_write_access(inode);
	}
	return error;
}


SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
{
	if (force_o_largefile())
		flags |= O_LARGEFILE;

	return do_sys_open(AT_FDCWD, filename, flags, mode);
}


static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link)
{
	int ret;

	if (IS_ERR(link))
		goto fail;

	if (*link == '/') {
		set_root(nd);
		path_put(&nd->path);
		nd->path = nd->root;
		path_get(&nd->root);
		nd->flags |= LOOKUP_JUMPED;
	}
	nd->inode = nd->path.dentry->d_inode;

	ret = link_path_walk(link, nd);
	return ret;
fail:
	path_put(&nd->path);
	return PTR_ERR(link);
}


int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
	       struct inode *new_dir, struct dentry *new_dentry)
{
	int error;
	int is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
	const unsigned char *old_name;

	if (old_dentry->d_inode == new_dentry->d_inode)
 		return 0;

	error = may_delete(old_dir, old_dentry, is_dir);
	if (error)
		return error;

	if (!new_dentry->d_inode)
		error = may_create(new_dir, new_dentry);
	else
		error = may_delete(new_dir, new_dentry, is_dir);
	if (error)
		return error;

	if (!old_dir->i_op->rename)
		return -EPERM;

	old_name = fsnotify_oldname_init(old_dentry->d_name.name);

	if (is_dir)
		error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry);
	else
		error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry);
	if (!error)
		fsnotify_move(old_dir, new_dir, old_name, is_dir,
			      new_dentry->d_inode, old_dentry);
	fsnotify_oldname_free(old_name);

	return error;
}


static int chown_common(struct path *path, uid_t user, gid_t group)
{
	struct inode *inode = path->dentry->d_inode;
	int error;
	struct iattr newattrs;
	kuid_t uid;
	kgid_t gid;

	uid = make_kuid(current_user_ns(), user);
	gid = make_kgid(current_user_ns(), group);

	newattrs.ia_valid =  ATTR_CTIME;
	if (user != (uid_t) -1) {
		if (!uid_valid(uid))
			return -EINVAL;
		newattrs.ia_valid |= ATTR_UID;
		newattrs.ia_uid = uid;
	}
	if (group != (gid_t) -1) {
		if (!gid_valid(gid))
			return -EINVAL;
		newattrs.ia_valid |= ATTR_GID;
		newattrs.ia_gid = gid;
	}
	if (!S_ISDIR(inode->i_mode))
		newattrs.ia_valid |=
			ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV;
	mutex_lock(&inode->i_mutex);
	error = security_path_chown(path, uid, gid);
	if (!error)
		error = notify_change(path->dentry, &newattrs);
	mutex_unlock(&inode->i_mutex);

	return error;
}


struct file *file_open_root(struct dentry *dentry, struct vfsmount *mnt,
			    const char *filename, int flags)
{
	struct open_flags op;
	int err = build_open_flags(flags, 0, &op);
	if (err)
		return ERR_PTR(err);
	if (flags & O_CREAT)
		return ERR_PTR(-EINVAL);
	if (!filename && (flags & O_DIRECTORY))
		if (!dentry->d_inode->i_op->lookup)
			return ERR_PTR(-ENOTDIR);
	return do_file_open_root(dentry, mnt, filename, &op);
}


getname_flags(const char __user *filename, int flags, int *empty)
{
	struct filename *result, *err;
	int len;
	long max;
	char *kname;

	result = audit_reusename(filename);
	if (result)
		return result;

	result = __getname();
	if (unlikely(!result))
		return ERR_PTR(-ENOMEM);

	/*
	 * First, try to embed the struct filename inside the names_cache
	 * allocation
	 */
	kname = (char *)result + sizeof(*result);
	result->name = kname;
	result->separate = false;
	max = EMBEDDED_NAME_MAX;

recopy:
	len = strncpy_from_user(kname, filename, max);
	if (unlikely(len < 0)) {
		err = ERR_PTR(len);
		goto error;
	}

	/*
	 * Uh-oh. We have a name that's approaching PATH_MAX. Allocate a
	 * separate struct filename so we can dedicate the entire
	 * names_cache allocation for the pathname, and re-do the copy from
	 * userland.
	 */
	if (len == EMBEDDED_NAME_MAX && max == EMBEDDED_NAME_MAX) {
		kname = (char *)result;

		result = kzalloc(sizeof(*result), GFP_KERNEL);
		if (!result) {
			err = ERR_PTR(-ENOMEM);
			result = (struct filename *)kname;
			goto error;
		}
		result->name = kname;
		result->separate = true;
		max = PATH_MAX;
		goto recopy;
	}

	/* The empty path is special. */
	if (unlikely(!len)) {
		if (empty)
			*empty = 1;
		err = ERR_PTR(-ENOENT);
		if (!(flags & LOOKUP_EMPTY))
			goto error;
	}

	err = ERR_PTR(-ENAMETOOLONG);
	if (unlikely(len >= PATH_MAX))
		goto error;

	result->uptr = filename;
	audit_getname(result);
	return result;

error:
	final_putname(result);
	return err;
}


static int follow_dotdot_rcu(struct nameidata *nd)
{
	set_root_rcu(nd);

	while (1) {
		if (nd->path.dentry == nd->root.dentry &&
		    nd->path.mnt == nd->root.mnt) {
			break;
		}
		if (nd->path.dentry != nd->path.mnt->mnt_root) {
			struct dentry *old = nd->path.dentry;
			struct dentry *parent = old->d_parent;
			unsigned seq;

			seq = read_seqcount_begin(&parent->d_seq);
			if (read_seqcount_retry(&old->d_seq, nd->seq))
				goto failed;
			nd->path.dentry = parent;
			nd->seq = seq;
			break;
		}
		if (!follow_up_rcu(&nd->path))
			break;
		nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
	}
	follow_mount_rcu(nd);
	nd->inode = nd->path.dentry->d_inode;
	return 0;

failed:
	nd->flags &= ~LOOKUP_RCU;
	if (!(nd->flags & LOOKUP_ROOT))
		nd->root.mnt = NULL;
	unlock_rcu_walk();
	return -ECHILD;
}


int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
{
	struct inode *inode = old_dentry->d_inode;
	unsigned max_links = dir->i_sb->s_max_links;
	int error;

	if (!inode)
		return -ENOENT;

	error = may_create(dir, new_dentry);
	if (error)
		return error;

	if (dir->i_sb != inode->i_sb)
		return -EXDEV;

	/*
	 * A link to an append-only or immutable file cannot be created.
	 */
	if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
		return -EPERM;
	if (!dir->i_op->link)
		return -EPERM;
	if (S_ISDIR(inode->i_mode))
		return -EPERM;

	error = security_inode_link(old_dentry, dir, new_dentry);
	if (error)
		return error;

	mutex_lock(&inode->i_mutex);
	/* Make sure we don't allow creating hardlink to an unlinked file */
	if (inode->i_nlink == 0 && !(inode->i_state & I_LINKABLE))
		error =  -ENOENT;
	else if (max_links && inode->i_nlink >= max_links)
		error = -EMLINK;
	else
		error = dir->i_op->link(old_dentry, dir, new_dentry);

	if (!error && (inode->i_state & I_LINKABLE)) {
		spin_lock(&inode->i_lock);
		inode->i_state &= ~I_LINKABLE;
		spin_unlock(&inode->i_lock);
	}
	mutex_unlock(&inode->i_mutex);
	if (!error)
		fsnotify_link(dir, inode, new_dentry);
	return error;
}


int follow_down(struct path *path)
{
	unsigned managed;
	int ret;

	while (managed = ACCESS_ONCE(path->dentry->d_flags),
	       unlikely(managed & DCACHE_MANAGED_DENTRY)) {
		/* Allow the filesystem to manage the transit without i_mutex
		 * being held.
		 *
		 * We indicate to the filesystem if someone is trying to mount
		 * something here.  This gives autofs the chance to deny anyone
		 * other than its daemon the right to mount on its
		 * superstructure.
		 *
		 * The filesystem may sleep at this point.
		 */
		if (managed & DCACHE_MANAGE_TRANSIT) {
			BUG_ON(!path->dentry->d_op);
			BUG_ON(!path->dentry->d_op->d_manage);
			ret = path->dentry->d_op->d_manage(
				path->dentry, false);
			if (ret < 0)
				return ret == -EISDIR ? 0 : ret;
		}

		/* Transit to a mounted filesystem. */
		if (managed & DCACHE_MOUNTED) {
			struct vfsmount *mounted = lookup_mnt(path);
			if (!mounted)
				break;
			dput(path->dentry);
			mntput(path->mnt);
			path->mnt = mounted;
			path->dentry = dget(mounted->mnt_root);
			continue;
		}

		/* Don't handle automount points here */
		break;
	}
	return 0;
}


SYSCALL_DEFINE1(fchdir, unsigned int, fd)
{
	struct fd f = fdget_raw(fd);
	struct inode *inode;
	int error = -EBADF;

	error = -EBADF;
	if (!f.file)
		goto out;

	inode = file_inode(f.file);

	error = -ENOTDIR;
	if (!S_ISDIR(inode->i_mode))
		goto out_putf;

	error = inode_permission(inode, MAY_EXEC | MAY_CHDIR);
	if (!error)
		set_fs_pwd(current->fs, &f.file->f_path);
out_putf:
	fdput(f);
out:
	return error;
}


struct dentry *kern_path_create(int dfd, const char *pathname,
				struct path *path, unsigned int lookup_flags)
{
	struct dentry *dentry = ERR_PTR(-EEXIST);
	struct nameidata nd;
	int err2;
	int error;
	bool is_dir = (lookup_flags & LOOKUP_DIRECTORY);

	/*
	 * Note that only LOOKUP_REVAL and LOOKUP_DIRECTORY matter here. Any
	 * other flags passed in are ignored!
	 */
	lookup_flags &= LOOKUP_REVAL;

	error = do_path_lookup(dfd, pathname, LOOKUP_PARENT|lookup_flags, &nd);
	if (error)
		return ERR_PTR(error);

	/*
	 * Yucky last component or no last component at all?
	 * (foo/., foo/.., /////)
	 */
	if (nd.last_type != LAST_NORM)
		goto out;
	nd.flags &= ~LOOKUP_PARENT;
	nd.flags |= LOOKUP_CREATE | LOOKUP_EXCL;

	/* don't fail immediately if it's r/o, at least try to report other errors */
	err2 = mnt_want_write(nd.path.mnt);
	/*
	 * Do the final lookup.
	 */
	mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
	dentry = lookup_hash(&nd);
	if (IS_ERR(dentry))
		goto unlock;

	error = -EEXIST;
	if (dentry->d_inode)
		goto fail;
	/*
	 * Special case - lookup gave negative, but... we had foo/bar/
	 * From the vfs_mknod() POV we just have a negative dentry -
	 * all is fine. Let's be bastards - you had / on the end, you've
	 * been asking for (non-existent) directory. -ENOENT for you.
	 */
	if (unlikely(!is_dir && nd.last.name[nd.last.len])) {
		error = -ENOENT;
		goto fail;
	}
	if (unlikely(err2)) {
		error = err2;
		goto fail;
	}
	*path = nd.path;
	return dentry;
fail:
	dput(dentry);
	dentry = ERR_PTR(error);
unlock:
	mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
	if (!err2)
		mnt_drop_write(nd.path.mnt);
out:
	path_put(&nd.path);
	return dentry;
}


static int chmod_common(struct path *path, umode_t mode)
{
	struct inode *inode = path->dentry->d_inode;
	struct iattr newattrs;
	int error;

	error = mnt_want_write(path->mnt);
	if (error)
		return error;
	mutex_lock(&inode->i_mutex);
	error = security_path_chmod(path, mode);
	if (error)
		goto out_unlock;
	newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
	newattrs.ia_valid = ATTR_MODE | ATTR_CTIME;
	error = notify_change(path->dentry, &newattrs);
out_unlock:
	mutex_unlock(&inode->i_mutex);
	mnt_drop_write(path->mnt);
	return error;
}


static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
			  struct inode *new_dir, struct dentry *new_dentry)
{
	int error = 0;
	struct inode *target = new_dentry->d_inode;
	unsigned max_links = new_dir->i_sb->s_max_links;

	/*
	 * If we are going to change the parent - check write permissions,
	 * we'll need to flip '..'.
	 */
	if (new_dir != old_dir) {
		error = inode_permission(old_dentry->d_inode, MAY_WRITE);
		if (error)
			return error;
	}

	error = security_inode_rename(old_dir, old_dentry, new_dir, new_dentry);
	if (error)
		return error;

	dget(new_dentry);
	if (target)
		mutex_lock(&target->i_mutex);

	error = -EBUSY;
	if (d_mountpoint(old_dentry) || d_mountpoint(new_dentry))
		goto out;

	error = -EMLINK;
	if (max_links && !target && new_dir != old_dir &&
	    new_dir->i_nlink >= max_links)
		goto out;

	if (target)
		shrink_dcache_parent(new_dentry);
	error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
	if (error)
		goto out;

	if (target) {
		target->i_flags |= S_DEAD;
		dont_mount(new_dentry);
	}
out:
	if (target)
		mutex_unlock(&target->i_mutex);
	dput(new_dentry);
	if (!error)
		if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
			d_move(old_dentry,new_dentry);
	return error;
}


SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
{
	struct fd f = fdget(fd);
	int error = -EBADF;

	if (f.file) {
		error = do_fallocate(f.file, mode, offset, len);
		fdput(f);
	}
	return error;
}


static int do_dentry_open(struct file *f,
			  int (*open)(struct inode *, struct file *),
			  const struct cred *cred)
{
	static const struct file_operations empty_fops = {};
	struct inode *inode;
	int error;

	f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK |
				FMODE_PREAD | FMODE_PWRITE;

	if (unlikely(f->f_flags & O_PATH))
		f->f_mode = FMODE_PATH;

	path_get(&f->f_path);
	inode = f->f_inode = f->f_path.dentry->d_inode;
	if (f->f_mode & FMODE_WRITE) {
		error = __get_file_write_access(inode, f->f_path.mnt);
		if (error)
			goto cleanup_file;
		if (!special_file(inode->i_mode))
			file_take_write(f);
	}

	f->f_mapping = inode->i_mapping;
	file_sb_list_add(f, inode->i_sb);

	if (unlikely(f->f_mode & FMODE_PATH)) {
		f->f_op = &empty_fops;
		return 0;
	}

	f->f_op = fops_get(inode->i_fop);

	error = security_file_open(f, cred);
	if (error)
		goto cleanup_all;

	error = break_lease(inode, f->f_flags);
	if (error)
		goto cleanup_all;

	if (!open && f->f_op)
		open = f->f_op->open;
	if (open) {
		error = open(inode, f);
		if (error)
			goto cleanup_all;
	}
	if ((f->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
		i_readcount_inc(inode);

	f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);

	file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping);

	return 0;

cleanup_all:
	fops_put(f->f_op);
	file_sb_list_del(f);
	if (f->f_mode & FMODE_WRITE) {
		put_write_access(inode);
		if (!special_file(inode->i_mode)) {
			/*
			 * We don't consider this a real
			 * mnt_want/drop_write() pair
			 * because it all happenend right
			 * here, so just reset the state.
			 */
			file_reset_write(f);
			__mnt_drop_write(f->f_path.mnt);
		}
	}
cleanup_file:
	path_put(&f->f_path);
	f->f_path.mnt = NULL;
	f->f_path.dentry = NULL;
	f->f_inode = NULL;
	return error;
}


int vfs_unlink(struct inode *dir, struct dentry *dentry)
{
	int error = may_delete(dir, dentry, 0);

	if (error)
		return error;

	if (!dir->i_op->unlink)
		return -EPERM;

	mutex_lock(&dentry->d_inode->i_mutex);
	if (d_mountpoint(dentry))
		error = -EBUSY;
	else {
		error = security_inode_unlink(dir, dentry);
		if (!error) {
			error = dir->i_op->unlink(dir, dentry);
			if (!error)
				dont_mount(dentry);
		}
	}
	mutex_unlock(&dentry->d_inode->i_mutex);

	/* We don't d_delete() NFS sillyrenamed files--they still exist. */
	if (!error && !(dentry->d_flags & DCACHE_NFSFS_RENAMED)) {
		fsnotify_link_count(dentry->d_inode);
		d_delete(dentry);
	}

	return error;
}


int follow_up(struct path *path)
{
	struct mount *mnt = real_mount(path->mnt);
	struct mount *parent;
	struct dentry *mountpoint;

	br_read_lock(&vfsmount_lock);
	parent = mnt->mnt_parent;
	if (parent == mnt) {
		br_read_unlock(&vfsmount_lock);
		return 0;
	}
	mntget(&parent->mnt);
	mountpoint = dget(mnt->mnt_mountpoint);
	br_read_unlock(&vfsmount_lock);
	dput(path->dentry);
	path->dentry = mountpoint;
	mntput(path->mnt);
	path->mnt = &parent->mnt;
	return 1;
}


static void follow_mount_rcu(struct nameidata *nd)
{
	while (d_mountpoint(nd->path.dentry)) {
		struct mount *mounted;
		mounted = __lookup_mnt(nd->path.mnt, nd->path.dentry, 1);
		if (!mounted)
			break;
		nd->path.mnt = &mounted->mnt;
		nd->path.dentry = mounted->mnt.mnt_root;
		nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
	}
}


SYSCALL_DEFINE3(chown, const char __user *, filename, uid_t, user, gid_t, group)
{
	return sys_fchownat(AT_FDCWD, filename, user, group, 0);
}


SYSCALL_DEFINE2(truncate64, const char __user *, path, loff_t, length)
{
	return do_sys_truncate(path, length);
}


SYSCALL_DEFINE2(access, const char __user *, filename, int, mode)
{
	return sys_faccessat(AT_FDCWD, filename, mode);
}


static inline int check_sticky(struct inode *dir, struct inode *inode)
{
	kuid_t fsuid = current_fsuid();

	if (!(dir->i_mode & S_ISVTX))
		return 0;
	if (uid_eq(inode->i_uid, fsuid))
		return 0;
	if (uid_eq(dir->i_uid, fsuid))
		return 0;
	return !inode_capable(inode, CAP_FOWNER);
}


long vfs_truncate(struct path *path, loff_t length)
{
	struct inode *inode;
	long error;

	inode = path->dentry->d_inode;

	/* For directories it's -EISDIR, for other non-regulars - -EINVAL */
	if (S_ISDIR(inode->i_mode))
		return -EISDIR;
	if (!S_ISREG(inode->i_mode))
		return -EINVAL;

	error = mnt_want_write(path->mnt);
	if (error)
		goto out;

	error = inode_permission(inode, MAY_WRITE);
	if (error)
		goto mnt_drop_write_and_out;

	error = -EPERM;
	if (IS_APPEND(inode))
		goto mnt_drop_write_and_out;

	error = get_write_access(inode);
	if (error)
		goto mnt_drop_write_and_out;

	/*
	 * Make sure that there are no leases.  get_write_access() protects
	 * against the truncate racing with a lease-granting setlease().
	 */
	error = break_lease(inode, O_WRONLY);
	if (error)
		goto put_write_and_out;

	error = locks_verify_truncate(inode, NULL, length);
	if (!error)
		error = security_path_truncate(path);
	if (!error)
		error = do_truncate(path->dentry, length, 0, NULL);

put_write_and_out:
	put_write_access(inode);
mnt_drop_write_and_out:
	mnt_drop_write(path->mnt);
out:
	return error;
}


static int path_init(int dfd, const char *name, unsigned int flags,
		     struct nameidata *nd, struct file **fp)
{
	int retval = 0;

	nd->last_type = LAST_ROOT; /* if there are only slashes... */
	nd->flags = flags | LOOKUP_JUMPED;
	nd->depth = 0;
	if (flags & LOOKUP_ROOT) {
		struct inode *inode = nd->root.dentry->d_inode;
		if (*name) {
			if (!can_lookup(inode))
				return -ENOTDIR;
			retval = inode_permission(inode, MAY_EXEC);
			if (retval)
				return retval;
		}
		nd->path = nd->root;
		nd->inode = inode;
		if (flags & LOOKUP_RCU) {
			lock_rcu_walk();
			nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
		} else {
			path_get(&nd->path);
		}
		return 0;
	}

	nd->root.mnt = NULL;

	if (*name=='/') {
		if (flags & LOOKUP_RCU) {
			lock_rcu_walk();
			set_root_rcu(nd);
		} else {
			set_root(nd);
			path_get(&nd->root);
		}
		nd->path = nd->root;
	} else if (dfd == AT_FDCWD) {
		if (flags & LOOKUP_RCU) {
			struct fs_struct *fs = current->fs;
			unsigned seq;

			lock_rcu_walk();

			do {
				seq = read_seqcount_begin(&fs->seq);
				nd->path = fs->pwd;
				nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
			} while (read_seqcount_retry(&fs->seq, seq));
		} else {
			get_fs_pwd(current->fs, &nd->path);
		}
	} else {
		/* Caller must check execute permissions on the starting path component */
		struct fd f = fdget_raw(dfd);
		struct dentry *dentry;

		if (!f.file)
			return -EBADF;

		dentry = f.file->f_path.dentry;

		if (*name) {
			if (!can_lookup(dentry->d_inode)) {
				fdput(f);
				return -ENOTDIR;
			}
		}

		nd->path = f.file->f_path;
		if (flags & LOOKUP_RCU) {
			if (f.need_put)
				*fp = f.file;
			nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
			lock_rcu_walk();
		} else {
			path_get(&nd->path);
			fdput(f);
		}
	}

	nd->inode = nd->path.dentry->d_inode;
	return 0;
}


int generic_file_open(struct inode * inode, struct file * filp)
{
	if (!(filp->f_flags & O_LARGEFILE) && i_size_read(inode) > MAX_NON_LFS)
		return -EOVERFLOW;
	return 0;
}


SYSCALL_DEFINE3(fchown, unsigned int, fd, uid_t, user, gid_t, group)
{
	struct fd f = fdget(fd);
	int error = -EBADF;

	if (!f.file)
		goto out;

	error = mnt_want_write_file(f.file);
	if (error)
		goto out_fput;
	audit_inode(NULL, f.file->f_path.dentry, 0);
	error = chown_common(&f.file->f_path, user, group);
	mnt_drop_write_file(f.file);
out_fput:
	fdput(f);
out:
	return error;
}


SYSCALL_DEFINE2(truncate, const char __user *, path, long, length)
{
	return do_sys_truncate(path, length);
}


static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
			       struct inode **inode)
{
	for (;;) {
		struct mount *mounted;
		/*
		 * Don't forget we might have a non-mountpoint managed dentry
		 * that wants to block transit.
		 */
		if (unlikely(managed_dentry_might_block(path->dentry)))
			return false;

		if (!d_mountpoint(path->dentry))
			break;

		mounted = __lookup_mnt(path->mnt, path->dentry, 1);
		if (!mounted)
			break;
		path->mnt = &mounted->mnt;
		path->dentry = mounted->mnt.mnt_root;
		nd->flags |= LOOKUP_JUMPED;
		nd->seq = read_seqcount_begin(&path->dentry->d_seq);
		/*
		 * Update the inode too. We don't need to re-check the
		 * dentry sequence number here after this d_inode read,
		 * because a mount-point is always pinned.
		 */
		*inode = path->dentry->d_inode;
	}
	return true;
}


static int link_path_walk(const char *name, struct nameidata *nd)
{
	struct path next;
	int err;

	while (*name=='/')
		name++;
	if (!*name)
		return 0;

	/* At this point we know we have a real path component. */
	for(;;) {
		struct qstr this;
		long len;
		int type;

		err = may_lookup(nd);
 		if (err)
			break;

		len = hash_name(name, &this.hash);
		this.name = name;
		this.len = len;

		type = LAST_NORM;
		if (name[0] == '.') switch (len) {
			case 2:
				if (name[1] == '.') {
					type = LAST_DOTDOT;
					nd->flags |= LOOKUP_JUMPED;
				}
				break;
			case 1:
				type = LAST_DOT;
		}
		if (likely(type == LAST_NORM)) {
			struct dentry *parent = nd->path.dentry;
			nd->flags &= ~LOOKUP_JUMPED;
			if (unlikely(parent->d_flags & DCACHE_OP_HASH)) {
				err = parent->d_op->d_hash(parent, &this);
				if (err < 0)
					break;
			}
		}

		nd->last = this;
		nd->last_type = type;

		if (!name[len])
			return 0;
		/*
		 * If it wasn't NUL, we know it was '/'. Skip that
		 * slash, and continue until no more slashes.
		 */
		do {
			len++;
		} while (unlikely(name[len] == '/'));
		if (!name[len])
			return 0;

		name += len;

		err = walk_component(nd, &next, LOOKUP_FOLLOW);
		if (err < 0)
			return err;

		if (err) {
			err = nested_symlink(&next, nd);
			if (err)
				return err;
		}
		if (!can_lookup(nd->inode)) {
			err = -ENOTDIR;
			break;
		}
	}
	terminate_walk(nd);
	return err;
}


long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{
	struct open_flags op;
	int fd = build_open_flags(flags, mode, &op);
	struct filename *tmp;

	if (fd)
		return fd;

	tmp = getname(filename);
	if (IS_ERR(tmp))
		return PTR_ERR(tmp);

	fd = get_unused_fd_flags(flags);
	if (fd >= 0) {
		struct file *f = do_filp_open(dfd, tmp, &op);
		if (IS_ERR(f)) {
			put_unused_fd(fd);
			fd = PTR_ERR(f);
		} else {
			fsnotify_open(f);
			fd_install(fd, f);
		}
	}
	putname(tmp);
	return fd;
}


static inline int can_lookup(struct inode *inode)
{
	if (likely(inode->i_opflags & IOP_LOOKUP))
		return 1;
	if (likely(!inode->i_op->lookup))
		return 0;

	/* We do this once for the lifetime of the inode */
	spin_lock(&inode->i_lock);
	inode->i_opflags |= IOP_LOOKUP;
	spin_unlock(&inode->i_lock);
	return 1;
}


static int atomic_open(struct nameidata *nd, struct dentry *dentry,
			struct path *path, struct file *file,
			const struct open_flags *op,
			bool got_write, bool need_lookup,
			int *opened)
{
	struct inode *dir =  nd->path.dentry->d_inode;
	unsigned open_flag = open_to_namei_flags(op->open_flag);
	umode_t mode;
	int error;
	int acc_mode;
	int create_error = 0;
	struct dentry *const DENTRY_NOT_SET = (void *) -1UL;

	BUG_ON(dentry->d_inode);

	/* Don't create child dentry for a dead directory. */
	if (unlikely(IS_DEADDIR(dir))) {
		error = -ENOENT;
		goto out;
	}

	mode = op->mode;
	if ((open_flag & O_CREAT) && !IS_POSIXACL(dir))
		mode &= ~current_umask();

	if ((open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT)) {
		open_flag &= ~O_TRUNC;
		*opened |= FILE_CREATED;
	}

	/*
	 * Checking write permission is tricky, bacuse we don't know if we are
	 * going to actually need it: O_CREAT opens should work as long as the
	 * file exists.  But checking existence breaks atomicity.  The trick is
	 * to check access and if not granted clear O_CREAT from the flags.
	 *
	 * Another problem is returing the "right" error value (e.g. for an
	 * O_EXCL open we want to return EEXIST not EROFS).
	 */
	if (((open_flag & (O_CREAT | O_TRUNC)) ||
	    (open_flag & O_ACCMODE) != O_RDONLY) && unlikely(!got_write)) {
		if (!(open_flag & O_CREAT)) {
			/*
			 * No O_CREATE -> atomicity not a requirement -> fall
			 * back to lookup + open
			 */
			goto no_open;
		} else if (open_flag & (O_EXCL | O_TRUNC)) {
			/* Fall back and fail with the right error */
			create_error = -EROFS;
			goto no_open;
		} else {
			/* No side effects, safe to clear O_CREAT */
			create_error = -EROFS;
			open_flag &= ~O_CREAT;
		}
	}

	if (open_flag & O_CREAT) {
		error = may_o_create(&nd->path, dentry, mode);
		if (error) {
			create_error = error;
			if (open_flag & O_EXCL)
				goto no_open;
			open_flag &= ~O_CREAT;
		}
	}

	if (nd->flags & LOOKUP_DIRECTORY)
		open_flag |= O_DIRECTORY;

	file->f_path.dentry = DENTRY_NOT_SET;
	file->f_path.mnt = nd->path.mnt;
	error = dir->i_op->atomic_open(dir, dentry, file, open_flag, mode,
				      opened);
	if (error < 0) {
		if (create_error && error == -ENOENT)
			error = create_error;
		goto out;
	}

	acc_mode = op->acc_mode;
	if (*opened & FILE_CREATED) {
		fsnotify_create(dir, dentry);
		acc_mode = MAY_OPEN;
	}

	if (error) {	/* returned 1, that is */
		if (WARN_ON(file->f_path.dentry == DENTRY_NOT_SET)) {
			error = -EIO;
			goto out;
		}
		if (file->f_path.dentry) {
			dput(dentry);
			dentry = file->f_path.dentry;
		}
		if (create_error && dentry->d_inode == NULL) {
			error = create_error;
			goto out;
		}
		goto looked_up;
	}

	/*
	 * We didn't have the inode before the open, so check open permission
	 * here.
	 */
	error = may_open(&file->f_path, acc_mode, open_flag);
	if (error)
		fput(file);

out:
	dput(dentry);
	return error;

no_open:
	if (need_lookup) {
		dentry = lookup_real(dir, dentry, nd->flags);
		if (IS_ERR(dentry))
			return PTR_ERR(dentry);

		if (create_error) {
			int open_flag = op->open_flag;

			error = create_error;
			if ((open_flag & O_EXCL)) {
				if (!dentry->d_inode)
					goto out;
			} else if (!dentry->d_inode) {
				goto out;
			} else if ((open_flag & O_TRUNC) &&
				   S_ISREG(dentry->d_inode->i_mode)) {
				goto out;
			}
			/* will fail later, go on to get the right error */
		}
	}
looked_up:
	path->dentry = dentry;
	path->mnt = nd->path.mnt;
	return 1;
}


ftp_close(ftpbuf_t *ftp)
{
	if (ftp == NULL) {
		return NULL;
	}
	if (ftp->data) {
		data_close(ftp, ftp->data);
	}
	if (ftp->fd != -1) {
#if HAVE_OPENSSL_EXT
		if (ftp->ssl_active) {
			SSL_shutdown(ftp->ssl_handle);
			SSL_free(ftp->ssl_handle);
		}
#endif
		closesocket(ftp->fd);
	}
	ftp_gc(ftp);
	efree(ftp);
	return NULL;
}

Integer overflow in the ftp_genlist function in ext/ftp/ftp.c in PHP before 5.4.41, 5.5.x before 5.5.25, and 5.6.x before 5.6.9 allows remote FTP servers to execute arbitrary code via a long reply to a LIST command, leading to a heap-based buffer overflow.


check vertexIndex which might be -1 for broken DXF

void DL_Dxf::addDimRadial(DL_CreationInterface* creationInterface) {
    DL_DimensionData d = getDimData();

    DL_DimRadialData dr(
        // definition point
        getRealValue(15, 0.0),
        getRealValue(25, 0.0),
        getRealValue(35, 0.0),
        // leader length:
        getRealValue(40, 0.0));
    creationInterface->addDimRadial(d, dr);
}

A code execution vulnerability exists in the DL_Dxf::handleLWPolylineData functionality of Ribbonsoft dxflib 3.17.0. A specially-crafted .dxf file can lead to a heap buffer overflow. An attacker can provide a malicious file to trigger this vulnerability.


void DL_Dxf::addDimOrdinate(DL_CreationInterface* creationInterface) {
    DL_DimensionData d = getDimData();

    // ordinate dimension:
    DL_DimOrdinateData dl(
        // definition point 1
        getRealValue(13, 0.0),
        getRealValue(23, 0.0),
        getRealValue(33, 0.0),
        // definition point 2
        getRealValue(14, 0.0),
        getRealValue(24, 0.0),
        getRealValue(34, 0.0),
        (getIntValue(70, 0)&64)==64         // true: X-type, false: Y-type
    );
    creationInterface->addDimOrdinate(d, dl);
}


int DL_Dxf::getLibVersion(const std::string& str) {
    int d[4];
    int idx = 0;
    //char v[4][5];
    std::string v[4];
    int ret = 0;

    for (unsigned int i=0; i<str.length() && idx<3; ++i) {
        if (str[i]=='.') {
            d[idx] = i;
            idx++;
        }
    }

    if (idx>=2) {
        d[3] = str.length();

        v[0] = str.substr(0, d[0]);
        v[1] = str.substr(d[0]+1, d[1]-d[0]-1);
        v[2] = str.substr(d[1]+1, d[2]-d[1]-1);
        if (idx>=3) {
            v[3] = str.substr(d[2]+1, d[3]-d[2]-1);
        }
        else {
            v[3] = "0";
        }

        ret = (atoi(v[0].c_str())<<(3*8)) +
              (atoi(v[1].c_str())<<(2*8)) +
              (atoi(v[2].c_str())<<(1*8)) +
              (atoi(v[3].c_str())<<(0*8));

        return ret;
    } else {
        std::cerr << "DL_Dxf::getLibVersion: invalid version number: " << str << "\n";
        return 0;
    }
}


bool DL_Dxf::in(const std::string& file, DL_CreationInterface* creationInterface) {
    FILE *fp;
    firstCall = true;
    currentObjectType = DL_UNKNOWN;

    fp = fopen(file.c_str(), "rt");
    if (fp) {
        std::locale oldLocale = std::locale::global(std::locale("C"));	// use dot in numbers
        while (readDxfGroups(fp, creationInterface)) {}
        std::locale::global(oldLocale);
        fclose(fp);
        return true;
    }

    return false;
}


bool DL_Dxf::handleXData(DL_CreationInterface* creationInterface) {
    if (groupCode==1001) {
        creationInterface->addXDataApp(groupValue);
        return true;
    }
    else if (groupCode>=1000 && groupCode<=1009) {
        creationInterface->addXDataString(groupCode, groupValue);
        return true;
    }
    else if (groupCode>=1010 && groupCode<=1059) {
        creationInterface->addXDataReal(groupCode, toReal(groupValue));
        return true;
    }
    else if (groupCode>=1060 && groupCode<=1070) {
        creationInterface->addXDataInt(groupCode, toInt(groupValue));
        return true;
    }
    else if (groupCode==1071) {
        creationInterface->addXDataInt(groupCode, toInt(groupValue));
        return true;
    }

    return false;
}


void DL_Dxf::writePolylineEnd(DL_WriterA& dw) {
    if (version==DL_VERSION_2000) {
    } else {
        dw.entity("SEQEND");
    }
}


void DL_Dxf::add3dFace(DL_CreationInterface* creationInterface) {
    DL_3dFaceData td;

    for (int k = 0; k < 4; k++) {
       td.x[k] = getRealValue(10 + k, 0.0);
       td.y[k] = getRealValue(20 + k, 0.0);
       td.z[k] = getRealValue(30 + k, 0.0);
    }
    creationInterface->add3dFace(td);
}


void DL_Dxf::writeLeaderEnd(DL_WriterA& dw,
                 const DL_LeaderData& data) {
    if (version==DL_VERSION_2000) {
        dw.dxfString(1001, "ACAD");
        dw.dxfString(1000, "DSTYLE");
        dw.dxfString(1002, "{");
        dw.dxfInt(1070,40);
        dw.dxfReal(1040, data.dimScale);
        dw.dxfString(1002, "}");
    }
}


void DL_Dxf::addImageDef(DL_CreationInterface* creationInterface) {
    DL_ImageDefData id(// handle
        getStringValue(5, ""),
        getStringValue(1, ""));

    creationInterface->linkImage(id);
    creationInterface->endEntity();
    currentObjectType = DL_UNKNOWN;
}


void DL_Dxf::writeView(DL_WriterA& dw) {
    dw.dxfString(  0, "TABLE");
    dw.dxfString(  2, "VIEW");
    if (version==DL_VERSION_2000) {
        dw.dxfHex(5, 6);
    }
    //dw.dxfHex(330, 0);
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbSymbolTable");
    }
    dw.dxfInt( 70, 0);
    dw.dxfString(  0, "ENDTAB");
}


bool DL_Dxf::readDxfGroups(std::istream& stream,
                           DL_CreationInterface* creationInterface) {

    static int line = 1;

    // Read one group of the DXF file and chop the lines:
    if (DL_Dxf::getStrippedLine(groupCodeTmp, DL_DXF_MAXLINE, stream) &&
            DL_Dxf::getStrippedLine(groupValue, DL_DXF_MAXLINE, stream, false) ) {

        groupCode = (unsigned int)toInt(groupCodeTmp);

        line+=2;
        processDXFGroup(creationInterface, groupCode, groupValue);
    }
    return !stream.eof();
}


void DL_Dxf::addTextStyle(DL_CreationInterface* creationInterface) {
    std::string name = getStringValue(2, "");
    if (name.length()==0) {
        return;
    }

    DL_StyleData d(
        // name:
        name,
        // flags
        getIntValue(70, 0),
        // fixed text heigth:
        getRealValue(40, 0.0),
        // width factor:
        getRealValue(41, 0.0),
        // oblique angle:
        getRealValue(50, 0.0),
        // text generation flags:
        getIntValue(71, 0),
        // last height used:
        getRealValue(42, 0.0),
        // primart font file:
        getStringValue(3, ""),
        // big font file:
        getStringValue(4, "")
        );
    creationInterface->addTextStyle(d);
}


void DL_Dxf::writePoint(DL_WriterA& dw,
                        const DL_PointData& data,
                        const DL_Attributes& attrib) {
    dw.entity("POINT");
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbPoint");
    }
    dw.coord(DL_POINT_COORD_CODE, data.x, data.y, data.z);
}


void DL_Dxf::writeDimLinear(DL_WriterA& dw,
                            const DL_DimensionData& data,
                            const DL_DimLinearData& edata,
                            const DL_Attributes& attrib) {

    dw.entity("DIMENSION");

    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbDimension");
    }

    dw.dxfReal(10, data.dpx);
    dw.dxfReal(20, data.dpy);
    dw.dxfReal(30, data.dpz);

    dw.dxfReal(11, data.mpx);
    dw.dxfReal(21, data.mpy);
    dw.dxfReal(31, 0.0);

    dw.dxfInt(70, data.type);
    if (version>DL_VERSION_R12) {
        dw.dxfInt(71, data.attachmentPoint);
        dw.dxfInt(72, data.lineSpacingStyle); // opt
        dw.dxfInt(74, data.arrow1Flipped);
        dw.dxfInt(75, data.arrow2Flipped);
        dw.dxfReal(41, data.lineSpacingFactor); // opt
    }

    dw.dxfReal(42, data.angle);

    dw.dxfString(1, data.text);   // opt
    //dw.dxfString(3, data.style);
    dw.dxfString(3, "Standard");

    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbAlignedDimension");
    }

    dw.dxfReal(13, edata.dpx1);
    dw.dxfReal(23, edata.dpy1);
    dw.dxfReal(33, 0.0);

    dw.dxfReal(14, edata.dpx2);
    dw.dxfReal(24, edata.dpy2);
    dw.dxfReal(34, 0.0);

    dw.dxfReal(50, edata.angle/(2.0*M_PI)*360.0);

    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbRotatedDimension");
    }

    writeDimStyleOverrides(dw, data);
}


void DL_Dxf::addPoint(DL_CreationInterface* creationInterface) {
    DL_PointData d(getRealValue(10, 0.0),
                   getRealValue(20, 0.0),
                   getRealValue(30, 0.0));
    creationInterface->addPoint(d);
}


void DL_Dxf::writeLeader(DL_WriterA& dw,
                         const DL_LeaderData& data,
                         const DL_Attributes& attrib) {
    if (version>DL_VERSION_R12) {
        dw.entity("LEADER");
        if (version==DL_VERSION_2000) {
            dw.dxfString(100, "AcDbEntity");
        }
        dw.entityAttributes(attrib);
        if (version==DL_VERSION_2000) {
            dw.dxfString(100, "AcDbLeader");
        }
        dw.dxfString(3, "Standard");
        dw.dxfInt(71, data.arrowHeadFlag);
        dw.dxfInt(72, data.leaderPathType);
        dw.dxfInt(73, data.leaderCreationFlag);
        dw.dxfInt(74, data.hooklineDirectionFlag);
        dw.dxfInt(75, data.hooklineFlag);
        dw.dxfReal(40, data.textAnnotationHeight);
        dw.dxfReal(41, data.textAnnotationWidth);
        dw.dxfInt(76, data.number);
    }
}


void DL_Dxf::addRay(DL_CreationInterface* creationInterface) {
    DL_RayData d(getRealValue(10, 0.0),
                 getRealValue(20, 0.0),
                 getRealValue(30, 0.0),
                 getRealValue(11, 0.0),
                 getRealValue(21, 0.0),
                 getRealValue(31, 0.0));

    creationInterface->addRay(d);
}


void DL_Dxf::writeObjectsEnd(DL_WriterA& dw) {
    dw.dxfString(  0, "ENDSEC");
}


void DL_Dxf::addHatchEdge() {
    if (hatchEdge.defined) {
        if (hatchEdges.size()>0) {
            hatchEdges.back().push_back(hatchEdge);
        }
        hatchEdge = DL_HatchEdgeData();
    }
}


void DL_Dxf::addDictionaryEntry(DL_CreationInterface* creationInterface) {
    creationInterface->addDictionaryEntry(DL_DictionaryEntryData(getStringValue(3, ""), getStringValue(350, "")));
}


void DL_Dxf::addVertex(DL_CreationInterface* creationInterface) {

    // vertex defines a face of the mesh if its vertex flags group has the
    // 128 bit set but not the 64 bit. 10, 20, 30 are irrelevant and set to
    // 0 in this case
    if ((getIntValue(70, 0)&128) && !(getIntValue(70, 0)&64)) {
        return;
    }

    DL_VertexData d(getRealValue(10, 0.0),
                    getRealValue(20, 0.0),
                    getRealValue(30, 0.0),
                    getRealValue(42, 0.0));

    creationInterface->addVertex(d);
}


void DL_Dxf::writeSpline(DL_WriterA& dw,
                         const DL_SplineData& data,
                         const DL_Attributes& attrib) {

    dw.entity("SPLINE");
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbSpline");
    }
    dw.dxfInt(70, data.flags);
    dw.dxfInt(71, data.degree);
    dw.dxfInt(72, data.nKnots);            // number of knots
    dw.dxfInt(73, data.nControl);          // number of control points
    dw.dxfInt(74, data.nFit);              // number of fit points
}


void DL_Dxf::writeLinetype(DL_WriterA& dw,
                           const DL_LinetypeData& data) {

    std::string nameUpper = data.name;
    std::transform(nameUpper.begin(), nameUpper.end(), nameUpper.begin(), ::toupper);

    if (data.name.empty()) {
        std::cerr << "DL_Dxf::writeLinetype: "
        << "Line type name must not be empty\n";
        return;
    }

    // ignore BYLAYER, BYBLOCK for R12
    if (version<DL_VERSION_2000) {
        if (nameUpper=="BYBLOCK" || nameUpper=="BYLAYER") {
            return;
        }
    }

    // write id (not for R12)
    if (nameUpper=="BYBLOCK") {
        dw.tableLinetypeEntry(0x14);
    } else if (nameUpper=="BYLAYER") {
        dw.tableLinetypeEntry(0x15);
    } else if (nameUpper=="CONTINUOUS") {
        dw.tableLinetypeEntry(0x16);
    } else {
        dw.tableLinetypeEntry();
    }

    dw.dxfString(2, data.name);
    dw.dxfInt(70, data.flags);

    if (nameUpper=="BYBLOCK") {
        dw.dxfString(3, "");
        dw.dxfInt(72, 65);
        dw.dxfInt(73, 0);
        dw.dxfReal(40, 0.0);
    } else if (nameUpper=="BYLAYER") {
        dw.dxfString(3, "");
        dw.dxfInt(72, 65);
        dw.dxfInt(73, 0);
        dw.dxfReal(40, 0.0);
    } else if (nameUpper=="CONTINUOUS") {
        dw.dxfString(3, "Solid line");
        dw.dxfInt(72, 65);
        dw.dxfInt(73, 0);
        dw.dxfReal(40, 0.0);
    } else {
        dw.dxfString(3, data.description);
        dw.dxfInt(72, 65);
        dw.dxfInt(73, data.numberOfDashes);
        dw.dxfReal(40, data.patternLength);
        for (int i = 0; i < data.numberOfDashes; i++) {
            dw.dxfReal(49, data.pattern[i]);
            if (version>=DL_VERSION_R13) {
                dw.dxfInt(74, 0);
            }
        }
    }
}


void DL_Dxf::writeTrace(DL_WriterA& dw,
                        const DL_TraceData& data,
                        const DL_Attributes& attrib) {
    dw.entity("TRACE");
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbTrace");
    }
    dw.coord(10, data.x[0], data.y[0], data.z[0]);
    dw.coord(11, data.x[1], data.y[1], data.z[1]);
    dw.coord(12, data.x[2], data.y[2], data.z[2]);
    dw.coord(13, data.x[3], data.y[3], data.z[3]);
    dw.dxfReal(39, data.thickness);
}


bool DL_Dxf::in(std::istream& stream,
                DL_CreationInterface* creationInterface) {

    if (stream.good()) {
        firstCall=true;
        currentObjectType = DL_UNKNOWN;
        while (readDxfGroups(stream, creationInterface)) {}
        return true;
    }
    return false;
}


bool DL_Dxf::handleMTextData(DL_CreationInterface* creationInterface) {
    // Special handling of text chunks for MTEXT entities:
    if (groupCode==3) {
        creationInterface->addMTextChunk(groupValue);
        return true;
    }

    return false;
}


void DL_Dxf::writeLeaderVertex(DL_WriterA& dw,
                               const DL_LeaderVertexData& data) {
    if (version>DL_VERSION_R12) {
        dw.dxfReal(10, data.x);
        dw.dxfReal(20, data.y);
    }
}


bool DL_Dxf::readDxfGroups(FILE *fp, DL_CreationInterface* creationInterface) {

    static int line = 1;

    // Read one group of the DXF file and strip the lines:
    if (DL_Dxf::getStrippedLine(groupCodeTmp, DL_DXF_MAXLINE, fp) &&
            DL_Dxf::getStrippedLine(groupValue, DL_DXF_MAXLINE, fp, false) ) {

        groupCode = (unsigned int)toInt(groupCodeTmp);

        creationInterface->processCodeValuePair(groupCode, groupValue);
        line+=2;
        processDXFGroup(creationInterface, groupCode, groupValue);
    }

    return !feof(fp);
}


void DL_Dxf::writeDimStyle(DL_WriterA& dw,
                    double dimasz, double dimexe, double dimexo,
                    double dimgap, double dimtxt) {

    dw.dxfString(  0, "TABLE");
    dw.dxfString(  2, "DIMSTYLE");
    if (version==DL_VERSION_2000) {
        dw.dxfHex(5, 0xA);
        dw.dxfString(100, "AcDbSymbolTable");
    }
    dw.dxfInt( 70, 1);
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbDimStyleTable");
        dw.dxfInt( 71, 0);
    }

    dw.dxfString(  0, "DIMSTYLE");
    if (version==DL_VERSION_2000) {
        dw.dxfHex(105, 0x27);
    }
    //dw.handle(105);
    //dw.dxfHex(330, 0xA);
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbSymbolTableRecord");
        dw.dxfString(100, "AcDbDimStyleTableRecord");
    }
    dw.dxfString(  2, "Standard");
    if (version==DL_VERSION_R12) {
        dw.dxfString(  3, "");
        dw.dxfString(  4, "");
        dw.dxfString(  5, "");
        dw.dxfString(  6, "");
        dw.dxfString(  7, "");
        dw.dxfReal( 40, 1.0);
    }

    dw.dxfReal( 41, dimasz);
    dw.dxfReal( 42, dimexo);
    dw.dxfReal( 43, 3.75);
    dw.dxfReal( 44, dimexe);
    if (version==DL_VERSION_R12) {
        dw.dxfReal( 45, 0.0);
        dw.dxfReal( 46, 0.0);
        dw.dxfReal( 47, 0.0);
        dw.dxfReal( 48, 0.0);
    }
    dw.dxfInt( 70, 0);
    if (version==DL_VERSION_R12) {
        dw.dxfInt( 71, 0);
        dw.dxfInt( 72, 0);
    }
    dw.dxfInt( 73, 0);
    dw.dxfInt( 74, 0);
    if (version==DL_VERSION_R12) {
        dw.dxfInt( 75, 0);
        dw.dxfInt( 76, 0);
    }
    dw.dxfInt( 77, 1);
    dw.dxfInt( 78, 8);
    dw.dxfReal(140, dimtxt);
    dw.dxfReal(141, 2.5);
    if (version==DL_VERSION_R12) {
        dw.dxfReal(142, 0.0);
    }
    dw.dxfReal(143, 0.03937007874016);
    if (version==DL_VERSION_R12) {
        dw.dxfReal(144, 1.0);
        dw.dxfReal(145, 0.0);
        dw.dxfReal(146, 1.0);
    }
    dw.dxfReal(147, dimgap);
    if (version==DL_VERSION_R12) {
        dw.dxfInt(170, 0);
    }
    dw.dxfInt(171, 3);
    dw.dxfInt(172, 1);
    if (version==DL_VERSION_R12) {
        dw.dxfInt(173, 0);
        dw.dxfInt(174, 0);
        dw.dxfInt(175, 0);
        dw.dxfInt(176, 0);
        dw.dxfInt(177, 0);
        dw.dxfInt(178, 0);
    }
    if (version==DL_VERSION_2000) {
        dw.dxfInt(271, 2);
        dw.dxfInt(272, 2);
        dw.dxfInt(274, 3);
        dw.dxfInt(278, 44);
        dw.dxfInt(283, 0);
        dw.dxfInt(284, 8);
        dw.dxfHex(340, styleHandleStd);
        //dw.dxfHex(340, 0x11);
    }
    // * /
    dw.dxfString(  0, "ENDTAB");
}


void DL_Dxf::addSolid(DL_CreationInterface* creationInterface) {
    DL_SolidData sd;

    for (int k = 0; k < 4; k++) {
       sd.x[k] = getRealValue(10 + k, 0.0);
       sd.y[k] = getRealValue(20 + k, 0.0);
       sd.z[k] = getRealValue(30 + k, 0.0);
    }
    creationInterface->addSolid(sd);
}


bool DL_Dxf::handleDictionaryData(DL_CreationInterface* creationInterface) {
    if (groupCode==3) {
        return true;
    }

    if (groupCode==5) {
        creationInterface->addDictionary(DL_DictionaryData(groupValue));
        return true;
    }

    if (groupCode==350) {
        creationInterface->addDictionaryEntry(DL_DictionaryEntryData(getStringValue(3, ""), groupValue));
        return true;
    }
    return false;
}


void DL_Dxf::writeHeader(DL_WriterA& dw) {
    dw.comment("dxflib " DL_VERSION);
    dw.sectionHeader();

    dw.dxfString(9, "$ACADVER");
    switch (version) {
    case DL_Codes::AC1009:
        dw.dxfString(1, "AC1009");
        break;
    case DL_Codes::AC1012:
        dw.dxfString(1, "AC1012");
        break;
    case DL_Codes::AC1014:
        dw.dxfString(1, "AC1014");
        break;
    case DL_Codes::AC1015:
        dw.dxfString(1, "AC1015");
        break;
    case DL_Codes::AC1009_MIN:
        // minimalistic DXF version is unidentified in file:
        break;
    }

    // Newer version require that (otherwise a*cad crashes..)
    if (version==DL_VERSION_2000) {
        dw.dxfString(9, "$HANDSEED");
        dw.dxfHex(5, 0xFFFF);
    }

    // commented out: more variables can be added after that by caller:
    //dw.sectionEnd();
}


void DL_Dxf::writeBlockRecord(DL_WriterA& dw, const std::string& name) {
    dw.dxfString(  0, "BLOCK_RECORD");
    if (version==DL_VERSION_2000) {
        dw.handle();
    }
    //dw->dxfHex(330, 1);
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbSymbolTableRecord");
        dw.dxfString(100, "AcDbBlockTableRecord");
    }
    dw.dxfString(  2, name);
    dw.dxfHex(340, 0);
}


void DL_Dxf::addLayer(DL_CreationInterface* creationInterface) {
    // correct some invalid attributes for layers:
    attrib = creationInterface->getAttributes();
    if (attrib.getColor()==256 || attrib.getColor()==0) {
        attrib.setColor(7);
    }
    if (attrib.getWidth()<0) {
        attrib.setWidth(1);
    }

    std::string linetype = attrib.getLinetype();
    std::transform(linetype.begin(), linetype.end(), linetype.begin(), ::toupper);
    if (linetype=="BYLAYER" || linetype=="BYBLOCK") {
        attrib.setLinetype("CONTINUOUS");
    }

    // add layer
    std::string name = getStringValue(2, "");
    if (name.length()==0) {
        return;
    }

    creationInterface->addLayer(DL_LayerData(name, getIntValue(70, 0)));
}


void DL_Dxf::writeXRecord(DL_WriterA& dw, int handle, const std::string& value) {
    dw.dxfString(  0, "XRECORD");
    dw.dxfHex(5, handle);
    dw.dxfHex(330, appDictionaryHandle);
    dw.dxfString(100, "AcDbXrecord");
    dw.dxfInt(280, 1);
    dw.dxfString(1000, value);
}


void DL_Dxf::addXLine(DL_CreationInterface* creationInterface) {
    DL_XLineData d(getRealValue(10, 0.0),
                   getRealValue(20, 0.0),
                   getRealValue(30, 0.0),
                   getRealValue(11, 0.0),
                   getRealValue(21, 0.0),
                   getRealValue(31, 0.0));

    creationInterface->addXLine(d);
}


void DL_Dxf::writeBlock(DL_WriterA& dw, const DL_BlockData& data) {
    if (data.name.empty()) {
        std::cerr << "DL_Dxf::writeBlock: "
        << "Block name must not be empty\n";
        return;
    }

    std::string n = data.name;
    std::transform(n.begin(), n.end(), n.begin(), ::toupper);

    if (n=="*PAPER_SPACE") {
        dw.sectionBlockEntry(0x1C);
    } else if (n=="*MODEL_SPACE") {
        dw.sectionBlockEntry(0x20);
    } else if (n=="*PAPER_SPACE0") {
        dw.sectionBlockEntry(0x24);
    } else {
        dw.sectionBlockEntry();
    }
    dw.dxfString(2, data.name);
    dw.dxfInt(70, 0);
    dw.coord(10, data.bpx, data.bpy, data.bpz);
    dw.dxfString(3, data.name);
    dw.dxfString(1, "");
}


void DL_Dxf::addInsert(DL_CreationInterface* creationInterface) {
    //printf("addInsert\n");
    //printf("code 50: %s\n", values[50]);
    //printf("code 50 length: %d\n", strlen(values[50]));
    //printf("code 50:\n");
    //getRealValue(50, 0.0);

    std::string name = getStringValue(2, "");
    if (name.length()==0) {
        return;
    }

    DL_InsertData d(name,
                    // insertion point
                    getRealValue(10, 0.0),
                    getRealValue(20, 0.0),
                    getRealValue(30, 0.0),
                    // scale:
                    getRealValue(41, 1.0),
                    getRealValue(42, 1.0),
                    getRealValue(43, 1.0),
                    // angle (deg):
                    getRealValue(50, 0.0),
                    // cols / rows:
                    getIntValue(70, 1),
                    getIntValue(71, 1),
                    // spacing:
                    getRealValue(44, 0.0),
                    getRealValue(45, 0.0));

    creationInterface->addInsert(d);
}


void DL_Dxf::writeLayer(DL_WriterA& dw,
                        const DL_LayerData& data,
                        const DL_Attributes& attrib) {

    if (data.name.empty()) {
        std::cerr << "DL_Dxf::writeLayer: "
        << "Layer name must not be empty\n";
        return;
    }

    int color = attrib.getColor();
    if (color>=256) {
        std::cerr << "Layer color cannot be " << color << ". Changed to 7.\n";
        color = 7;
    }
    if (data.off) {
        // negative color value means layer is off:
        color = -color;
    }

    if (data.name == "0") {
        dw.tableLayerEntry(0x10);
    } else {
        dw.tableLayerEntry();
    }

    dw.dxfString(2, data.name);
    dw.dxfInt(70, data.flags);
    dw.dxfInt(62, color);
    if (version>=DL_VERSION_2000 && attrib.getColor24()!=-1) {
        dw.dxfInt(420, attrib.getColor24());
    }

    dw.dxfString(6, (attrib.getLinetype().length()==0 ?
                     std::string("CONTINUOUS") : attrib.getLinetype()));

    if (version>=DL_VERSION_2000) {
        // layer defpoints cannot be plotted
        std::string lstr = data.name;
        std::transform(lstr.begin(), lstr.end(), lstr.begin(), tolower);
        if (lstr=="defpoints") {
            dw.dxfInt(290, 0);
        }
    }
    if (version>=DL_VERSION_2000 && attrib.getWidth()!=-1) {
        dw.dxfInt(370, attrib.getWidth());
    }
    if (version>=DL_VERSION_2000) {
        dw.dxfHex(390, 0xF);
    }
}


void DL_Dxf::writeXRecord(DL_WriterA& dw, int handle, bool value) {
    dw.dxfString(  0, "XRECORD");
    dw.dxfHex(5, handle);
    dw.dxfHex(330, appDictionaryHandle);
    dw.dxfString(100, "AcDbXrecord");
    dw.dxfInt(280, 1);
    dw.dxfBool(290, value);
}


bool DL_Dxf::getStrippedLine(std::string &s, unsigned int size,
                            std::istream& stream, bool stripSpace) {

    if (!stream.eof()) {
        // Only the useful part of the line
        char* line = new char[size+1];
        char* oriLine = line;
        stream.getline(line, size);
        stripWhiteSpace(&line, stripSpace);
        s = line;
        assert(size > s.length());
        delete[] oriLine;
        return true;
    } else {
        s[0] = '\0';
        return false;
    }
}


void DL_Dxf::addSetting(DL_CreationInterface* creationInterface) {
    int c = -1;
    std::map<int,std::string>::iterator it = values.begin();
    if (it!=values.end()) {
        c = it->first;
    }
//    for (int i=0; i<=380; ++i) {
//        if (values[i][0]!='\0') {
//            c = i;
//            break;
//        }
//    }

    // string
    if (c>=0 && c<=9) {
        creationInterface->setVariableString(settingKey, values[c], c);
 #ifdef DL_COMPAT
        // backwards compatibility:
        creationInterface->setVariableString(settingKey.c_str(), values[c].c_str(), c);
 #endif
    }
    // vector
    else if (c>=10 && c<=39) {
        if (c==10) {
            creationInterface->setVariableVector(
                settingKey,
                getRealValue(c, 0.0),
                getRealValue(c+10, 0.0),
                getRealValue(c+20, 0.0),
                c);
        }
    }
    // double
    else if (c>=40 && c<=59) {
        creationInterface->setVariableDouble(settingKey, getRealValue(c, 0.0), c);
    }
    // int
    else if (c>=60 && c<=99) {
        creationInterface->setVariableInt(settingKey, getIntValue(c, 0), c);
    }
    // misc
    else if (c>=0) {
        creationInterface->setVariableString(settingKey, getStringValue(c, ""), c);
    }
}


bool DL_Dxf::stripWhiteSpace(char** s, bool stripSpace) {
    // last non-NULL char:
    int lastChar = strlen(*s) - 1;

    // Is last character CR or LF?
    while ( (lastChar >= 0) &&
            (((*s)[lastChar] == 10) || ((*s)[lastChar] == 13) ||
             (stripSpace && ((*s)[lastChar] == ' ' || ((*s)[lastChar] == '\t')))) ) {
        (*s)[lastChar] = '\0';
        lastChar--;
    }

    // Skip whitespace, excluding \n, at beginning of line
    if (stripSpace) {
        while ((*s)[0]==' ' || (*s)[0]=='\t') {
            ++(*s);
        }
    }

    return ((*s) ? true : false);
}


void DL_Dxf::writePolyline(DL_WriterA& dw,
                           const DL_PolylineData& data,
                           const DL_Attributes& attrib) {
    if (version==DL_VERSION_2000) {
        dw.entity("LWPOLYLINE");
        dw.dxfString(100, "AcDbEntity");
        dw.entityAttributes(attrib);
        dw.dxfString(100, "AcDbPolyline");
        dw.dxfInt(90, (int)data.number);
        dw.dxfInt(70, data.flags);
    } else {
        dw.entity("POLYLINE");
        dw.entityAttributes(attrib);
        polylineLayer = attrib.getLayer();
        dw.dxfInt(66, 1);
        dw.dxfInt(70, data.flags);
        dw.coord(DL_VERTEX_COORD_CODE, 0.0, 0.0, 0.0);
    }
}


void DL_Dxf::writeImageDef(DL_WriterA& dw,
                           int handle,
                           const DL_ImageData& data) {

    /*if (data.file.empty()) {
        std::cerr << "DL_Dxf::writeImage: "
        << "Image file must not be empty\n";
        return;
}*/

    dw.dxfString(0, "IMAGEDEF");
    if (version==DL_VERSION_2000) {
        dw.dxfHex(5, handle);
    }

    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbRasterImageDef");
        dw.dxfInt(90, 0);
    }
    // file name:
    dw.dxfString(1, data.ref);

    // image size in pixel
    dw.dxfReal(10, data.width);
    dw.dxfReal(20, data.height);

    dw.dxfReal(11, 1.0);
    dw.dxfReal(21, 1.0);

    // loaded:
    dw.dxfInt(280, 1);
    // units:
    dw.dxfInt(281, 0);
}


void DL_Dxf::writeHatchEdge(DL_WriterA& dw,
                            const DL_HatchEdgeData& data) {

    if (data.type<1 || data.type>4) {
        printf("WARNING: unsupported hatch edge type: %d", data.type);
    }

    dw.dxfInt(72, data.type);

    switch (data.type) {
    // line:
    case 1:
        dw.dxfReal(10, data.x1);
        dw.dxfReal(20, data.y1);
        dw.dxfReal(11, data.x2);
        dw.dxfReal(21, data.y2);
        break;

    // arc:
    case 2:
        dw.dxfReal(10, data.cx);
        dw.dxfReal(20, data.cy);
        dw.dxfReal(40, data.radius);
        dw.dxfReal(50, data.angle1/(2*M_PI)*360.0);
        dw.dxfReal(51, data.angle2/(2*M_PI)*360.0);
        dw.dxfInt(73, (int)(data.ccw));
        break;

    // ellipse arc:
    case 3:
        dw.dxfReal(10, data.cx);
        dw.dxfReal(20, data.cy);
        dw.dxfReal(11, data.mx);
        dw.dxfReal(21, data.my);
        dw.dxfReal(40, data.ratio);
        dw.dxfReal(50, data.angle1/(2*M_PI)*360.0);
        dw.dxfReal(51, data.angle2/(2*M_PI)*360.0);
        dw.dxfInt(73, (int)(data.ccw));
        break;

    // spline:
    case 4:
        dw.dxfInt(94, data.degree);
        dw.dxfBool(73, data.rational);
        dw.dxfBool(74, data.periodic);
        dw.dxfInt(95, data.nKnots);
        dw.dxfInt(96, data.nControl);
        for (unsigned int i=0; i<data.knots.size(); i++) {
            dw.dxfReal(40, data.knots[i]);
        }
        for (unsigned int i=0; i<data.controlPoints.size(); i++) {
            dw.dxfReal(10, data.controlPoints[i][0]);
            dw.dxfReal(20, data.controlPoints[i][1]);
        }
        for (unsigned int i=0; i<data.weights.size(); i++) {
            dw.dxfReal(42, data.weights[i]);
        }
        if (data.nFit>0) {
            dw.dxfInt(97, data.nFit);
            for (unsigned int i=0; i<data.fitPoints.size(); i++) {
                dw.dxfReal(11, data.fitPoints[i][0]);
                dw.dxfReal(21, data.fitPoints[i][1]);
            }
        }
        if (fabs(data.startTangentX)>1.0e-4 || fabs(data.startTangentY)>1.0e-4) {
            dw.dxfReal(12, data.startTangentX);
            dw.dxfReal(22, data.startTangentY);
        }
        if (fabs(data.endTangentX)>1.0e-4 || fabs(data.endTangentY)>1.0e-4) {
            dw.dxfReal(13, data.endTangentX);
            dw.dxfReal(23, data.endTangentY);
        }
        break;

    default:
        break;
    }
}


bool DL_Dxf::handleSplineData(DL_CreationInterface* /*creationInterface*/) {
    // Allocate Spline knots (group code 72):
    if (groupCode==72) {
        maxKnots = toInt(groupValue);
        if (maxKnots>0) {
            if (knots!=NULL) {
                delete[] knots;
            }
            knots = new double[maxKnots];
            for (int i=0; i<maxKnots; ++i) {
                knots[i] = 0.0;
            }
        }
        knotIndex=-1;
        return true;
    }

    // Allocate Spline control points / weights (group code 73):
    else if (groupCode==73) {
        maxControlPoints = toInt(groupValue);
        if (maxControlPoints>0) {
            if (controlPoints!=NULL) {
                delete[] controlPoints;
            }
            if (weights!=NULL) {
                delete[] weights;
            }
            controlPoints = new double[3*maxControlPoints];
            weights = new double[maxControlPoints];
            for (int i=0; i<maxControlPoints; ++i) {
                controlPoints[i*3] = 0.0;
                controlPoints[i*3+1] = 0.0;
                controlPoints[i*3+2] = 0.0;
                weights[i] = 1.0;
            }
        }
        controlPointIndex=-1;
        weightIndex=-1;
        return true;
    }

    // Allocate Spline fit points (group code 74):
    else if (groupCode==74) {
        maxFitPoints = toInt(groupValue);
        if (maxFitPoints>0) {
            if (fitPoints!=NULL) {
                delete[] fitPoints;
            }
            fitPoints = new double[3*maxFitPoints];
            for (int i=0; i<maxFitPoints; ++i) {
                fitPoints[i*3] = 0.0;
                fitPoints[i*3+1] = 0.0;
                fitPoints[i*3+2] = 0.0;
            }
        }
        fitPointIndex=-1;
        return true;
    }

    // Process spline knot vertices (group code 40):
    else if (groupCode==40) {
        if (knotIndex<maxKnots-1) {
            knotIndex++;
            knots[knotIndex] = toReal(groupValue);
        }
        return true;
    }

    // Process spline control points (group codes 10/20/30):
    else if (groupCode==10 || groupCode==20 ||
             groupCode==30) {

        if (controlPointIndex<maxControlPoints-1 && groupCode==10) {
            controlPointIndex++;
        }

        if (controlPointIndex>=0 && controlPointIndex<maxControlPoints) {
            controlPoints[3*controlPointIndex + (groupCode/10-1)] = toReal(groupValue);
        }
        return true;
    }

    // Process spline fit points (group codes 11/21/31):
    else if (groupCode==11 || groupCode==21 || groupCode==31) {
        if (fitPointIndex<maxFitPoints-1 && groupCode==11) {
            fitPointIndex++;
        }

        if (fitPointIndex>=0 && fitPointIndex<maxFitPoints) {
            fitPoints[3*fitPointIndex + ((groupCode-1)/10-1)] = toReal(groupValue);
        }
        return true;
    }

    // Process spline weights (group code 41)
    else if (groupCode==41) {

        if (weightIndex<maxControlPoints-1) {
            weightIndex++;
        }

        if (weightIndex>=0 && weightIndex<maxControlPoints) {
            weights[weightIndex] = toReal(groupValue);
        }
        return true;
    }
    return false;
}


void DL_Dxf::addHatch(DL_CreationInterface* creationInterface) {
    DL_HatchData hd(getIntValue(91, 1),
                    getIntValue(70, 0),
                    getRealValue(41, 1.0),
                    getRealValue(52, 0.0),
                    getStringValue(2, ""));

    creationInterface->addHatch(hd);

    for (unsigned int i=0; i<hatchEdges.size(); i++) {
        creationInterface->addHatchLoop(DL_HatchLoopData(hatchEdges[i].size()));
        for (unsigned int k=0; k<hatchEdges[i].size(); k++) {
            creationInterface->addHatchEdge(DL_HatchEdgeData(hatchEdges[i][k]));
        }
    }

    creationInterface->endEntity();
}


void DL_Dxf::write3dFace(DL_WriterA& dw,
                   const DL_3dFaceData& data,
                   const DL_Attributes& attrib) {
    dw.entity("3DFACE");
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbFace");
    }
    dw.coord(10, data.x[0], data.y[0], data.z[0]);
    dw.coord(11, data.x[1], data.y[1], data.z[1]);
    dw.coord(12, data.x[2], data.y[2], data.z[2]);
    dw.coord(13, data.x[3], data.y[3], data.z[3]);
}


void DL_Dxf::writeXLine(DL_WriterA& dw,
                       const DL_XLineData& data,
                       const DL_Attributes& attrib) {
    dw.entity("XLINE");
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbLine");
    }
    dw.coord(DL_LINE_START_CODE, data.bx, data.by, data.bz);
    dw.coord(DL_LINE_END_CODE, data.dx, data.dy, data.dz);
}


void DL_Dxf::writeKnot(DL_WriterA& dw,
                       const DL_KnotData& data) {

    dw.dxfReal(40, data.k);
}


void DL_Dxf::writeInsert(DL_WriterA& dw,
                         const DL_InsertData& data,
                         const DL_Attributes& attrib) {

    if (data.name.empty()) {
        std::cerr << "DL_Dxf::writeInsert: "
        << "Block name must not be empty\n";
        return;
    }

    dw.entity("INSERT");
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    if (version==DL_VERSION_2000) {
        if (data.cols!=1 || data.rows!=1) {
            dw.dxfString(100, "AcDbMInsertBlock");
        }
        else {
            dw.dxfString(100, "AcDbBlockReference");
        }
    }
    dw.dxfString(2, data.name);
    dw.dxfReal(10, data.ipx);
    dw.dxfReal(20, data.ipy);
    dw.dxfReal(30, data.ipz);
    if (data.sx!=1.0 || data.sy!=1.0) {
        dw.dxfReal(41, data.sx);
        dw.dxfReal(42, data.sy);
        dw.dxfReal(43, 1.0);
    }
    if (data.angle!=0.0) {
        dw.dxfReal(50, data.angle);
    }
    if (data.cols!=1 || data.rows!=1) {
        dw.dxfInt(70, data.cols);
        dw.dxfInt(71, data.rows);
    }
    if (data.colSp!=0.0 || data.rowSp!=0.0) {
        dw.dxfReal(44, data.colSp);
        dw.dxfReal(45, data.rowSp);
    }
}


void DL_Dxf::writeSolid(DL_WriterA& dw,
                   const DL_SolidData& data,
                   const DL_Attributes& attrib) {
    dw.entity("SOLID");
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbEntity");
    }
    dw.entityAttributes(attrib);
    if (version==DL_VERSION_2000) {
        dw.dxfString(100, "AcDbTrace");
    }
    dw.coord(10, data.x[0], data.y[0], data.z[0]);
    dw.coord(11, data.x[1], data.y[1], data.z[1]);
    dw.coord(12, data.x[2], data.y[2], data.z[2]);
    dw.coord(13, data.x[3], data.y[3], data.z[3]);
    dw.dxfReal(39, data.thickness);
}


DL_WriterA* DL_Dxf::out(const char* file, DL_Codes::version version) {
    char* f = new char[strlen(file)+1];
    strcpy(f, file);
    this->version = version;

    DL_WriterA* dw = new DL_WriterA(f, version);
    if (dw->openFailed()) {
        delete dw;
        delete[] f;
        return NULL;
    } else {
        delete[] f;
        return dw;
    }
}


void DL_Dxf::writeDimStyleOverrides(DL_WriterA& dw,
                             const DL_DimensionData& data) {

    if (version==DL_VERSION_2000) {
        dw.dxfString(1001, "ACAD");
        dw.dxfString(1000, "DSTYLE");
        dw.dxfString(1002, "{");
        if (data.type&128) {
            // custom text position:
            dw.dxfInt(1070, 279);
            dw.dxfInt(1070, 2);
        }
        dw.dxfInt(1070, 144);
        dw.dxfReal(1040, data.linearFactor);
        dw.dxfInt(1070,40);
        dw.dxfReal(1040, data.dimScale);
        dw.dxfString(1002, "}");
    }
}


unsigned long DL_Dxf::writeDictionaryEntry(DL_WriterA& dw, const std::string& name) {
    dw.dxfString(  3, name);
    return dw.handle(350);
}


void DL_Dxf::addDimAngular3P(DL_CreationInterface* creationInterface) {
    DL_DimensionData d = getDimData();

    // angular dimension (3P):
    DL_DimAngular3PData da(
        // definition point 1
        getRealValue(13, 0.0),
        getRealValue(23, 0.0),
        getRealValue(33, 0.0),
        // definition point 2
        getRealValue(14, 0.0),
        getRealValue(24, 0.0),
        getRealValue(34, 0.0),
        // definition point 3
        getRealValue(15, 0.0),
        getRealValue(25, 0.0),
        getRealValue(35, 0.0));
    creationInterface->addDimAngular3P(d, da);
}