static apr_status_t wsgi_python_parent_cleanup(void *data)
#endif
{
    if (wsgi_parent_pid == getpid()) {
        /*
         * Destroy Python itself including the main
         * interpreter. If mod_python is being loaded it
         * is left to mod_python to destroy Python,
         * although it currently doesn't do so.
         */

        if (wsgi_python_initialized)
            wsgi_python_term();
    }

#if AP_SERVER_MAJORVERSION_NUMBER >= 2
    return APR_SUCCESS;
#endif
}


static int wsgi_execute_script(request_rec *r)
{
    WSGIRequestConfig *config = NULL;

    InterpreterObject *interp = NULL;
    PyObject *modules = NULL;
    PyObject *module = NULL;
    const char *script = NULL;
    const char *name = NULL;
    int exists = 0;

    int status;

    /* Grab request configuration. */

    config = (WSGIRequestConfig *)ap_get_module_config(r->request_config,
                                                       &wsgi_module);

    /*
     * Acquire the desired python interpreter. Once this is done
     * it is safe to start manipulating python objects.
     */

    interp = wsgi_acquire_interpreter(config->application_group);

    if (!interp) {
        ap_log_rerror(APLOG_MARK, WSGI_LOG_CRIT(0), r,
                      "mod_wsgi (pid=%d): Cannot acquire interpreter '%s'.",
                      getpid(), config->application_group);

        return HTTP_INTERNAL_SERVER_ERROR;
    }

    /* Calculate the Python module name to be used for script. */

    if (config->handler_script && *config->handler_script)
        script = config->handler_script;
    else
        script = r->filename;

    name = wsgi_module_name(r->pool, script);

    /*
     * Use a lock around the check to see if the module is
     * already loaded and the import of the module to prevent
     * two request handlers trying to import the module at the
     * same time.
     */

#if APR_HAS_THREADS
    Py_BEGIN_ALLOW_THREADS
    apr_thread_mutex_lock(wsgi_module_lock);
    Py_END_ALLOW_THREADS
#endif

    modules = PyImport_GetModuleDict();
    module = PyDict_GetItemString(modules, name);

    Py_XINCREF(module);

    if (module)
        exists = 1;

    /*
     * If script reloading is enabled and the module for it has
     * previously been loaded, see if it has been modified since
     * the last time it was accessed. For a handler script will
     * also see if it contains a custom function for determining
     * if a reload should be performed.
     */

    if (module && config->script_reloading) {
        if (wsgi_reload_required(r->pool, r, script, module, r->filename)) {
            /*
             * Script file has changed. Discard reference to
             * loaded module and work out what action we are
             * supposed to take. Choices are process reloading
             * and module reloading. Process reloading cannot be
             * performed unless a daemon process is being used.
             */

            Py_DECREF(module);
            module = NULL;

#if defined(MOD_WSGI_WITH_DAEMONS)
            if (*config->process_group) {
                /*
                 * Need to restart the daemon process. We bail
                 * out on the request process here, sending back
                 * a special response header indicating that
                 * process is being restarted and that remote
                 * end should abandon connection and attempt to
                 * reconnect again. We also need to signal this
                 * process so it will actually shutdown. The
                 * process supervisor code will ensure that it
                 * is restarted.
                 */

                Py_BEGIN_ALLOW_THREADS
                ap_log_rerror(APLOG_MARK, WSGI_LOG_INFO(0), r,
                             "mod_wsgi (pid=%d): Force restart of "
                             "process '%s'.", getpid(),
                             config->process_group);
                Py_END_ALLOW_THREADS

#if APR_HAS_THREADS
                apr_thread_mutex_unlock(wsgi_module_lock);
#endif

                wsgi_release_interpreter(interp);

                r->status = HTTP_INTERNAL_SERVER_ERROR;
                r->status_line = "200 Rejected";

                wsgi_daemon_shutdown++;
                kill(getpid(), SIGINT);

                return OK;
            }
            else {
                /*
                 * Need to reload just the script module. Remove
                 * the module from the modules dictionary before
                 * reloading it again. If code is executing
                 * within the module at the time, the callers
                 * reference count on the module should ensure
                 * it isn't actually destroyed until it is
                 * finished.
                 */

                PyDict_DelItemString(modules, name);
            }
#else
            /*
             * Need to reload just the script module. Remove
             * the module from the modules dictionary before
             * reloading it again. If code is executing
             * within the module at the time, the callers
             * reference count on the module should ensure
             * it isn't actually destroyed until it is
             * finished.
             */

            PyDict_DelItemString(modules, name);
#endif
        }
    }

    /*
     * When process reloading is in use need to indicate
     * that request content should now be sent through.
     * This is done by writing a special response header
     * directly out onto the appropriate network output
     * filter. The special response is picked up by
     * remote end and data will then be sent.
     */

#if defined(MOD_WSGI_WITH_DAEMONS)
    if (*config->process_group) {
        ap_filter_t *filters;
        apr_bucket_brigade *bb;
        apr_bucket *b;

        const char *data = "Status: 200 Continue\r\n\r\n";
        int length = strlen(data);

        filters = r->output_filters;
        while (filters && filters->frec->ftype != AP_FTYPE_NETWORK) {
            filters = filters->next;
        }

        bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);

        b = apr_bucket_transient_create(data, length,
                                        r->connection->bucket_alloc);
        APR_BRIGADE_INSERT_TAIL(bb, b);

        b = apr_bucket_flush_create(r->connection->bucket_alloc);
        APR_BRIGADE_INSERT_TAIL(bb, b);

        /*
         * This should always work, so ignore any errors
         * from passing the brigade to the network
         * output filter. If there are are problems they
         * will be picked up further down in processing
         * anyway.
         */

        ap_pass_brigade(filters, bb);
    }
#endif

    /* Load module if not already loaded. */

    if (!module) {
        module = wsgi_load_source(r->pool, r, name, exists, script,
                                  config->process_group,
                                  config->application_group);
    }

    /* Safe now to release the module lock. */

#if APR_HAS_THREADS
    apr_thread_mutex_unlock(wsgi_module_lock);
#endif

    /* Assume an internal server error unless everything okay. */

    status = HTTP_INTERNAL_SERVER_ERROR;

    /* Determine if script exists and execute it. */

    if (module) {
        PyObject *module_dict = NULL;
        PyObject *object = NULL;

        module_dict = PyModule_GetDict(module);
        object = PyDict_GetItemString(module_dict, config->callable_object);

        if (object) {
            AdapterObject *adapter = NULL;
            adapter = newAdapterObject(r);

            if (adapter) {
                PyObject *method = NULL;
                PyObject *args = NULL;

                Py_INCREF(object);
                status = Adapter_run(adapter, object);
                Py_DECREF(object);

                /*
                 * Wipe out references to Apache request object
                 * held by Python objects, so can detect when an
                 * application holds on to the transient Python
                 * objects beyond the life of the request and
                 * thus raise an exception if they are used.
                 */

                adapter->r = NULL;
                adapter->input->r = NULL;

                /* Close the log object so data is flushed. */

                method = PyObject_GetAttrString(adapter->log, "close");

                if (!method) {
                    PyErr_Format(PyExc_AttributeError,
                                 "'%s' object has no attribute 'close'",
                                 adapter->log->ob_type->tp_name);
                }
                else {
                    args = PyTuple_New(0);
                    object = PyEval_CallObject(method, args);
                    Py_DECREF(args);
                }

                Py_XDECREF(object);
                Py_XDECREF(method);

#if defined(MOD_WSGI_WITH_BUCKETS)
                adapter->bb = NULL;
#endif
            }

            Py_XDECREF((PyObject *)adapter);
        }
        else {
            Py_BEGIN_ALLOW_THREADS
            ap_log_rerror(APLOG_MARK, WSGI_LOG_ERR(0), r,
                          "mod_wsgi (pid=%d): Target WSGI script '%s' does "
                          "not contain WSGI application '%s'.",
                          getpid(), script, config->callable_object);
            Py_END_ALLOW_THREADS

            status = HTTP_NOT_FOUND;
        }
    }

    /* Log any details of exceptions if execution failed. */

    if (PyErr_Occurred())
        wsgi_log_python_error(r, NULL, r->filename);

    /* Cleanup and release interpreter, */

    Py_XDECREF(module);

    wsgi_release_interpreter(interp);

    return status;
}


static int Adapter_process_file_wrapper(AdapterObject *self)
{
    int done = 0;

#ifndef WIN32
#if AP_SERVER_MAJORVERSION_NUMBER >= 2

    PyObject *filelike = NULL;
    PyObject *method = NULL;
    PyObject *object = NULL;

    apr_status_t rv = 0;

    apr_os_file_t fd = -1;
    apr_file_t *tmpfile = NULL;
    apr_finfo_t finfo;

    apr_off_t fd_offset = 0;
    apr_off_t fo_offset = 0;

    apr_off_t length = 0;

    /* Perform file wrapper optimisations where possible. */

    if (self->sequence->ob_type != &Stream_Type)
        return 0;

    /*
     * Only attempt to perform optimisations if the
     * write() function returned by start_response()
     * function has not been called with non zero length
     * data. In other words if no prior response content
     * generated. Technically it could be done, but want
     * to have a consistent rule about how specifying a
     * content length affects how much of a file is
     * sent. Don't want to have to take into
     * consideration whether write() function has been
     * called or not as just complicates things.
     */

    if (self->output_length != 0)
        return 0;

    /*
     * Work out if file wrapper is associated with a
     * file like object, where that file object is
     * associated with a regular file. If it does then
     * we can optimise how the contents of the file are
     * sent out. If no such associated file descriptor
     * then it needs to be processed like any other
     * iterable value.
     */

    filelike = ((StreamObject *)self->sequence)->filelike;

    fd = PyObject_AsFileDescriptor(filelike);
    if (fd == -1) {
        PyErr_Clear();
        return 0;
    }

    /*
     * On some platforms, such as Linux, sendfile() system call
     * will not work on UNIX sockets. Thus when using daemon mode
     * cannot enable that feature.
     */

    if (self->config->enable_sendfile)
        apr_os_file_put(&tmpfile, &fd, APR_SENDFILE_ENABLED, self->r->pool);
    else
        apr_os_file_put(&tmpfile, &fd, 0, self->r->pool);

    rv = apr_file_info_get(&finfo, APR_FINFO_SIZE|APR_FINFO_TYPE, tmpfile);
    if (rv != APR_SUCCESS || finfo.filetype != APR_REG)
        return 0;

    /*
     * Because Python file like objects potentially have
     * their own buffering layering, or use an operating
     * system FILE object which also has a buffering
     * layer on top of a normal file descriptor, need to
     * determine from the file like object its position
     * within the file and use that as starting position.
     * Note that it is assumed that user had flushed any
     * modifications to the file as necessary. Also, we
     * need to make sure we remember the original file
     * descriptor position as will need to restore that
     * position so it matches the upper buffering layers
     * when done. This is done to avoid any potential
     * problems if file like object does anything strange
     * in its close() method which relies on file position
     * being what it thought it should be.
     */

    rv = apr_file_seek(tmpfile, APR_CUR, &fd_offset);
    if (rv != APR_SUCCESS)
        return 0;

    method = PyObject_GetAttrString(filelike, "tell");
    if (!method)
        return 0;

    object = PyEval_CallObject(method, NULL);
    Py_DECREF(method);

    if (!object) {
        PyErr_Clear();
        return 0;
    }

   if (PyLong_Check(object)) {
#if defined(HAVE_LONG_LONG)
       fo_offset = PyLong_AsLongLong(object);
#else
       fo_offset = PyLong_AsLong(object);
#endif
   }
#if PY_MAJOR_VERSION < 3
   else if (PyInt_Check(object)) {
       fo_offset = PyInt_AsLong(object);
   }
#endif
   else {
       Py_DECREF(object);
       return 0;
   }

   if (PyErr_Occurred()){
       Py_DECREF(object);
       PyErr_Clear();
       return 0;
   }

    Py_DECREF(object);

    /*
     * For a file wrapper object need to always ensure
     * that response headers are parsed. This is done so
     * that if the content length header has been
     * defined we can get its value and use it to limit
     * how much of a file is being sent. The WSGI 1.0
     * specification says that we are meant to send all
     * available bytes from the file, however this is
     * questionable as sending more than content length
     * would violate HTTP RFC. Note that this doesn't
     * actually flush the headers out when using Apache
     * 2.X. This is good, as we want to still be able to
     * set the content length header if none set and file
     * is seekable. If processing response headers fails,
     * then need to return as if done, with error being
     * logged later.
     */

    if (!Adapter_output(self, "", 0, 0))
        return 1;

    /*
     * If content length wasn't defined then determine
     * the amount of data which is available to send and
     * set the content length response header. Either
     * way, if can work out length then send data
     * otherwise fall through and treat it as normal
     * iterable.
     */

    if (!self->content_length_set) {
        length = finfo.size - fo_offset;
        self->output_length += length;

        ap_set_content_length(self->r, length);

        self->content_length_set = 1;
        self->content_length = length;

        if (Adapter_output_file(self, tmpfile, fo_offset, length))
            self->result = OK;

        done = 1;
    }
    else {
        length = finfo.size - fo_offset;
        self->output_length += length;

        /* Use user specified content length instead. */

        length = self->content_length;

        if (Adapter_output_file(self, tmpfile, fo_offset, length))
            self->result = OK;

        done = 1;
    }

    /*
     * Restore position of underlying file descriptor.
     * If this fails, then not much we can do about it.
     */

    apr_file_seek(tmpfile, APR_SET, &fd_offset);

#endif
#endif

    return done;
}


static const char *wsgi_set_python_path(cmd_parms *cmd, void *mconfig,
                                        const char *f)
{
    const char *error = NULL;
    WSGIServerConfig *sconfig = NULL;

    error = ap_check_cmd_context(cmd, GLOBAL_ONLY);
    if (error != NULL)
        return error;

    sconfig = ap_get_module_config(cmd->server->module_config, &wsgi_module);
    sconfig->python_path = f;

    return NULL;
}


static void wsgi_hook_init(server_rec *s, apr_pool_t *p)
{
    char package[128];

    /* Setup module version information. */

    sprintf(package, "mod_wsgi/%s", MOD_WSGI_VERSION_STRING);

    ap_add_version_component(package);

    /* Record Python version string with Apache. */

    if (!Py_IsInitialized()) {
        char buffer[256];
        const char *token = NULL;
        const char *version = NULL;

        version = Py_GetVersion();

        token = version;
        while (*token && *token != ' ')
            token++;

        strcpy(buffer, "Python/");
        strncat(buffer, version, token - version);

        ap_add_version_component(buffer);
    }

    /* Retain reference to base server. */

    wsgi_server = s;

    /* Retain record of parent process ID. */

    wsgi_parent_pid = getpid();

    /* Determine whether multiprocess and/or multithreaded. */

    wsgi_multiprocess = 1;
    wsgi_multithread = 0;

    /* Retain reference to main server config. */

    wsgi_server_config = ap_get_module_config(s->module_config, &wsgi_module);

    /*
     * Check that the version of Python found at
     * runtime is what was used at compilation.
     */

    wsgi_python_version();

    /*
     * Initialise Python if required to be done in
     * the parent process. Note that it will not be
     * initialised if mod_python loaded and it has
     * already been done.
     */

    if (!wsgi_python_after_fork)
        wsgi_python_init(p);
}


static void dns_tcp_call_process_done(struct tevent_req *subreq)
{
	struct dns_tcp_call *call = tevent_req_callback_data(subreq,
			struct dns_tcp_call);
	struct dns_tcp_connection *dns_conn = call->dns_conn;
	WERROR err;

	err = dns_process_recv(subreq, call, &call->out);
	TALLOC_FREE(subreq);
	if (!W_ERROR_IS_OK(err)) {
		DEBUG(1, ("dns_process returned %s\n", win_errstr(err)));
		dns_tcp_terminate_connection(dns_conn,
				"dns_tcp_call_loop: process function failed");
		return;
	}

	/* First add the length of the out buffer */
	RSSVAL(call->out_hdr, 0, call->out.length);
	call->out_iov[0].iov_base = (char *) call->out_hdr;
	call->out_iov[0].iov_len = 2;

	call->out_iov[1].iov_base = (char *) call->out.data;
	call->out_iov[1].iov_len = call->out.length;

	subreq = tstream_writev_queue_send(call,
					   dns_conn->conn->event.ctx,
					   dns_conn->tstream,
					   dns_conn->send_queue,
					   call->out_iov, 2);
	if (subreq == NULL) {
		dns_tcp_terminate_connection(dns_conn, "dns_tcp_call_loop: "
				"no memory for tstream_writev_queue_send");
		return;
	}
	tevent_req_set_callback(subreq, dns_tcp_call_writev_done, call);
}


static int vfswrap_link(vfs_handle_struct *handle, const char *oldpath, const char *newpath)
{
	int result;

	START_PROFILE(syscall_link);
	result = link(oldpath, newpath);
	END_PROFILE(syscall_link);
	return result;
}


static int vfswrap_fsync(vfs_handle_struct *handle, files_struct *fsp)
{
#ifdef HAVE_FSYNC
	int result;

	START_PROFILE(syscall_fsync);
	result = fsync(fsp->fh->fd);
	END_PROFILE(syscall_fsync);
	return result;
#else
	return 0;
#endif
}


static int vfswrap_chmod(vfs_handle_struct *handle, const char *path, mode_t mode)
{
	int result;

	START_PROFILE(syscall_chmod);

	/*
	 * We need to do this due to the fact that the default POSIX ACL
	 * chmod modifies the ACL *mask* for the group owner, not the
	 * group owner bits directly. JRA.
	 */

	{
		int saved_errno = errno; /* We might get ENOSYS */
		if ((result = SMB_VFS_CHMOD_ACL(handle->conn, path, mode)) == 0) {
			END_PROFILE(syscall_chmod);
			return result;
		}
		/* Error - return the old errno. */
		errno = saved_errno;
	}

	result = chmod(path, mode);
	END_PROFILE(syscall_chmod);
	return result;
}


static NTSTATUS vfswrap_audit_file(struct vfs_handle_struct *handle,
				   struct smb_filename *file,
				   struct security_acl *sacl,
				   uint32_t access_requested,
				   uint32_t access_denied)
{
	return NT_STATUS_OK; /* Nothing to do here ... */
}


static int vfswrap_set_offline(struct vfs_handle_struct *handle,
			       const struct smb_filename *fname)
{
	/* We don't know how to set offline bit by default, needs to be overriden in the vfs modules */
#if defined(ENOTSUP)
	errno = ENOTSUP;
#endif
	return -1;
}


static off_t vfswrap_lseek(vfs_handle_struct *handle, files_struct *fsp, off_t offset, int whence)
{
	off_t result = 0;

	START_PROFILE(syscall_lseek);

	/* Cope with 'stat' file opens. */
	if (fsp->fh->fd != -1)
		result = lseek(fsp->fh->fd, offset, whence);

	/*
	 * We want to maintain the fiction that we can seek
	 * on a fifo for file system purposes. This allows
	 * people to set up UNIX fifo's that feed data to Windows
	 * applications. JRA.
	 */

	if((result == -1) && (errno == ESPIPE)) {
		result = 0;
		errno = 0;
	}

	END_PROFILE(syscall_lseek);
	return result;
}


static int vfswrap_get_quota(struct vfs_handle_struct *handle, enum SMB_QUOTA_TYPE qtype, unid_t id, SMB_DISK_QUOTA *qt)
{
#ifdef HAVE_SYS_QUOTAS
	int result;

	START_PROFILE(syscall_get_quota);
	result = sys_get_quota(handle->conn->connectpath, qtype, id, qt);
	END_PROFILE(syscall_get_quota);
	return result;
#else
	errno = ENOSYS;
	return -1;
#endif
}


static struct dirent *vfswrap_readdir(vfs_handle_struct *handle,
				          DIR *dirp,
					  SMB_STRUCT_STAT *sbuf)
{
	struct dirent *result;

	START_PROFILE(syscall_readdir);
	result = readdir(dirp);
	END_PROFILE(syscall_readdir);
	if (sbuf) {
		/* Default Posix readdir() does not give us stat info.
		 * Set to invalid to indicate we didn't return this info. */
		SET_STAT_INVALID(*sbuf);
#if defined(HAVE_DIRFD) && defined(HAVE_FSTATAT)
		if (result != NULL) {
			/* See if we can efficiently return this. */
			struct stat st;
			int flags = (lp_posix_pathnames() ?
				AT_SYMLINK_NOFOLLOW : 0);
			int ret = fstatat(dirfd(dirp),
					result->d_name,
					&st,
					flags);
			if (ret == 0) {
				init_stat_ex_from_stat(sbuf,
					&st,
					lp_fake_dir_create_times(
						SNUM(handle->conn)));
			}
		}
#endif
	}
	return result;
}


NTSTATUS vfs_default_init(void)
{
	return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
				DEFAULT_VFS_MODULE_NAME, &vfs_default_fns);
}


static bool vfswrap_brl_unlock_windows(struct vfs_handle_struct *handle,
				       struct messaging_context *msg_ctx,
				       struct byte_range_lock *br_lck,
			               const struct lock_struct *plock)
{
	SMB_ASSERT(plock->lock_flav == WINDOWS_LOCK);

	return brl_unlock_windows_default(msg_ctx, br_lck, plock);
}


static NTSTATUS vfswrap_streaminfo(vfs_handle_struct *handle,
				   struct files_struct *fsp,
				   const char *fname,
				   TALLOC_CTX *mem_ctx,
				   unsigned int *pnum_streams,
				   struct stream_struct **pstreams)
{
	SMB_STRUCT_STAT sbuf;
	struct stream_struct *tmp_streams = NULL;
	int ret;

	if ((fsp != NULL) && (fsp->is_directory)) {
		/*
		 * No default streams on directories
		 */
		goto done;
	}

	if ((fsp != NULL) && (fsp->fh->fd != -1)) {
		ret = SMB_VFS_FSTAT(fsp, &sbuf);
	}
	else {
		struct smb_filename smb_fname;

		ZERO_STRUCT(smb_fname);
		smb_fname.base_name = discard_const_p(char, fname);

		if (lp_posix_pathnames()) {
			ret = SMB_VFS_LSTAT(handle->conn, &smb_fname);
		} else {
			ret = SMB_VFS_STAT(handle->conn, &smb_fname);
		}
		sbuf = smb_fname.st;
	}

	if (ret == -1) {
		return map_nt_error_from_unix(errno);
	}

	if (S_ISDIR(sbuf.st_ex_mode)) {
		goto done;
	}

	tmp_streams = talloc_realloc(mem_ctx, *pstreams, struct stream_struct,
					(*pnum_streams) + 1);
	if (tmp_streams == NULL) {
		return NT_STATUS_NO_MEMORY;
	}
	tmp_streams[*pnum_streams].name = talloc_strdup(tmp_streams, "::$DATA");
	if (tmp_streams[*pnum_streams].name == NULL) {
		return NT_STATUS_NO_MEMORY;
	}
	tmp_streams[*pnum_streams].size = sbuf.st_ex_size;
	tmp_streams[*pnum_streams].alloc_size = SMB_VFS_GET_ALLOC_SIZE(handle->conn, fsp, &sbuf);

	*pnum_streams += 1;
	*pstreams = tmp_streams;
 done:
	return NT_STATUS_OK;
}


static NTSTATUS vfswrap_durable_disconnect(struct vfs_handle_struct *handle,
					   struct files_struct *fsp,
					   const DATA_BLOB old_cookie,
					   TALLOC_CTX *mem_ctx,
					   DATA_BLOB *new_cookie)
{
	return vfs_default_durable_disconnect(fsp, old_cookie, mem_ctx,
					      new_cookie);
}


static NTSTATUS vfswrap_get_dfs_referrals(struct vfs_handle_struct *handle,
					  struct dfs_GetDFSReferral *r)
{
	struct junction_map *junction = NULL;
	int consumedcnt = 0;
	bool self_referral = false;
	char *pathnamep = NULL;
	char *local_dfs_path = NULL;
	NTSTATUS status;
	int i;
	uint16_t max_referral_level = r->in.req.max_referral_level;

	if (DEBUGLVL(10)) {
		NDR_PRINT_IN_DEBUG(dfs_GetDFSReferral, r);
	}

	/* get the junction entry */
	if (r->in.req.servername == NULL) {
		return NT_STATUS_NOT_FOUND;
	}

	/*
	 * Trim pathname sent by client so it begins with only one backslash.
	 * Two backslashes confuse some dfs clients
	 */

	local_dfs_path = talloc_strdup(r, r->in.req.servername);
	if (local_dfs_path == NULL) {
		return NT_STATUS_NO_MEMORY;
	}
	pathnamep = local_dfs_path;
	while (IS_DIRECTORY_SEP(pathnamep[0]) &&
	       IS_DIRECTORY_SEP(pathnamep[1])) {
		pathnamep++;
	}

	junction = talloc_zero(r, struct junction_map);
	if (junction == NULL) {
		return NT_STATUS_NO_MEMORY;
	}

	/* The following call can change cwd. */
	status = get_referred_path(r, pathnamep,
				   !handle->conn->sconn->using_smb2,
				   junction, &consumedcnt, &self_referral);
	if (!NT_STATUS_IS_OK(status)) {
		vfs_ChDir(handle->conn, handle->conn->connectpath);
		return status;
	}
	vfs_ChDir(handle->conn, handle->conn->connectpath);

	if (!self_referral) {
		pathnamep[consumedcnt] = '\0';

		if (DEBUGLVL(3)) {
			dbgtext("setup_dfs_referral: Path %s to "
				"alternate path(s):",
				pathnamep);
			for (i=0; i < junction->referral_count; i++) {
				dbgtext(" %s",
				junction->referral_list[i].alternate_path);
			}
			dbgtext(".\n");
		}
	}

	if (r->in.req.max_referral_level <= 2) {
		max_referral_level = 2;
	}
	if (r->in.req.max_referral_level >= 3) {
		max_referral_level = 3;
	}

	r->out.resp = talloc_zero(r, struct dfs_referral_resp);
	if (r->out.resp == NULL) {
		return NT_STATUS_NO_MEMORY;
	}

	r->out.resp->path_consumed = strlen_m(pathnamep) * 2;
	r->out.resp->nb_referrals = junction->referral_count;

	r->out.resp->header_flags = DFS_HEADER_FLAG_STORAGE_SVR;
	if (self_referral) {
		r->out.resp->header_flags |= DFS_HEADER_FLAG_REFERAL_SVR;
	}

	r->out.resp->referral_entries = talloc_zero_array(r,
				struct dfs_referral_type,
				r->out.resp->nb_referrals);
	if (r->out.resp->referral_entries == NULL) {
		return NT_STATUS_NO_MEMORY;
	}

	switch (max_referral_level) {
	case 2:
		for(i=0; i < junction->referral_count; i++) {
			struct referral *ref = &junction->referral_list[i];
			TALLOC_CTX *mem_ctx = r->out.resp->referral_entries;
			struct dfs_referral_type *t =
				&r->out.resp->referral_entries[i];
			struct dfs_referral_v2 *v2 = &t->referral.v2;

			t->version = 2;
			v2->size = VERSION2_REFERRAL_SIZE;
			if (self_referral) {
				v2->server_type = DFS_SERVER_ROOT;
			} else {
				v2->server_type = DFS_SERVER_NON_ROOT;
			}
			v2->entry_flags = 0;
			v2->proximity = ref->proximity;
			v2->ttl = ref->ttl;
			v2->DFS_path = talloc_strdup(mem_ctx, pathnamep);
			if (v2->DFS_path == NULL) {
				return NT_STATUS_NO_MEMORY;
			}
			v2->DFS_alt_path = talloc_strdup(mem_ctx, pathnamep);
			if (v2->DFS_alt_path == NULL) {
				return NT_STATUS_NO_MEMORY;
			}
			v2->netw_address = talloc_strdup(mem_ctx,
							 ref->alternate_path);
			if (v2->netw_address == NULL) {
				return NT_STATUS_NO_MEMORY;
			}
		}

		break;
	case 3:
		for(i=0; i < junction->referral_count; i++) {
			struct referral *ref = &junction->referral_list[i];
			TALLOC_CTX *mem_ctx = r->out.resp->referral_entries;
			struct dfs_referral_type *t =
				&r->out.resp->referral_entries[i];
			struct dfs_referral_v3 *v3 = &t->referral.v3;
			struct dfs_normal_referral *r1 = &v3->referrals.r1;

			t->version = 3;
			v3->size = VERSION3_REFERRAL_SIZE;
			if (self_referral) {
				v3->server_type = DFS_SERVER_ROOT;
			} else {
				v3->server_type = DFS_SERVER_NON_ROOT;
			}
			v3->entry_flags = 0;
			v3->ttl = ref->ttl;
			r1->DFS_path = talloc_strdup(mem_ctx, pathnamep);
			if (r1->DFS_path == NULL) {
				return NT_STATUS_NO_MEMORY;
			}
			r1->DFS_alt_path = talloc_strdup(mem_ctx, pathnamep);
			if (r1->DFS_alt_path == NULL) {
				return NT_STATUS_NO_MEMORY;
			}
			r1->netw_address = talloc_strdup(mem_ctx,
							 ref->alternate_path);
			if (r1->netw_address == NULL) {
				return NT_STATUS_NO_MEMORY;
			}
		}
		break;
	default:
		DEBUG(0,("setup_dfs_referral: Invalid dfs referral "
			"version: %d\n",
			max_referral_level));
		return NT_STATUS_INVALID_LEVEL;
	}

	if (DEBUGLVL(10)) {
		NDR_PRINT_OUT_DEBUG(dfs_GetDFSReferral, r);
	}

	return NT_STATUS_OK;
}


static bool vfswrap_lock(vfs_handle_struct *handle, files_struct *fsp, int op, off_t offset, off_t count, int type)
{
	bool result;

	START_PROFILE(syscall_fcntl_lock);
	result =  fcntl_lock(fsp->fh->fd, op, offset, count, type);
	END_PROFILE(syscall_fcntl_lock);
	return result;
}


static NTSTATUS vfswrap_fsctl(struct vfs_handle_struct *handle,
			      struct files_struct *fsp,
			      TALLOC_CTX *ctx,
			      uint32_t function,
			      uint16_t req_flags, /* Needed for UNICODE ... */
			      const uint8_t *_in_data,
			      uint32_t in_len,
			      uint8_t **_out_data,
			      uint32_t max_out_len,
			      uint32_t *out_len)
{
	const char *in_data = (const char *)_in_data;
	char **out_data = (char **)_out_data;

	switch (function) {
	case FSCTL_SET_SPARSE:
	{
		bool set_sparse = true;
		NTSTATUS status;

		if (in_len >= 1 && in_data[0] == 0) {
			set_sparse = false;
		}

		status = file_set_sparse(handle->conn, fsp, set_sparse);

		DEBUG(NT_STATUS_IS_OK(status) ? 10 : 9,
		      ("FSCTL_SET_SPARSE: fname[%s] set[%u] - %s\n",
		       smb_fname_str_dbg(fsp->fsp_name), set_sparse,
		       nt_errstr(status)));

		return status;
	}

	case FSCTL_CREATE_OR_GET_OBJECT_ID:
	{
		unsigned char objid[16];
		char *return_data = NULL;

		/* This should return the object-id on this file.
		 * I think I'll make this be the inode+dev. JRA.
		 */

		DEBUG(10,("FSCTL_CREATE_OR_GET_OBJECT_ID: called on %s\n",
			  fsp_fnum_dbg(fsp)));

		*out_len = (max_out_len >= 64) ? 64 : max_out_len;
		/* Hmmm, will this cause problems if less data asked for? */
		return_data = talloc_array(ctx, char, 64);
		if (return_data == NULL) {
			return NT_STATUS_NO_MEMORY;
		}

		/* For backwards compatibility only store the dev/inode. */
		push_file_id_16(return_data, &fsp->file_id);
		memcpy(return_data+16,create_volume_objectid(fsp->conn,objid),16);
		push_file_id_16(return_data+32, &fsp->file_id);
		*out_data = return_data;
		return NT_STATUS_OK;
	}

	case FSCTL_GET_REPARSE_POINT:
	{
		/* Fail it with STATUS_NOT_A_REPARSE_POINT */
		DEBUG(10, ("FSCTL_GET_REPARSE_POINT: called on %s. "
			   "Status: NOT_IMPLEMENTED\n", fsp_fnum_dbg(fsp)));
		return NT_STATUS_NOT_A_REPARSE_POINT;
	}

	case FSCTL_SET_REPARSE_POINT:
	{
		/* Fail it with STATUS_NOT_A_REPARSE_POINT */
		DEBUG(10, ("FSCTL_SET_REPARSE_POINT: called on %s. "
			   "Status: NOT_IMPLEMENTED\n", fsp_fnum_dbg(fsp)));
		return NT_STATUS_NOT_A_REPARSE_POINT;
	}

	case FSCTL_GET_SHADOW_COPY_DATA:
	{
		/*
		 * This is called to retrieve the number of Shadow Copies (a.k.a. snapshots)
		 * and return their volume names.  If max_data_count is 16, then it is just
		 * asking for the number of volumes and length of the combined names.
		 *
		 * pdata is the data allocated by our caller, but that uses
		 * total_data_count (which is 0 in our case) rather than max_data_count.
		 * Allocate the correct amount and return the pointer to let
		 * it be deallocated when we return.
		 */
		struct shadow_copy_data *shadow_data = NULL;
		bool labels = False;
		uint32 labels_data_count = 0;
		uint32 i;
		char *cur_pdata = NULL;

		if (max_out_len < 16) {
			DEBUG(0,("FSCTL_GET_SHADOW_COPY_DATA: max_data_count(%u) < 16 is invalid!\n",
				max_out_len));
			return NT_STATUS_INVALID_PARAMETER;
		}

		if (max_out_len > 16) {
			labels = True;
		}

		shadow_data = talloc_zero(ctx, struct shadow_copy_data);
		if (shadow_data == NULL) {
			DEBUG(0,("TALLOC_ZERO() failed!\n"));
			return NT_STATUS_NO_MEMORY;
		}

		/*
		 * Call the VFS routine to actually do the work.
		 */
		if (SMB_VFS_GET_SHADOW_COPY_DATA(fsp, shadow_data, labels)!=0) {
			TALLOC_FREE(shadow_data);
			if (errno == ENOSYS) {
				DEBUG(5,("FSCTL_GET_SHADOW_COPY_DATA: connectpath %s, not supported.\n",
					fsp->conn->connectpath));
				return NT_STATUS_NOT_SUPPORTED;
			} else {
				DEBUG(0,("FSCTL_GET_SHADOW_COPY_DATA: connectpath %s, failed.\n",
					fsp->conn->connectpath));
				return NT_STATUS_UNSUCCESSFUL;
			}
		}

		labels_data_count = (shadow_data->num_volumes * 2 *
					sizeof(SHADOW_COPY_LABEL)) + 2;

		if (!labels) {
			*out_len = 16;
		} else {
			*out_len = 12 + labels_data_count;
		}

		if (max_out_len < *out_len) {
			DEBUG(0,("FSCTL_GET_SHADOW_COPY_DATA: max_data_count(%u) too small (%u) bytes needed!\n",
				max_out_len, *out_len));
			TALLOC_FREE(shadow_data);
			return NT_STATUS_BUFFER_TOO_SMALL;
		}

		cur_pdata = talloc_zero_array(ctx, char, *out_len);
		if (cur_pdata == NULL) {
			TALLOC_FREE(shadow_data);
			return NT_STATUS_NO_MEMORY;
		}

		*out_data = cur_pdata;

		/* num_volumes 4 bytes */
		SIVAL(cur_pdata, 0, shadow_data->num_volumes);

		if (labels) {
			/* num_labels 4 bytes */
			SIVAL(cur_pdata, 4, shadow_data->num_volumes);
		}

		/* needed_data_count 4 bytes */
		SIVAL(cur_pdata, 8, labels_data_count);

		cur_pdata += 12;

		DEBUG(10,("FSCTL_GET_SHADOW_COPY_DATA: %u volumes for path[%s].\n",
			  shadow_data->num_volumes, fsp_str_dbg(fsp)));
		if (labels && shadow_data->labels) {
			for (i=0; i<shadow_data->num_volumes; i++) {
				srvstr_push(cur_pdata, req_flags,
					    cur_pdata, shadow_data->labels[i],
					    2 * sizeof(SHADOW_COPY_LABEL),
					    STR_UNICODE|STR_TERMINATE);
				cur_pdata += 2 * sizeof(SHADOW_COPY_LABEL);
				DEBUGADD(10,("Label[%u]: '%s'\n",i,shadow_data->labels[i]));
			}
		}

		TALLOC_FREE(shadow_data);

		return NT_STATUS_OK;
	}

	case FSCTL_FIND_FILES_BY_SID:
	{
		/* pretend this succeeded -
		 *
		 * we have to send back a list with all files owned by this SID
		 *
		 * but I have to check that --metze
		 */
		struct dom_sid sid;
		uid_t uid;
		size_t sid_len;

		DEBUG(10, ("FSCTL_FIND_FILES_BY_SID: called on %s\n",
			   fsp_fnum_dbg(fsp)));

		if (in_len < 8) {
			/* NT_STATUS_BUFFER_TOO_SMALL maybe? */
			return NT_STATUS_INVALID_PARAMETER;
		}

		sid_len = MIN(in_len - 4,SID_MAX_SIZE);

		/* unknown 4 bytes: this is not the length of the sid :-(  */
		/*unknown = IVAL(pdata,0);*/

		if (!sid_parse(in_data + 4, sid_len, &sid)) {
			return NT_STATUS_INVALID_PARAMETER;
		}
		DEBUGADD(10, ("for SID: %s\n", sid_string_dbg(&sid)));

		if (!sid_to_uid(&sid, &uid)) {
			DEBUG(0,("sid_to_uid: failed, sid[%s] sid_len[%lu]\n",
				 sid_string_dbg(&sid),
				 (unsigned long)sid_len));
			uid = (-1);
		}

		/* we can take a look at the find source :-)
		 *
		 * find ./ -uid $uid  -name '*'   is what we need here
		 *
		 *
		 * and send 4bytes len and then NULL terminated unicode strings
		 * for each file
		 *
		 * but I don't know how to deal with the paged results
		 * (maybe we can hang the result anywhere in the fsp struct)
		 *
		 * but I don't know how to deal with the paged results
		 * (maybe we can hang the result anywhere in the fsp struct)
		 *
		 * we don't send all files at once
		 * and at the next we should *not* start from the beginning,
		 * so we have to cache the result
		 *
		 * --metze
		 */

		/* this works for now... */
		return NT_STATUS_OK;
	}

	case FSCTL_QUERY_ALLOCATED_RANGES:
	{
		/* FIXME: This is just a dummy reply, telling that all of the
		 * file is allocated. MKS cp needs that.
		 * Adding the real allocated ranges via FIEMAP on Linux
		 * and SEEK_DATA/SEEK_HOLE on Solaris is needed to make
		 * this FSCTL correct for sparse files.
		 */
		NTSTATUS status;
		uint64_t offset, length;
		char *out_data_tmp = NULL;

		if (in_len != 16) {
			DEBUG(0,("FSCTL_QUERY_ALLOCATED_RANGES: data_count(%u) != 16 is invalid!\n",
				in_len));
			return NT_STATUS_INVALID_PARAMETER;
		}

		if (max_out_len < 16) {
			DEBUG(0,("FSCTL_QUERY_ALLOCATED_RANGES: max_out_len (%u) < 16 is invalid!\n",
				max_out_len));
			return NT_STATUS_INVALID_PARAMETER;
		}

		offset = BVAL(in_data,0);
		length = BVAL(in_data,8);

		if (offset + length < offset) {
			/* No 64-bit integer wrap. */
			return NT_STATUS_INVALID_PARAMETER;
		}

		/* Shouldn't this be SMB_VFS_STAT ... ? */
		status = vfs_stat_fsp(fsp);
		if (!NT_STATUS_IS_OK(status)) {
			return status;
		}

		*out_len = 16;
		out_data_tmp = talloc_array(ctx, char, *out_len);
		if (out_data_tmp == NULL) {
			DEBUG(10, ("unable to allocate memory for response\n"));
			return NT_STATUS_NO_MEMORY;
		}

		if (offset > fsp->fsp_name->st.st_ex_size ||
				fsp->fsp_name->st.st_ex_size == 0 ||
				length == 0) {
			memset(out_data_tmp, 0, *out_len);
		} else {
			uint64_t end = offset + length;
			end = MIN(end, fsp->fsp_name->st.st_ex_size);
			SBVAL(out_data_tmp, 0, 0);
			SBVAL(out_data_tmp, 8, end);
		}

		*out_data = out_data_tmp;

		return NT_STATUS_OK;
	}

	case FSCTL_IS_VOLUME_DIRTY:
	{
		DEBUG(10,("FSCTL_IS_VOLUME_DIRTY: called on %s "
			  "(but remotely not supported)\n", fsp_fnum_dbg(fsp)));
		/*
		 * http://msdn.microsoft.com/en-us/library/cc232128%28PROT.10%29.aspx
		 * says we have to respond with NT_STATUS_INVALID_PARAMETER
		 */
		return NT_STATUS_INVALID_PARAMETER;
	}

	default:
		/*
		 * Only print once ... unfortunately there could be lots of
		 * different FSCTLs that are called.
		 */
		if (!vfswrap_logged_ioctl_message) {
			vfswrap_logged_ioctl_message = true;
			DEBUG(2, ("%s (0x%x): Currently not implemented.\n",
			__func__, function));
		}
	}

	return NT_STATUS_NOT_SUPPORTED;
}


_asn1_get_indefinite_length_string (const unsigned char *der, int *len)
{
  int len2, len3, counter, indefinite;
  unsigned long tag;
  unsigned char class;

  counter = indefinite = 0;

  while (1)
    {
      if (counter+1 >= *len)
	return ASN1_DER_ERROR;

      if ((der[counter] == 0) && (der[counter + 1] == 0))
	{
	  counter += 2;
	  indefinite--;
	  if (indefinite <= 0)
	    break;
	  else
	    continue;
	}

      if (asn1_get_tag_der
	  (der + counter, *len - counter, &class, &len2,
	   &tag) != ASN1_SUCCESS)
	return ASN1_DER_ERROR;
      if (counter + len2 > *len)
	return ASN1_DER_ERROR;
      counter += len2;
      len2 = asn1_get_length_der (der + counter, *len - counter, &len3);
      if (len2 < -1)
	return ASN1_DER_ERROR;
      if (len2 == -1)
	{
	  indefinite++;
	  counter += 1;
	}
      else
	{
	  counter += len2 + len3;
	}
    }

  *len = counter;
  return ASN1_SUCCESS;

}


asn1_der_decoding_startEnd (asn1_node element, const void *ider, int ider_len,
			    const char *name_element, int *start, int *end)
{
  asn1_node node, node_to_find, p, p2;
  int counter, len2, len3, len4, move, ris;
  unsigned char class;
  unsigned long tag;
  int indefinite, result = ASN1_DER_ERROR;
  const unsigned char *der = ider;

  node = element;

  if (node == NULL)
    return ASN1_ELEMENT_NOT_FOUND;

  node_to_find = asn1_find_node (node, name_element);

  if (node_to_find == NULL)
    return ASN1_ELEMENT_NOT_FOUND;

  if (node_to_find == node)
    {
      *start = 0;
      *end = ider_len - 1;
      return ASN1_SUCCESS;
    }

  if (node->type & CONST_OPTION)
    return ASN1_GENERIC_ERROR;

  counter = 0;
  move = DOWN;
  p = node;
  while (1)
    {
      if (p == NULL)
	return ASN1_DER_ERROR;

      ris = ASN1_SUCCESS;

      if (move != UP)
	{
	  if (p->type & CONST_SET)
	    {
	      p2 = _asn1_find_up (p);
	      if (p2 == NULL)
	        {
		  warn();
		  return ASN1_DER_ERROR;
		}

	      len2 = _asn1_strtol (p2->value, NULL, 10);
	      if (len2 == -1)
		{
		  if (HAVE_TWO(ider_len) && !der[counter] && !der[counter + 1])
		    {
		      p = p2;
		      move = UP;
		      counter += 2;
		      DECR_LEN(ider_len, 2);
		      continue;
		    }
		}
	      else if (counter == len2)
		{
		  p = p2;
		  move = UP;
		  continue;
		}
	      else if (counter > len2)
	        {
		  warn();
		  return ASN1_DER_ERROR;
		}

	      p2 = p2->down;

	      while (p2)
		{
		  if ((p2->type & CONST_SET) && (p2->type & CONST_NOT_USED))
		    {		/* CONTROLLARE */
		      ris =
			  extract_tag_der_recursive (p2, der + counter,
						 ider_len, &len2);
		      if (ris == ASN1_SUCCESS)
			{
			  p2->type &= ~CONST_NOT_USED;
			  p = p2;
			  break;
			}
		    }
		  p2 = p2->right;
		}
	      if (p2 == NULL)
	        {
		  warn();
		  return ASN1_DER_ERROR;
		}
	    }

	  if (p == node_to_find)
	    *start = counter;

	  if (type_field (p->type) == ASN1_ETYPE_CHOICE)
	    {
	      p = p->down;
	      if (p == NULL)
	        {
		  warn();
		  return ASN1_DER_ERROR;
		}

	      ris =
		_asn1_extract_tag_der (p, der + counter, ider_len,
				       &len2);
	      if (p == node_to_find)
		*start = counter;
	    }

	  if (ris == ASN1_SUCCESS)
	    ris =
	      _asn1_extract_tag_der (p, der + counter, ider_len, &len2);
	  if (ris != ASN1_SUCCESS)
	    {
	      if (p->type & CONST_OPTION)
		{
		  p->type |= CONST_NOT_USED;
		  move = RIGHT;
		}
	      else if (p->type & CONST_DEFAULT)
		{
		  move = RIGHT;
		}
	      else
		{
		  warn();
		  return ASN1_TAG_ERROR;
		}
	    }
	  else
	    {
	      DECR_LEN(ider_len, len2);
	      counter += len2;
	    }
	}

      if (ris == ASN1_SUCCESS)
	{
	  switch (type_field (p->type))
	    {
	    case ASN1_ETYPE_NULL:
	       DECR_LEN(ider_len, 1);

	      if (der[counter])
	        {
		  warn();
		  return ASN1_DER_ERROR;
		}
	      counter++;
	      move = RIGHT;
	      break;
	    case ASN1_ETYPE_BOOLEAN:
              DECR_LEN(ider_len, 2);

	      if (der[counter] != 1)
	        {
		  warn();
		  return ASN1_DER_ERROR;
		}

	      counter += 2;
	      move = RIGHT;
	      break;
	    case ASN1_ETYPE_OCTET_STRING:
	      ris = _asn1_get_octet_string (NULL, der + counter, ider_len, &len3);
	      if (ris != ASN1_SUCCESS)
	        {
		  warn();
		  return ris;
		}
              DECR_LEN(ider_len, len3);
	      counter += len3;
	      move = RIGHT;
	      break;
	    case ASN1_ETYPE_UTC_TIME:
	    case ASN1_ETYPE_GENERALIZED_TIME:
	    case ASN1_ETYPE_OBJECT_ID:
	    case ASN1_ETYPE_INTEGER:
	    case ASN1_ETYPE_ENUMERATED:
	    case ASN1_ETYPE_GENERALSTRING:
	    case ASN1_ETYPE_NUMERIC_STRING:
	    case ASN1_ETYPE_IA5_STRING:
	    case ASN1_ETYPE_TELETEX_STRING:
	    case ASN1_ETYPE_PRINTABLE_STRING:
	    case ASN1_ETYPE_UNIVERSAL_STRING:
	    case ASN1_ETYPE_BMP_STRING:
	    case ASN1_ETYPE_UTF8_STRING:
	    case ASN1_ETYPE_VISIBLE_STRING:
	    case ASN1_ETYPE_BIT_STRING:
	      len2 =
		asn1_get_length_der (der + counter, ider_len, &len3);
	      if (len2 < 0)
	        {
		  warn();
		  return ASN1_DER_ERROR;
		}

              DECR_LEN(ider_len, len3 + len2);
	      counter += len3 + len2;
	      move = RIGHT;
	      break;
	    case ASN1_ETYPE_SEQUENCE:
	    case ASN1_ETYPE_SET:
	      if (move != UP)
		{
		  len3 =
		    asn1_get_length_der (der + counter, ider_len, &len2);
		  if (len3 < -1)
		    {
  		      warn();
		      return ASN1_DER_ERROR;
		    }

                  DECR_LEN(ider_len, len2);
		  counter += len2;

		  if (len3 == 0)
		    move = RIGHT;
		  else
		    move = DOWN;
		}
	      else
		{
		  if (HAVE_TWO(ider_len) && !der[counter] && !der[counter + 1])	/* indefinite length method */
		    {
		      counter += 2;
		      DECR_LEN(ider_len, 2);
		    }
		  move = RIGHT;
		}
	      break;
	    case ASN1_ETYPE_SEQUENCE_OF:
	    case ASN1_ETYPE_SET_OF:
	      if (move != UP)
		{
		  len3 =
		    asn1_get_length_der (der + counter, ider_len, &len2);
		  if (len3 < -1)
		    {
  		      warn();
		      return ASN1_DER_ERROR;
		    }

                  DECR_LEN(ider_len, len2);
		  counter += len2;

		  if (len3 == -1)
		    {
		       if (HAVE_TWO(ider_len) && !der[counter] && !der[counter + 1])
		         {
  		           DECR_LEN(ider_len, 2);
		           counter += 2;
		         }
		    }

		  if (len3)
		    {
		      p2 = p->down;
		      while ((type_field (p2->type) == ASN1_ETYPE_TAG) ||
			     (type_field (p2->type) == ASN1_ETYPE_SIZE))
			p2 = p2->right;
		      p = p2;
		    }
		}
	      else
		{
		  if (HAVE_TWO(ider_len) && !der[counter] && !der[counter + 1])	/* indefinite length method */
		    {
		      DECR_LEN(ider_len, 2);
		      counter += 2;
		    }
		}
	      move = RIGHT;
	      break;
	    case ASN1_ETYPE_ANY:
	      if (asn1_get_tag_der
		  (der + counter, ider_len, &class, &len2,
		   &tag) != ASN1_SUCCESS)
		 {
  		    warn();
		    return ASN1_DER_ERROR;
		 }

	      DECR_LEN(ider_len, len2);

	      len4 =
		asn1_get_length_der (der + counter + len2,
				     ider_len, &len3);
	      if (len4 < -1)
	        {
 	          warn();
		  return ASN1_DER_ERROR;
		}

	      if (len4 != -1)
		{
		  DECR_LEN(ider_len, len3 + len4);
		  counter += len2 + len3 + len4;
		}
	      else
		{		/* indefinite length */
		  /* Check indefinite lenth method in an EXPLICIT TAG */
		  ider_len += len2; /* undo DECR_LEN */

		  if (counter == 0)
		    {
		      result = ASN1_DER_ERROR;
                      warn();
		      goto cleanup;
		    }

		  if ((p->type & CONST_TAG) && (der[counter - 1] == 0x80))
		    indefinite = 1;
		  else
		    indefinite = 0;

		  ris =
		    _asn1_get_indefinite_length_string (der + counter, ider_len, &len2);
		  if (ris != ASN1_SUCCESS)
		    {
 	              warn();
		      return ris;
		    }
		  counter += len2;
		  DECR_LEN(ider_len, len2);

		  /* Check if a couple of 0x00 are present due to an EXPLICIT TAG with
		     an indefinite length method. */
		  if (indefinite)
		    {
		      DECR_LEN(ider_len, 2);

		      if (!der[counter] && !der[counter + 1])
			counter += 2;
		      else
		        {
 	                  warn();
			  return ASN1_DER_ERROR;
			}
		    }
		}
	      move = RIGHT;
	      break;
	    default:
	      move = (move == UP) ? RIGHT : DOWN;
	      break;
	    }
	}

      if ((p == node_to_find) && (move == RIGHT))
	{
	  *end = counter - 1;
	  return ASN1_SUCCESS;
	}

      if (p == node && move != DOWN)
	break;

      if (move == DOWN)
	{
	  if (p->down)
	    p = p->down;
	  else
	    move = RIGHT;
	}
      if ((move == RIGHT) && !(p->type & CONST_SET))
	{
	  if (p->right)
	    p = p->right;
	  else
	    move = UP;
	}
      if (move == UP)
	p = _asn1_find_up (p);
    }

  warn();
  return ASN1_ELEMENT_NOT_FOUND;

cleanup:
  return result;
}


_asn1_get_time_der (const unsigned char *der, int der_len, int *ret_len,
		    char *str, int str_size)
{
  int len_len, str_len;

  if (der_len <= 0 || str == NULL)
    return ASN1_DER_ERROR;

  str_len = asn1_get_length_der (der, der_len, &len_len);
  if (str_len <= 0 || str_size < str_len)
    return ASN1_DER_ERROR;

  memcpy (str, der + len_len, str_len);
  str[str_len] = 0;
  *ret_len = str_len + len_len;

  return ASN1_SUCCESS;
}


asn1_get_octet_der (const unsigned char *der, int der_len,
		    int *ret_len, unsigned char *str, int str_size,
		    int *str_len)
{
  int len_len = 0;

  if (der_len <= 0)
    return ASN1_GENERIC_ERROR;

  /* if(str==NULL) return ASN1_SUCCESS; */
  *str_len = asn1_get_length_der (der, der_len, &len_len);

  if (*str_len < 0)
    return ASN1_DER_ERROR;

  *ret_len = *str_len + len_len;
  if (str_size >= *str_len)
    memcpy (str, der + len_len, *str_len);
  else
    {
      return ASN1_MEM_ERROR;
    }

  return ASN1_SUCCESS;
}


asn1_write_value (asn1_node node_root, const char *name,
		  const void *ivalue, int len)
{
  asn1_node node, p, p2;
  unsigned char *temp, *value_temp = NULL, *default_temp = NULL;
  int len2, k, k2, negative;
  size_t i;
  const unsigned char *value = ivalue;
  unsigned int type;

  node = asn1_find_node (node_root, name);
  if (node == NULL)
    return ASN1_ELEMENT_NOT_FOUND;

  if ((node->type & CONST_OPTION) && (value == NULL) && (len == 0))
    {
      asn1_delete_structure (&node);
      return ASN1_SUCCESS;
    }

  type = type_field (node->type);

  if ((type == ASN1_ETYPE_SEQUENCE_OF || type == ASN1_ETYPE_SET_OF) && (value == NULL) && (len == 0))
    {
      p = node->down;
      while ((type_field (p->type) == ASN1_ETYPE_TAG)
	     || (type_field (p->type) == ASN1_ETYPE_SIZE))
	p = p->right;

      while (p->right)
	asn1_delete_structure (&p->right);

      return ASN1_SUCCESS;
    }

  /* Don't allow element deletion for other types */
  if (value == NULL)
    {
      return ASN1_VALUE_NOT_VALID;
    }

  switch (type)
    {
    case ASN1_ETYPE_BOOLEAN:
      if (!_asn1_strcmp (value, "TRUE"))
	{
	  if (node->type & CONST_DEFAULT)
	    {
	      p = node->down;
	      while (type_field (p->type) != ASN1_ETYPE_DEFAULT)
		p = p->right;
	      if (p->type & CONST_TRUE)
		_asn1_set_value (node, NULL, 0);
	      else
		_asn1_set_value (node, "T", 1);
	    }
	  else
	    _asn1_set_value (node, "T", 1);
	}
      else if (!_asn1_strcmp (value, "FALSE"))
	{
	  if (node->type & CONST_DEFAULT)
	    {
	      p = node->down;
	      while (type_field (p->type) != ASN1_ETYPE_DEFAULT)
		p = p->right;
	      if (p->type & CONST_FALSE)
		_asn1_set_value (node, NULL, 0);
	      else
		_asn1_set_value (node, "F", 1);
	    }
	  else
	    _asn1_set_value (node, "F", 1);
	}
      else
	return ASN1_VALUE_NOT_VALID;
      break;
    case ASN1_ETYPE_INTEGER:
    case ASN1_ETYPE_ENUMERATED:
      if (len == 0)
	{
	  if ((isdigit (value[0])) || (value[0] == '-'))
	    {
	      value_temp = malloc (SIZEOF_UNSIGNED_LONG_INT);
	      if (value_temp == NULL)
		return ASN1_MEM_ALLOC_ERROR;

	      _asn1_convert_integer (value, value_temp,
				     SIZEOF_UNSIGNED_LONG_INT, &len);
	    }
	  else
	    {			/* is an identifier like v1 */
	      if (!(node->type & CONST_LIST))
		return ASN1_VALUE_NOT_VALID;
	      p = node->down;
	      while (p)
		{
		  if (type_field (p->type) == ASN1_ETYPE_CONSTANT)
		    {
		      if (!_asn1_strcmp (p->name, value))
			{
			  value_temp = malloc (SIZEOF_UNSIGNED_LONG_INT);
			  if (value_temp == NULL)
			    return ASN1_MEM_ALLOC_ERROR;

			  _asn1_convert_integer (p->value,
						 value_temp,
						 SIZEOF_UNSIGNED_LONG_INT,
						 &len);
			  break;
			}
		    }
		  p = p->right;
		}
	      if (p == NULL)
		return ASN1_VALUE_NOT_VALID;
	    }
	}
      else
	{			/* len != 0 */
	  value_temp = malloc (len);
	  if (value_temp == NULL)
	    return ASN1_MEM_ALLOC_ERROR;
	  memcpy (value_temp, value, len);
	}

      if (value_temp[0] & 0x80)
	negative = 1;
      else
	negative = 0;

      if (negative && (type_field (node->type) == ASN1_ETYPE_ENUMERATED))
	{
	  free (value_temp);
	  return ASN1_VALUE_NOT_VALID;
	}

      for (k = 0; k < len - 1; k++)
	if (negative && (value_temp[k] != 0xFF))
	  break;
	else if (!negative && value_temp[k])
	  break;

      if ((negative && !(value_temp[k] & 0x80)) ||
	  (!negative && (value_temp[k] & 0x80)))
	k--;

      _asn1_set_value_lv (node, value_temp + k, len - k);

      if (node->type & CONST_DEFAULT)
	{
	  p = node->down;
	  while (type_field (p->type) != ASN1_ETYPE_DEFAULT)
	    p = p->right;
	  if ((isdigit (p->value[0])) || (p->value[0] == '-'))
	    {
	      default_temp = malloc (SIZEOF_UNSIGNED_LONG_INT);
	      if (default_temp == NULL)
		{
		  free (value_temp);
		  return ASN1_MEM_ALLOC_ERROR;
		}

	      _asn1_convert_integer (p->value, default_temp,
				     SIZEOF_UNSIGNED_LONG_INT, &len2);
	    }
	  else
	    {			/* is an identifier like v1 */
	      if (!(node->type & CONST_LIST))
		{
		  free (value_temp);
		  return ASN1_VALUE_NOT_VALID;
		}
	      p2 = node->down;
	      while (p2)
		{
		  if (type_field (p2->type) == ASN1_ETYPE_CONSTANT)
		    {
		      if (!_asn1_strcmp (p2->name, p->value))
			{
			  default_temp = malloc (SIZEOF_UNSIGNED_LONG_INT);
			  if (default_temp == NULL)
			    {
			      free (value_temp);
			      return ASN1_MEM_ALLOC_ERROR;
			    }

			  _asn1_convert_integer (p2->value,
						 default_temp,
						 SIZEOF_UNSIGNED_LONG_INT,
						 &len2);
			  break;
			}
		    }
		  p2 = p2->right;
		}
	      if (p2 == NULL)
		{
		  free (value_temp);
		  return ASN1_VALUE_NOT_VALID;
		}
	    }

	  if ((len - k) == len2)
	    {
	      for (k2 = 0; k2 < len2; k2++)
		if (value_temp[k + k2] != default_temp[k2])
		  {
		    break;
		  }
	      if (k2 == len2)
		_asn1_set_value (node, NULL, 0);
	    }
	  free (default_temp);
	}
      free (value_temp);
      break;
    case ASN1_ETYPE_OBJECT_ID:
      for (i = 0; i < _asn1_strlen (value); i++)
	if ((!isdigit (value[i])) && (value[i] != '.') && (value[i] != '+'))
	  return ASN1_VALUE_NOT_VALID;
      if (node->type & CONST_DEFAULT)
	{
	  p = node->down;
	  while (type_field (p->type) != ASN1_ETYPE_DEFAULT)
	    p = p->right;
	  if (!_asn1_strcmp (value, p->value))
	    {
	      _asn1_set_value (node, NULL, 0);
	      break;
	    }
	}
      _asn1_set_value (node, value, _asn1_strlen (value) + 1);
      break;
    case ASN1_ETYPE_UTC_TIME:
      {
	len = _asn1_strlen (value);
	if (len < 11)
	  return ASN1_VALUE_NOT_VALID;
	for (k = 0; k < 10; k++)
	  if (!isdigit (value[k]))
	    return ASN1_VALUE_NOT_VALID;
	switch (len)
	  {
	  case 11:
	    if (value[10] != 'Z')
	      return ASN1_VALUE_NOT_VALID;
	    break;
	  case 13:
	    if ((!isdigit (value[10])) || (!isdigit (value[11])) ||
		(value[12] != 'Z'))
	      return ASN1_VALUE_NOT_VALID;
	    break;
	  case 15:
	    if ((value[10] != '+') && (value[10] != '-'))
	      return ASN1_VALUE_NOT_VALID;
	    for (k = 11; k < 15; k++)
	      if (!isdigit (value[k]))
		return ASN1_VALUE_NOT_VALID;
	    break;
	  case 17:
	    if ((!isdigit (value[10])) || (!isdigit (value[11])))
	      return ASN1_VALUE_NOT_VALID;
	    if ((value[12] != '+') && (value[12] != '-'))
	      return ASN1_VALUE_NOT_VALID;
	    for (k = 13; k < 17; k++)
	      if (!isdigit (value[k]))
		return ASN1_VALUE_NOT_VALID;
	    break;
	  default:
	    return ASN1_VALUE_NOT_FOUND;
	  }
	_asn1_set_value (node, value, len);
      }
      break;
    case ASN1_ETYPE_GENERALIZED_TIME:
      len = _asn1_strlen (value);
      _asn1_set_value (node, value, len);
      break;
    case ASN1_ETYPE_OCTET_STRING:
    case ASN1_ETYPE_GENERALSTRING:
    case ASN1_ETYPE_NUMERIC_STRING:
    case ASN1_ETYPE_IA5_STRING:
    case ASN1_ETYPE_TELETEX_STRING:
    case ASN1_ETYPE_PRINTABLE_STRING:
    case ASN1_ETYPE_UNIVERSAL_STRING:
    case ASN1_ETYPE_BMP_STRING:
    case ASN1_ETYPE_UTF8_STRING:
    case ASN1_ETYPE_VISIBLE_STRING:
      if (len == 0)
	len = _asn1_strlen (value);
      _asn1_set_value_lv (node, value, len);
      break;
    case ASN1_ETYPE_BIT_STRING:
      if (len == 0)
	len = _asn1_strlen (value);
      asn1_length_der ((len >> 3) + 2, NULL, &len2);
      temp = malloc ((len >> 3) + 2 + len2);
      if (temp == NULL)
	return ASN1_MEM_ALLOC_ERROR;

      asn1_bit_der (value, len, temp, &len2);
      _asn1_set_value_m (node, temp, len2);
      temp = NULL;
      break;
    case ASN1_ETYPE_CHOICE:
      p = node->down;
      while (p)
	{
	  if (!_asn1_strcmp (p->name, value))
	    {
	      p2 = node->down;
	      while (p2)
		{
		  if (p2 != p)
		    {
		      asn1_delete_structure (&p2);
		      p2 = node->down;
		    }
		  else
		    p2 = p2->right;
		}
	      break;
	    }
	  p = p->right;
	}
      if (!p)
	return ASN1_ELEMENT_NOT_FOUND;
      break;
    case ASN1_ETYPE_ANY:
      _asn1_set_value_lv (node, value, len);
      break;
    case ASN1_ETYPE_SEQUENCE_OF:
    case ASN1_ETYPE_SET_OF:
      if (_asn1_strcmp (value, "NEW"))
	return ASN1_VALUE_NOT_VALID;
      _asn1_append_sequence_set (node);
      break;
    default:
      return ASN1_ELEMENT_NOT_FOUND;
      break;
    }

  return ASN1_SUCCESS;
}


static void futex_wait_queue_me(struct futex_hash_bucket *hb, struct futex_q *q,
				struct hrtimer_sleeper *timeout)
{
	/*
	 * The task state is guaranteed to be set before another task can
	 * wake it. set_current_state() is implemented using set_mb() and
	 * queue_me() calls spin_unlock() upon completion, both serializing
	 * access to the hash list and forcing another memory barrier.
	 */
	set_current_state(TASK_INTERRUPTIBLE);
	queue_me(q, hb);

	/* Arm the timer */
	if (timeout) {
		hrtimer_start_expires(&timeout->timer, HRTIMER_MODE_ABS);
		if (!hrtimer_active(&timeout->timer))
			timeout->task = NULL;
	}

	/*
	 * If we have been removed from the hash list, then another task
	 * has tried to wake us, and we can skip the call to schedule().
	 */
	if (likely(!plist_node_empty(&q->list))) {
		/*
		 * If the timer has already expired, current will already be
		 * flagged for rescheduling. Only call schedule if there
		 * is no timeout, or if it has yet to expire.
		 */
		if (!timeout || timeout->task)
			freezable_schedule();
	}
	__set_current_state(TASK_RUNNING);
}


static int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val,
		      ktime_t *abs_time, u32 bitset)
{
	struct hrtimer_sleeper timeout, *to = NULL;
	struct restart_block *restart;
	struct futex_hash_bucket *hb;
	struct futex_q q = futex_q_init;
	int ret;

	if (!bitset)
		return -EINVAL;
	q.bitset = bitset;

	if (abs_time) {
		to = &timeout;

		hrtimer_init_on_stack(&to->timer, (flags & FLAGS_CLOCKRT) ?
				      CLOCK_REALTIME : CLOCK_MONOTONIC,
				      HRTIMER_MODE_ABS);
		hrtimer_init_sleeper(to, current);
		hrtimer_set_expires_range_ns(&to->timer, *abs_time,
					     current->timer_slack_ns);
	}

retry:
	/*
	 * Prepare to wait on uaddr. On success, holds hb lock and increments
	 * q.key refs.
	 */
	ret = futex_wait_setup(uaddr, val, flags, &q, &hb);
	if (ret)
		goto out;

	/* queue_me and wait for wakeup, timeout, or a signal. */
	futex_wait_queue_me(hb, &q, to);

	/* If we were woken (and unqueued), we succeeded, whatever. */
	ret = 0;
	/* unqueue_me() drops q.key ref */
	if (!unqueue_me(&q))
		goto out;
	ret = -ETIMEDOUT;
	if (to && !to->task)
		goto out;

	/*
	 * We expect signal_pending(current), but we might be the
	 * victim of a spurious wakeup as well.
	 */
	if (!signal_pending(current))
		goto retry;

	ret = -ERESTARTSYS;
	if (!abs_time)
		goto out;

	restart = &current_thread_info()->restart_block;
	restart->fn = futex_wait_restart;
	restart->futex.uaddr = uaddr;
	restart->futex.val = val;
	restart->futex.time = abs_time->tv64;
	restart->futex.bitset = bitset;
	restart->futex.flags = flags | FLAGS_HAS_TIMEOUT;

	ret = -ERESTART_RESTARTBLOCK;

out:
	if (to) {
		hrtimer_cancel(&to->timer);
		destroy_hrtimer_on_stack(&to->timer);
	}
	return ret;
}


	__releases(&hb->lock)
{
	spin_unlock(&hb->lock);
	hb_waiters_dec(hb);
}


static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
				struct task_struct *newowner)
{
	u32 newtid = task_pid_vnr(newowner) | FUTEX_WAITERS;
	struct futex_pi_state *pi_state = q->pi_state;
	struct task_struct *oldowner = pi_state->owner;
	u32 uval, uninitialized_var(curval), newval;
	int ret;

	/* Owner died? */
	if (!pi_state->owner)
		newtid |= FUTEX_OWNER_DIED;

	/*
	 * We are here either because we stole the rtmutex from the
	 * previous highest priority waiter or we are the highest priority
	 * waiter but failed to get the rtmutex the first time.
	 * We have to replace the newowner TID in the user space variable.
	 * This must be atomic as we have to preserve the owner died bit here.
	 *
	 * Note: We write the user space value _before_ changing the pi_state
	 * because we can fault here. Imagine swapped out pages or a fork
	 * that marked all the anonymous memory readonly for cow.
	 *
	 * Modifying pi_state _before_ the user space value would
	 * leave the pi_state in an inconsistent state when we fault
	 * here, because we need to drop the hash bucket lock to
	 * handle the fault. This might be observed in the PID check
	 * in lookup_pi_state.
	 */
retry:
	if (get_futex_value_locked(&uval, uaddr))
		goto handle_fault;

	while (1) {
		newval = (uval & FUTEX_OWNER_DIED) | newtid;

		if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval))
			goto handle_fault;
		if (curval == uval)
			break;
		uval = curval;
	}

	/*
	 * We fixed up user space. Now we need to fix the pi_state
	 * itself.
	 */
	if (pi_state->owner != NULL) {
		raw_spin_lock_irq(&pi_state->owner->pi_lock);
		WARN_ON(list_empty(&pi_state->list));
		list_del_init(&pi_state->list);
		raw_spin_unlock_irq(&pi_state->owner->pi_lock);
	}

	pi_state->owner = newowner;

	raw_spin_lock_irq(&newowner->pi_lock);
	WARN_ON(!list_empty(&pi_state->list));
	list_add(&pi_state->list, &newowner->pi_state_list);
	raw_spin_unlock_irq(&newowner->pi_lock);
	return 0;

	/*
	 * To handle the page fault we need to drop the hash bucket
	 * lock here. That gives the other task (either the highest priority
	 * waiter itself or the task which stole the rtmutex) the
	 * chance to try the fixup of the pi_state. So once we are
	 * back from handling the fault we need to check the pi_state
	 * after reacquiring the hash bucket lock and before trying to
	 * do another fixup. When the fixup has been done already we
	 * simply return.
	 */
handle_fault:
	spin_unlock(q->lock_ptr);

	ret = fault_in_user_writeable(uaddr);

	spin_lock(q->lock_ptr);

	/*
	 * Check if someone else fixed it for us:
	 */
	if (pi_state->owner != oldowner)
		return 0;

	if (ret)
		return ret;

	goto retry;
}


lookup_pi_state(u32 uval, struct futex_hash_bucket *hb,
		union futex_key *key, struct futex_pi_state **ps)
{
	struct futex_pi_state *pi_state = NULL;
	struct futex_q *this, *next;
	struct task_struct *p;
	pid_t pid = uval & FUTEX_TID_MASK;

	plist_for_each_entry_safe(this, next, &hb->chain, list) {
		if (match_futex(&this->key, key)) {
			/*
			 * Sanity check the waiter before increasing
			 * the refcount and attaching to it.
			 */
			pi_state = this->pi_state;
			/*
			 * Userspace might have messed up non-PI and
			 * PI futexes [3]
			 */
			if (unlikely(!pi_state))
				return -EINVAL;

			WARN_ON(!atomic_read(&pi_state->refcount));

			/*
			 * Handle the owner died case:
			 */
			if (uval & FUTEX_OWNER_DIED) {
				/*
				 * exit_pi_state_list sets owner to NULL and
				 * wakes the topmost waiter. The task which
				 * acquires the pi_state->rt_mutex will fixup
				 * owner.
				 */
				if (!pi_state->owner) {
					/*
					 * No pi state owner, but the user
					 * space TID is not 0. Inconsistent
					 * state. [5]
					 */
					if (pid)
						return -EINVAL;
					/*
					 * Take a ref on the state and
					 * return. [4]
					 */
					goto out_state;
				}

				/*
				 * If TID is 0, then either the dying owner
				 * has not yet executed exit_pi_state_list()
				 * or some waiter acquired the rtmutex in the
				 * pi state, but did not yet fixup the TID in
				 * user space.
				 *
				 * Take a ref on the state and return. [6]
				 */
				if (!pid)
					goto out_state;
			} else {
				/*
				 * If the owner died bit is not set,
				 * then the pi_state must have an
				 * owner. [7]
				 */
				if (!pi_state->owner)
					return -EINVAL;
			}

			/*
			 * Bail out if user space manipulated the
			 * futex value. If pi state exists then the
			 * owner TID must be the same as the user
			 * space TID. [9/10]
			 */
			if (pid != task_pid_vnr(pi_state->owner))
				return -EINVAL;

		out_state:
			atomic_inc(&pi_state->refcount);
			*ps = pi_state;
			return 0;
		}
	}

	/*
	 * We are the first waiter - try to look up the real owner and attach
	 * the new pi_state to it, but bail out when TID = 0 [1]
	 */
	if (!pid)
		return -ESRCH;
	p = futex_find_get_task(pid);
	if (!p)
		return -ESRCH;

	if (!p->mm) {
		put_task_struct(p);
		return -EPERM;
	}

	/*
	 * We need to look at the task state flags to figure out,
	 * whether the task is exiting. To protect against the do_exit
	 * change of the task flags, we do this protected by
	 * p->pi_lock:
	 */
	raw_spin_lock_irq(&p->pi_lock);
	if (unlikely(p->flags & PF_EXITING)) {
		/*
		 * The task is on the way out. When PF_EXITPIDONE is
		 * set, we know that the task has finished the
		 * cleanup:
		 */
		int ret = (p->flags & PF_EXITPIDONE) ? -ESRCH : -EAGAIN;

		raw_spin_unlock_irq(&p->pi_lock);
		put_task_struct(p);
		return ret;
	}

	/*
	 * No existing pi state. First waiter. [2]
	 */
	pi_state = alloc_pi_state();

	/*
	 * Initialize the pi_mutex in locked state and make 'p'
	 * the owner of it:
	 */
	rt_mutex_init_proxy_locked(&pi_state->pi_mutex, p);

	/* Store the key for possible exit cleanups: */
	pi_state->key = *key;

	WARN_ON(!list_empty(&pi_state->list));
	list_add(&pi_state->list, &p->pi_state_list);
	pi_state->owner = p;
	raw_spin_unlock_irq(&p->pi_lock);

	put_task_struct(p);

	*ps = pi_state;

	return 0;
}


static int futex_lock_pi_atomic(u32 __user *uaddr, struct futex_hash_bucket *hb,
				union futex_key *key,
				struct futex_pi_state **ps,
				struct task_struct *task, int set_waiters)
{
	int lock_taken, ret, force_take = 0;
	u32 uval, newval, curval, vpid = task_pid_vnr(task);

retry:
	ret = lock_taken = 0;

	/*
	 * To avoid races, we attempt to take the lock here again
	 * (by doing a 0 -> TID atomic cmpxchg), while holding all
	 * the locks. It will most likely not succeed.
	 */
	newval = vpid;
	if (set_waiters)
		newval |= FUTEX_WAITERS;

	if (unlikely(cmpxchg_futex_value_locked(&curval, uaddr, 0, newval)))
		return -EFAULT;

	/*
	 * Detect deadlocks.
	 */
	if ((unlikely((curval & FUTEX_TID_MASK) == vpid)))
		return -EDEADLK;

	/*
	 * Surprise - we got the lock, but we do not trust user space at all.
	 */
	if (unlikely(!curval)) {
		/*
		 * We verify whether there is kernel state for this
		 * futex. If not, we can safely assume, that the 0 ->
		 * TID transition is correct. If state exists, we do
		 * not bother to fixup the user space state as it was
		 * corrupted already.
		 */
		return futex_top_waiter(hb, key) ? -EINVAL : 1;
	}

	uval = curval;

	/*
	 * Set the FUTEX_WAITERS flag, so the owner will know it has someone
	 * to wake at the next unlock.
	 */
	newval = curval | FUTEX_WAITERS;

	/*
	 * Should we force take the futex? See below.
	 */
	if (unlikely(force_take)) {
		/*
		 * Keep the OWNER_DIED and the WAITERS bit and set the
		 * new TID value.
		 */
		newval = (curval & ~FUTEX_TID_MASK) | vpid;
		force_take = 0;
		lock_taken = 1;
	}

	if (unlikely(cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)))
		return -EFAULT;
	if (unlikely(curval != uval))
		goto retry;

	/*
	 * We took the lock due to forced take over.
	 */
	if (unlikely(lock_taken))
		return 1;

	/*
	 * We dont have the lock. Look up the PI state (or create it if
	 * we are the first waiter):
	 */
	ret = lookup_pi_state(uval, hb, key, ps);

	if (unlikely(ret)) {
		switch (ret) {
		case -ESRCH:
			/*
			 * We failed to find an owner for this
			 * futex. So we have no pi_state to block
			 * on. This can happen in two cases:
			 *
			 * 1) The owner died
			 * 2) A stale FUTEX_WAITERS bit
			 *
			 * Re-read the futex value.
			 */
			if (get_futex_value_locked(&curval, uaddr))
				return -EFAULT;

			/*
			 * If the owner died or we have a stale
			 * WAITERS bit the owner TID in the user space
			 * futex is 0.
			 */
			if (!(curval & FUTEX_TID_MASK)) {
				force_take = 1;
				goto retry;
			}
		default:
			break;
		}
	}

	return ret;
}


handle_ns_request(int dns_fd, struct query *q)
/* Mostly identical to handle_a_request() below */
{
	char buf[64*1024];
	int len;

	if (ns_ip != INADDR_ANY) {
		/* If ns_ip set, overwrite destination addr with it.
		 * Destination addr will be sent as additional record (A, IN) */
		memcpy(&q->destination.s_addr, &ns_ip, sizeof(in_addr_t));
	}

	len = dns_encode_ns_response(buf, sizeof(buf), q, topdomain);
	if (len < 1) {
		warnx("dns_encode_ns_response doesn't fit");
		return;
	}

	if (debug >= 2) {
		fprintf(stderr, "TX: client %s, type %d, name %s, %d bytes NS reply\n",
			format_addr(&q->from, q->fromlen), q->type, q->name, len);
	}
	if (sendto(dns_fd, buf, len, 0, (struct sockaddr*)&q->from, q->fromlen) <= 0) {
		warn("ns reply send error");
	}
}


handle_full_packet(int tun_fd, int dns_fd, int userid)
{
	unsigned long outlen;
	char out[64*1024];
	int touser;
	int ret;

	outlen = sizeof(out);
	ret = uncompress((uint8_t*)out, &outlen,
		   (uint8_t*)users[userid].inpacket.data, users[userid].inpacket.len);

	if (ret == Z_OK) {
		struct ip *hdr;

		hdr = (struct ip*) (out + 4);
		touser = find_user_by_ip(hdr->ip_dst.s_addr);

		if (touser == -1) {
			/* send the uncompressed packet to tun device */
			write_tun(tun_fd, out, outlen);
		} else {
			/* send the compressed(!) packet to other client */
			if (users[touser].conn == CONN_DNS_NULL) {
				if (users[touser].outpacket.len == 0) {
					start_new_outpacket(touser,
						users[userid].inpacket.data,
						users[userid].inpacket.len);

					/* Start sending immediately if query is waiting */
					if (users[touser].q_sendrealsoon.id != 0)
						send_chunk_or_dataless(dns_fd, touser, &users[touser].q_sendrealsoon);
					else if (users[touser].q.id != 0)
						send_chunk_or_dataless(dns_fd, touser, &users[touser].q);
#ifdef OUTPACKETQ_LEN
				} else {
					save_to_outpacketq(touser,
						users[userid].inpacket.data,
						users[userid].inpacket.len);
#endif
				}
			} else{ /* CONN_RAW_UDP */
				send_raw(dns_fd, users[userid].inpacket.data,
					 users[userid].inpacket.len, touser,
					 RAW_HDR_CMD_DATA, &users[touser].q);
			}
		}
	} else {
		if (debug >= 1)
			fprintf(stderr, "Discarded data, uncompress() result: %d\n", ret);
	}

	/* This packet is done */
	users[userid].inpacket.len = 0;
	users[userid].inpacket.offset = 0;
}


get_from_outpacketq(int userid)
/* Starts new outpacket from queue, if any.
   Returns: 1 = okay, 0 = no packets were waiting. */
{
	int use;

	if (users[userid].outpacketq_filled <= 0)
		/* no packets */
		return 0;

	use = users[userid].outpacketq_nexttouse;

	start_new_outpacket(userid, users[userid].outpacketq[use].data,
			    users[userid].outpacketq[use].len);

	use++;
	if (use >= OUTPACKETQ_LEN)
		use = 0;
	users[userid].outpacketq_nexttouse = use;
	users[userid].outpacketq_filled--;

	if (debug >= 3)
		fprintf(stderr, "    Qget, now %d\n",
			users[userid].outpacketq_filled);

	return 1;
}


save_to_qmem_pingordata(int userid, struct query *q)
{
	/* Our CMC is a bit more than the "official" CMC; we store 4 bytes
	   just because we can, and because it may prevent some false matches.
	   For ping, we save the 4 decoded bytes: userid + seq/frag + CMC.
	   For data, we save the 4 _un_decoded chars in lowercase: seq/frag's
	   + 1 char CMC; that last char is non-Base32.
	 */

	char cmc[8];
	int i;

	if (q->name[0] == 'P' || q->name[0] == 'p') {
		/* Ping packet */

		size_t cmcsize = sizeof(cmc);
		char *cp = strchr(q->name, '.');

		if (cp == NULL)
			return;  /* illegal hostname; shouldn't happen */

		/* We already unpacked in handle_null_request(), but that's
		   lost now... Note: b32 directly, we want no undotify here! */
		i = b32->decode(cmc, &cmcsize, q->name + 1, (cp - q->name) - 1);

		if (i < 4)
			return;	 /* illegal ping; shouldn't happen */

		save_to_qmem(users[userid].qmemping_cmc,
			     users[userid].qmemping_type, QMEMPING_LEN,
			     &users[userid].qmemping_lastfilled,
			     (void *) cmc, q->type);
	} else {
		/* Data packet, hopefully not illegal */
		if (strlen(q->name) < 5)
			return;

		/* We store CMC in lowercase; if routing via multiple parallel
		   DNS servers, one may do case-switch and another may not,
		   and we still want to detect duplicates.
		   Data-header is always base32, so case-swap won't hurt.
		 */
		for (i = 0; i < 4; i++)
			if (q->name[i+1] >= 'A' && q->name[i+1] <= 'Z')
				cmc[i] = q->name[i+1] + ('a' - 'A');
			else
				cmc[i] = q->name[i+1];

		save_to_qmem(users[userid].qmemdata_cmc,
			     users[userid].qmemdata_type, QMEMDATA_LEN,
			     &users[userid].qmemdata_lastfilled,
			     (void *) cmc, q->type);
	}
}


forward_query(int bind_fd, struct query *q)
{
	char buf[64*1024];
	int len;
	struct fw_query fwq;
	struct sockaddr_in *myaddr;
	in_addr_t newaddr;

	len = dns_encode(buf, sizeof(buf), q, QR_QUERY, q->name, strlen(q->name));
	if (len < 1) {
		warnx("dns_encode doesn't fit");
		return;
	}

	/* Store sockaddr for q->id */
	memcpy(&(fwq.addr), &(q->from), q->fromlen);
	fwq.addrlen = q->fromlen;
	fwq.id = q->id;
	fw_query_put(&fwq);

	newaddr = inet_addr("127.0.0.1");
	myaddr = (struct sockaddr_in *) &(q->from);
	memcpy(&(myaddr->sin_addr), &newaddr, sizeof(in_addr_t));
	myaddr->sin_port = htons(bind_port);

	if (debug >= 2) {
		fprintf(stderr, "TX: NS reply \n");
	}

	if (sendto(bind_fd, buf, len, 0, (struct sockaddr*)&q->from, q->fromlen) <= 0) {
		warn("forward query error");
	}
}


syslog(int a, const char *str, ...)
{
	/* TODO: implement (add to event log), move to common.c */
	;
}


save_to_qmem(unsigned char *qmem_cmc, unsigned short *qmem_type, int qmem_len,
	     int *qmem_lastfilled, unsigned char *cmc_to_add,
	     unsigned short type_to_add)
/* Remember query to check for duplicates */
{
	int fill;

	fill = *qmem_lastfilled + 1;
	if (fill >= qmem_len)
		fill = 0;

	memcpy(qmem_cmc + fill * 4, cmc_to_add, 4);
	qmem_type[fill] = type_to_add;
	*qmem_lastfilled = fill;
}


static int wsgi_setup_access(WSGIDaemonProcess *daemon)
{
    /* Setup the umask for the effective user. */

    if (daemon->group->umask != -1)
        umask(daemon->group->umask);

    /* Change to chroot environment. */

    if (daemon->group->root) {
        if (chroot(daemon->group->root) == -1) {
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                         "mod_wsgi (pid=%d): Unable to change root "
                         "directory to '%s'.", getpid(), daemon->group->root);

            return -1;
        }
    }

    /* Setup the working directory.*/

    if (daemon->group->home) {
        if (chdir(daemon->group->home) == -1) {
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                         "mod_wsgi (pid=%d): Unable to change working "
                         "directory to '%s'.", getpid(), daemon->group->home);

            return -1;
        }
    }
    else if (geteuid()) {
        struct passwd *pwent;

        pwent = getpwuid(geteuid());

        if (pwent) {
            if (chdir(pwent->pw_dir) == -1) {
                ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                             "mod_wsgi (pid=%d): Unable to change working "
                             "directory to '%s'.", getpid(), pwent->pw_dir);

            return -1;
            }
        }
        else {
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                         "mod_wsgi (pid=%d): Unable to determine home "
                         "directory for uid=%ld.", getpid(), (long)geteuid());

            return -1;
        }
    }
    else {
        struct passwd *pwent;

        pwent = getpwuid(daemon->group->uid);

        if (pwent) {
            if (chdir(pwent->pw_dir) == -1) {
                ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                             "mod_wsgi (pid=%d): Unable to change working "
                             "directory to '%s'.", getpid(), pwent->pw_dir);

                return -1;
            }
        }
        else {
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                         "mod_wsgi (pid=%d): Unable to determine home "
                         "directory for uid=%ld.", getpid(),
                         (long)daemon->group->uid);

            return -1;
        }
    }

    /* Don't bother switch user/group if not root. */

    if (geteuid())
        return 0;

    /* Setup the daemon process real and effective group. */

    if (setgid(daemon->group->gid) == -1) {
        ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                     "mod_wsgi (pid=%d): Unable to set group id to gid=%u.",
                     getpid(), (unsigned)daemon->group->gid);

        return -1;
    }
    else {
        if (daemon->group->groups) {
            if (setgroups(daemon->group->groups_count,
                          daemon->group->groups) == -1) {
                ap_log_error(APLOG_MARK, APLOG_ALERT, errno,
                             wsgi_server, "mod_wsgi (pid=%d): Unable "
                             "to set supplementary groups for uname=%s "
                             "of '%s'.", getpid(), daemon->group->user,
                             daemon->group->groups_list);

                return -1;
            }
        }
        else if (initgroups(daemon->group->user, daemon->group->gid) == -1) {
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno,
                         wsgi_server, "mod_wsgi (pid=%d): Unable "
                         "to set groups for uname=%s and gid=%u.", getpid(),
                         daemon->group->user, (unsigned)daemon->group->gid);

            return -1;
        }
    }

    /* Setup the daemon process real and effective user. */

    if (setuid(daemon->group->uid) == -1) {
        ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                     "mod_wsgi (pid=%d): Unable to change to uid=%ld.",
                     getpid(), (long)daemon->group->uid);

        /*
         * On true UNIX systems this should always succeed at
         * this point. With certain Linux kernel versions though
         * we can get back EAGAIN where the target user had
         * reached their process limit. In that case will be left
         * running as wrong user. Just exit on all failures to be
         * safe. Don't die immediately to avoid a fork bomb.
         *
         * We could just return -1 here and let the caller do the
         * sleep() and exit() but this failure is critical enough
         * that we still do it here so it is obvious that the issue
         * is being addressed.
         */

        ap_log_error(APLOG_MARK, APLOG_ALERT, 0, wsgi_server,
                     "mod_wsgi (pid=%d): Failure to configure the "
                     "daemon process correctly and process left in "
                     "unspecified state. Restarting daemon process "
                     "after delay.", getpid());

        sleep(20);

        exit(-1);
    }

    /*
     * Linux prevents generation of core dumps after setuid()
     * has been used. Attempt to reenable ability to dump core
     * so that the CoreDumpDirectory directive still works.
     */

#if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
    /* This applies to Linux 2.4 and later. */
    if (ap_coredumpdir_configured) {
        if (prctl(PR_SET_DUMPABLE, 1)) {
            ap_log_error(APLOG_MARK, APLOG_ALERT, errno, wsgi_server,
                    "mod_wsgi (pid=%d): Set dumpable failed. This child "
                    "will not coredump after software errors.", getpid());
        }
    }
#endif

    return 0;
}


static const char *wsgi_callable_object(request_rec *r, const char *s)
{
    const char *name = NULL;
    const char *value = NULL;

    if (!s)
        return "application";

    if (*s != '%')
        return s;

    name = s + 1;

    if (!*name)
        return "application";

    if (strstr(name, "{ENV:") == name) {
        long len = 0;

        name = name + 5;
        len = strlen(name);

        if (len && name[len-1] == '}') {
            name = apr_pstrndup(r->pool, name, len-1);

            value = apr_table_get(r->notes, name);

            if (!value)
                value = apr_table_get(r->subprocess_env, name);

            if (!value)
                value = getenv(name);

            if (value)
                return value;
        }
    }

    return "application";
}


static void *wsgi_merge_dir_config(apr_pool_t *p, void *base_conf,
                                   void *new_conf)
{
    WSGIDirectoryConfig *config = NULL;
    WSGIDirectoryConfig *parent = NULL;
    WSGIDirectoryConfig *child = NULL;

    config = newWSGIDirectoryConfig(p);

    parent = (WSGIDirectoryConfig *)base_conf;
    child = (WSGIDirectoryConfig *)new_conf;

    if (child->restrict_process)
        config->restrict_process = child->restrict_process;
    else
        config->restrict_process = parent->restrict_process;

    if (child->process_group)
        config->process_group = child->process_group;
    else
        config->process_group = parent->process_group;

    if (child->application_group)
        config->application_group = child->application_group;
    else
        config->application_group = parent->application_group;

    if (child->callable_object)
        config->callable_object = child->callable_object;
    else
        config->callable_object = parent->callable_object;

    if (child->dispatch_script)
        config->dispatch_script = child->dispatch_script;
    else
        config->dispatch_script = parent->dispatch_script;

    if (child->pass_apache_request != -1)
        config->pass_apache_request = child->pass_apache_request;
    else
        config->pass_apache_request = parent->pass_apache_request;

    if (child->pass_authorization != -1)
        config->pass_authorization = child->pass_authorization;
    else
        config->pass_authorization = parent->pass_authorization;

    if (child->script_reloading != -1)
        config->script_reloading = child->script_reloading;
    else
        config->script_reloading = parent->script_reloading;

    if (child->error_override != -1)
        config->error_override = child->error_override;
    else
        config->error_override = parent->error_override;

    if (child->chunked_request != -1)
        config->chunked_request = child->chunked_request;
    else
        config->chunked_request = parent->chunked_request;

    if (child->enable_sendfile != -1)
        config->enable_sendfile = child->enable_sendfile;
    else
        config->enable_sendfile = parent->enable_sendfile;

    if (child->access_script)
        config->access_script = child->access_script;
    else
        config->access_script = parent->access_script;

    if (child->auth_user_script)
        config->auth_user_script = child->auth_user_script;
    else
        config->auth_user_script = parent->auth_user_script;

    if (child->auth_group_script)
        config->auth_group_script = child->auth_group_script;
    else
        config->auth_group_script = parent->auth_group_script;

    if (child->user_authoritative != -1)
        config->user_authoritative = child->user_authoritative;
    else
        config->user_authoritative = parent->user_authoritative;

    if (child->group_authoritative != -1)
        config->group_authoritative = child->group_authoritative;
    else
        config->group_authoritative = parent->group_authoritative;

    if (!child->handler_scripts)
        config->handler_scripts = parent->handler_scripts;
    else if (!parent->handler_scripts)
        config->handler_scripts = child->handler_scripts;
    else {
        config->handler_scripts = apr_hash_overlay(p, child->handler_scripts,
                                                   parent->handler_scripts);
    }

    return config;
}


static int Adapter_process_file_wrapper(AdapterObject *self)
{
    int done = 0;

#ifndef WIN32
    PyObject *filelike = NULL;
    PyObject *method = NULL;
    PyObject *object = NULL;

    apr_status_t rv = 0;

    apr_os_file_t fd = -1;
    apr_file_t *tmpfile = NULL;
    apr_finfo_t finfo;

    apr_off_t fd_offset = 0;
    apr_off_t fo_offset = 0;

    apr_off_t length = 0;

    /* Perform file wrapper optimisations where possible. */

    if (!PyObject_IsInstance(self->sequence, (PyObject *)&Stream_Type))
        return 0;

    /*
     * Only attempt to perform optimisations if the
     * write() function returned by start_response()
     * function has not been called with non zero length
     * data. In other words if no prior response content
     * generated. Technically it could be done, but want
     * to have a consistent rule about how specifying a
     * content length affects how much of a file is
     * sent. Don't want to have to take into
     * consideration whether write() function has been
     * called or not as just complicates things.
     */

    if (self->output_length != 0)
        return 0;

    /*
     * Work out if file wrapper is associated with a
     * file like object, where that file object is
     * associated with a regular file. If it does then
     * we can optimise how the contents of the file are
     * sent out. If no such associated file descriptor
     * then it needs to be processed like any other
     * iterable value.
     */

    filelike = PyObject_GetAttrString((PyObject *)self->sequence, "filelike");

    if (!filelike) {
        PyErr_SetString(PyExc_KeyError,
                        "file wrapper no filelike attribute");
        return 0;
    }

    fd = PyObject_AsFileDescriptor(filelike);
    if (fd == -1) {
        PyErr_Clear();
        Py_DECREF(filelike);
        return 0;
    }

    Py_DECREF(filelike);

    /*
     * On some platforms, such as Linux, sendfile() system call
     * will not work on UNIX sockets. Thus when using daemon mode
     * cannot enable that feature.
     */

    if (self->config->enable_sendfile)
        apr_os_file_put(&tmpfile, &fd, APR_SENDFILE_ENABLED, self->r->pool);
    else
        apr_os_file_put(&tmpfile, &fd, 0, self->r->pool);

    rv = apr_file_info_get(&finfo, APR_FINFO_SIZE|APR_FINFO_TYPE, tmpfile);
    if (rv != APR_SUCCESS || finfo.filetype != APR_REG)
        return 0;

    /*
     * Because Python file like objects potentially have
     * their own buffering layering, or use an operating
     * system FILE object which also has a buffering
     * layer on top of a normal file descriptor, need to
     * determine from the file like object its position
     * within the file and use that as starting position.
     * Note that it is assumed that user had flushed any
     * modifications to the file as necessary. Also, we
     * need to make sure we remember the original file
     * descriptor position as will need to restore that
     * position so it matches the upper buffering layers
     * when done. This is done to avoid any potential
     * problems if file like object does anything strange
     * in its close() method which relies on file position
     * being what it thought it should be.
     */

    rv = apr_file_seek(tmpfile, APR_CUR, &fd_offset);
    if (rv != APR_SUCCESS)
        return 0;

    method = PyObject_GetAttrString(filelike, "tell");
    if (!method)
        return 0;

    object = PyEval_CallObject(method, NULL);
    Py_DECREF(method);

    if (!object) {
        PyErr_Clear();
        return 0;
    }

    if (PyLong_Check(object)) {
#if defined(HAVE_LONG_LONG)
        fo_offset = PyLong_AsLongLong(object);
#else
        fo_offset = PyLong_AsLong(object);
#endif
    }
#if PY_MAJOR_VERSION < 3
    else if (PyInt_Check(object)) {
        fo_offset = PyInt_AsLong(object);
    }
#endif
    else {
        Py_DECREF(object);
        return 0;
    }

    if (PyErr_Occurred()){
        Py_DECREF(object);
        PyErr_Clear();
        return 0;
    }

    Py_DECREF(object);

    /*
     * For a file wrapper object need to always ensure
     * that response headers are parsed. This is done so
     * that if the content length header has been
     * defined we can get its value and use it to limit
     * how much of a file is being sent. The WSGI 1.0
     * specification says that we are meant to send all
     * available bytes from the file, however this is
     * questionable as sending more than content length
     * would violate HTTP RFC. Note that this doesn't
     * actually flush the headers out when using Apache
     * 2.X. This is good, as we want to still be able to
     * set the content length header if none set and file
     * is seekable. If processing response headers fails,
     * then need to return as if done, with error being
     * logged later.
     */

    if (!Adapter_output(self, "", 0, NULL, 0))
        return 1;

    /*
     * If content length wasn't defined then determine
     * the amount of data which is available to send and
     * set the content length response header. Either
     * way, if can work out length then send data
     * otherwise fall through and treat it as normal
     * iterable.
     */

    if (!self->content_length_set) {
        length = finfo.size - fo_offset;
        self->output_length += length;

        ap_set_content_length(self->r, length);

        self->content_length_set = 1;
        self->content_length = length;

        if (Adapter_output_file(self, tmpfile, fo_offset, length))
            self->result = OK;

        done = 1;
    }
    else {
        length = finfo.size - fo_offset;
        self->output_length += length;

        /* Use user specified content length instead. */

        length = self->content_length;

        if (Adapter_output_file(self, tmpfile, fo_offset, length))
            self->result = OK;

        done = 1;
    }

    /*
     * Restore position of underlying file descriptor.
     * If this fails, then not much we can do about it.
     */

    apr_file_seek(tmpfile, APR_SET, &fd_offset);

#endif

    return done;
}


static PyObject *Adapter_environ(AdapterObject *self)
{
    request_rec *r = NULL;

    PyObject *vars = NULL;
    PyObject *object = NULL;

    const apr_array_header_t *head = NULL;
    const apr_table_entry_t *elts = NULL;

    int i = 0;

    const char *scheme = NULL;

    /* Create the WSGI environment dictionary. */

    vars = PyDict_New();

    /* Merge the CGI environment into the WSGI environment. */

    r = self->r;

    head = apr_table_elts(r->subprocess_env);
    elts = (apr_table_entry_t *)head->elts;

    for (i = 0; i < head->nelts; ++i) {
        if (elts[i].key) {
            if (elts[i].val) {
#if PY_MAJOR_VERSION >= 3
                if (!strcmp(elts[i].val, "DOCUMENT_ROOT")) {
                    object = PyUnicode_Decode(elts[i].val, strlen(elts[i].val),
                                             Py_FileSystemDefaultEncoding,
                                             "surrogateescape");
                }
                else if (!strcmp(elts[i].val, "SCRIPT_FILENAME")) {
                    object = PyUnicode_Decode(elts[i].val, strlen(elts[i].val),
                                             Py_FileSystemDefaultEncoding,
                                             "surrogateescape");
                }
                else {
                    object = PyUnicode_DecodeLatin1(elts[i].val,
                                                    strlen(elts[i].val), NULL);
                }
#else
                object = PyString_FromString(elts[i].val);
#endif
                PyDict_SetItemString(vars, elts[i].key, object);
                Py_DECREF(object);
            }
            else
                PyDict_SetItemString(vars, elts[i].key, Py_None);
        }
    }

    PyDict_DelItemString(vars, "PATH");

    /* Now setup all the WSGI specific environment values. */

    object = Py_BuildValue("(ii)", 1, 0);
    PyDict_SetItemString(vars, "wsgi.version", object);
    Py_DECREF(object);

    object = PyBool_FromLong(wsgi_multithread);
    PyDict_SetItemString(vars, "wsgi.multithread", object);
    Py_DECREF(object);

    object = PyBool_FromLong(wsgi_multiprocess);
    PyDict_SetItemString(vars, "wsgi.multiprocess", object);
    Py_DECREF(object);

#if defined(MOD_WSGI_WITH_DAEMONS)
    if (wsgi_daemon_process) {
        if (wsgi_daemon_process->group->threads == 1 &&
            wsgi_daemon_process->group->maximum_requests == 1) {
            PyDict_SetItemString(vars, "wsgi.run_once", Py_True);
        }
        else
            PyDict_SetItemString(vars, "wsgi.run_once", Py_False);
    }
    else
        PyDict_SetItemString(vars, "wsgi.run_once", Py_False);
#else
    PyDict_SetItemString(vars, "wsgi.run_once", Py_False);
#endif

    scheme = apr_table_get(r->subprocess_env, "HTTPS");

    if (scheme && (!strcasecmp(scheme, "On") || !strcmp(scheme, "1"))) {
#if PY_MAJOR_VERSION >= 3
        object = PyUnicode_FromString("https");
#else
        object = PyString_FromString("https");
#endif
        PyDict_SetItemString(vars, "wsgi.url_scheme", object);
        Py_DECREF(object);
    }
    else {
#if PY_MAJOR_VERSION >= 3
        object = PyUnicode_FromString("http");
#else
        object = PyString_FromString("http");
#endif
        PyDict_SetItemString(vars, "wsgi.url_scheme", object);
        Py_DECREF(object);
    }

    /*
     * We remove the HTTPS variable because WSGI compliant
     * applications shouldn't rely on it. Instead they should
     * use wsgi.url_scheme. We do this even if SetEnv was
     * used to set HTTPS from Apache configuration. That is
     * we convert it into the correct variable and remove the
     * original.
     */

    if (scheme)
        PyDict_DelItemString(vars, "HTTPS");

    /*
     * Setup log object for WSGI errors. Don't decrement
     * reference to log object as keep reference to it.
     */

    object = (PyObject *)self->log;
    PyDict_SetItemString(vars, "wsgi.errors", object);

    /* Setup input object for request content. */

    object = (PyObject *)self->input;
    PyDict_SetItemString(vars, "wsgi.input", object);

    /* Setup file wrapper object for efficient file responses. */

    PyDict_SetItemString(vars, "wsgi.file_wrapper", (PyObject *)&Stream_Type);

    /* Add Apache and mod_wsgi version information. */

    object = Py_BuildValue("(iii)", AP_SERVER_MAJORVERSION_NUMBER,
                           AP_SERVER_MINORVERSION_NUMBER,
                           AP_SERVER_PATCHLEVEL_NUMBER);
    PyDict_SetItemString(vars, "apache.version", object);
    Py_DECREF(object);

    object = Py_BuildValue("(iii)", MOD_WSGI_MAJORVERSION_NUMBER,
                           MOD_WSGI_MINORVERSION_NUMBER,
                           MOD_WSGI_MICROVERSION_NUMBER);
    PyDict_SetItemString(vars, "mod_wsgi.version", object);
    Py_DECREF(object);

    /*
     * If Apache extensions are enabled and running in embedded
     * mode add a CObject reference to the Apache request_rec
     * structure instance.
     */

    if (!wsgi_daemon_pool && self->config->pass_apache_request) {
#if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 2) || \
    (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION >= 7)
        object = PyCapsule_New(self->r, 0, 0);
#else
        object = PyCObject_FromVoidPtr(self->r, 0);
#endif
        PyDict_SetItemString(vars, "apache.request_rec", object);
        Py_DECREF(object);
    }

    /*
     * Extensions for accessing SSL certificate information from
     * mod_ssl when in use.
     */

#if 0
    if (!wsgi_daemon_pool) {
        object = PyObject_GetAttrString((PyObject *)self, "ssl_is_https");
        PyDict_SetItemString(vars, "mod_ssl.is_https", object);
        Py_DECREF(object);

        object = PyObject_GetAttrString((PyObject *)self, "ssl_var_lookup");
        PyDict_SetItemString(vars, "mod_ssl.var_lookup", object);
        Py_DECREF(object);
    }
#endif

    return vars;
}


static void spl_array_it_dtor(zend_object_iterator *iter TSRMLS_DC) /* {{{ */
{
	spl_array_it *iterator = (spl_array_it *)iter;

	zend_user_it_invalidate_current(iter TSRMLS_CC);
	zval_ptr_dtor((zval**)&iterator->intern.it.data);

	efree(iterator);
}


SPL_METHOD(SplObjectStorage, removeAll)
{
	zval *obj;
	spl_SplObjectStorage *intern = (spl_SplObjectStorage *)zend_object_store_get_object(getThis() TSRMLS_CC);
	spl_SplObjectStorage *other;
	spl_SplObjectStorageElement *element;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &obj, spl_ce_SplObjectStorage) == FAILURE) {
		return;
	}

	other = (spl_SplObjectStorage *)zend_object_store_get_object(obj TSRMLS_CC);

	zend_hash_internal_pointer_reset(&other->storage);
	while (zend_hash_get_current_data(&other->storage, (void **)&element) == SUCCESS) {
		if (spl_object_storage_detach(intern, getThis(), element->obj TSRMLS_CC) == FAILURE) {
			zend_hash_move_forward(&other->storage);
		}
	}

	zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
	intern->index = 0;

	RETURN_LONG(zend_hash_num_elements(&intern->storage));
} /* }}} */


static inline HashTable *spl_array_get_hash_table(spl_array_object* intern, int check_std_props TSRMLS_DC) { /* {{{ */
	if ((intern->ar_flags & SPL_ARRAY_IS_SELF) != 0) {
		if (!intern->std.properties) {
			rebuild_object_properties(&intern->std);
		}
		return intern->std.properties;
	} else if ((intern->ar_flags & SPL_ARRAY_USE_OTHER) && (check_std_props == 0 || (intern->ar_flags & SPL_ARRAY_STD_PROP_LIST) == 0) && Z_TYPE_P(intern->array) == IS_OBJECT) {
		spl_array_object *other  = (spl_array_object*)zend_object_store_get_object(intern->array TSRMLS_CC);
		return spl_array_get_hash_table(other, check_std_props TSRMLS_CC);
	} else if ((intern->ar_flags & ((check_std_props ? SPL_ARRAY_STD_PROP_LIST : 0) | SPL_ARRAY_IS_SELF)) != 0) {
		if (!intern->std.properties) {
			rebuild_object_properties(&intern->std);
		}
		return intern->std.properties;
	} else {
		return HASH_OF(intern->array);
	}
} /* }}} */


static HashTable* spl_array_get_debug_info(zval *obj, int *is_temp TSRMLS_DC) /* {{{ */
{
	spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(obj TSRMLS_CC);
	zval *tmp, *storage;
	int name_len;
	char *zname;
	zend_class_entry *base;

	*is_temp = 0;

	if (!intern->std.properties) {
		rebuild_object_properties(&intern->std);
	}

	if (HASH_OF(intern->array) == intern->std.properties) {
		return intern->std.properties;
	} else {
		if (intern->debug_info == NULL) {
			ALLOC_HASHTABLE(intern->debug_info);
			ZEND_INIT_SYMTABLE_EX(intern->debug_info, zend_hash_num_elements(intern->std.properties) + 1, 0);
		}

		if (intern->debug_info->nApplyCount == 0) {
			zend_hash_clean(intern->debug_info);
			zend_hash_copy(intern->debug_info, intern->std.properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));

			storage = intern->array;
			zval_add_ref(&storage);

			base = (Z_OBJ_HT_P(obj) == &spl_handler_ArrayIterator) ? spl_ce_ArrayIterator : spl_ce_ArrayObject;
			zname = spl_gen_private_prop_name(base, "storage", sizeof("storage")-1, &name_len TSRMLS_CC);
			zend_symtable_update(intern->debug_info, zname, name_len+1, &storage, sizeof(zval *), NULL);
			efree(zname);
		}

		return intern->debug_info;
	}
}


static void spl_array_write_property(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC) /* {{{ */
{
	spl_array_object *intern = (spl_array_object*)zend_object_store_get_object(object TSRMLS_CC);

	if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0
	&& !std_object_handlers.has_property(object, member, 2, key TSRMLS_CC)) {
		spl_array_write_dimension(object, member, value TSRMLS_CC);
		return;
	}
	std_object_handlers.write_property(object, member, value, key TSRMLS_CC);
} /* }}} */


static zend_object_value spl_SplObjectStorage_new(zend_class_entry *class_type TSRMLS_DC)
{
	spl_SplObjectStorage *tmp;
	return spl_object_storage_new_ex(class_type, &tmp, NULL TSRMLS_CC);
}