Exemplo n.º 1
0
static void test_removeall(abts_case *tc, void *data)
{
    apr_status_t rv;

    rv = apr_dir_remove("data/one/two/three", p);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);

    rv = apr_dir_remove("data/one/two", p);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);

    rv = apr_dir_remove("data/one", p);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
}
Exemplo n.º 2
0
/* Test for a (fixed) bug in apr_dir_read().  This bug only happened
   in threadless cases. */
static void test_uncleared_errno(abts_case *tc, void *data)
{
    apr_file_t *thefile = NULL;
    apr_finfo_t finfo;
    apr_int32_t finfo_flags = APR_FINFO_TYPE | APR_FINFO_NAME;
    apr_dir_t *this_dir;
    apr_status_t rv; 

    rv = apr_dir_make("dir1", APR_OS_DEFAULT, p);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
    rv = apr_dir_make("dir2", APR_OS_DEFAULT, p);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
    rv = apr_file_open(&thefile, "dir1/file1",
                       APR_FOPEN_READ | APR_FOPEN_WRITE | APR_FOPEN_CREATE, APR_OS_DEFAULT, p);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
    rv = apr_file_close(thefile);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);

    /* Try to remove dir1.  This should fail because it's not empty.
       However, on a platform with threads disabled (such as FreeBSD),
       `errno' will be set as a result. */
    rv = apr_dir_remove("dir1", p);
    ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ENOTEMPTY(rv));
    
    /* Read `.' and `..' out of dir2. */
    rv = apr_dir_open(&this_dir, "dir2", p);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
    rv = apr_dir_read(&finfo, finfo_flags, this_dir);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
    rv = apr_dir_read(&finfo, finfo_flags, this_dir);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);

    /* Now, when we attempt to do a third read of empty dir2, and the
       underlying system readdir() returns NULL, the old value of
       errno shouldn't cause a false alarm.  We should get an ENOENT
       back from apr_dir_read, and *not* the old errno. */
    rv = apr_dir_read(&finfo, finfo_flags, this_dir);
    ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ENOENT(rv));

    rv = apr_dir_close(this_dir);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
		 
    /* Cleanup */
    rv = apr_file_remove("dir1/file1", p);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
    rv = apr_dir_remove("dir1", p);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
    rv = apr_dir_remove("dir2", p);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);

}
Exemplo n.º 3
0
static void test_remove_notthere(abts_case *tc, void *data)
{
    apr_status_t rv;

    rv = apr_dir_remove("data/notthere", p);
    ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ENOENT(rv));
}
Exemplo n.º 4
0
static void test_removeall_fail(abts_case *tc, void *data)
{
    apr_status_t rv;

    rv = apr_dir_remove("data/one", p);
    ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ENOTEMPTY(rv));
}
Exemplo n.º 5
0
static void test_remove(abts_case *tc, void *data)
{
    apr_status_t rv;
    apr_finfo_t finfo;

    rv = apr_dir_remove("data/testdir", p);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);

    rv = apr_stat(&finfo, "data/testdir", APR_FINFO_TYPE, p);
    ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_ENOENT(rv));
}
Exemplo n.º 6
0
int lua_apr_dir_remove(lua_State *L)
{
  apr_status_t status;
  apr_pool_t *memory_pool;
  const char *filepath;

  memory_pool = to_pool(L);
  filepath = luaL_checkstring(L, 1);
  status = apr_dir_remove(filepath, memory_pool);

  return push_status(L, status);
}
Exemplo n.º 7
0
static void test_mkdir_twice(abts_case *tc, void *data)
{
    apr_status_t rv;

    rv = apr_dir_make("data/testdir", APR_UREAD | APR_UWRITE | APR_UEXECUTE, p);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);

    rv = apr_dir_make("data/testdir", APR_UREAD | APR_UWRITE | APR_UEXECUTE, p);
    ABTS_INT_EQUAL(tc, 1, APR_STATUS_IS_EEXIST(rv));

    rv = apr_dir_remove("data/testdir", p);
    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
}
void nx_expr_proc__xm_fileop_dir_remove(nx_expr_eval_ctx_t *eval_ctx,
					nx_module_t *module,
					nx_expr_arg_list_t *args)
{
    nx_expr_arg_t *arg;
    nx_value_t path;
    apr_status_t rv;
    apr_pool_t *pool;

    ASSERT(module != NULL);

    ASSERT(args != NULL);
    arg = NX_DLIST_FIRST(args);
    ASSERT(arg != NULL);
    ASSERT(arg->expr != NULL);

    nx_expr_evaluate(eval_ctx, &path, arg->expr);

    if ( path.defined != TRUE )
    {
	throw_msg("'path' is undef");
    }
    if ( path.type != NX_VALUE_TYPE_STRING )
    {
	nx_value_kill(&path);
	throw_msg("string type required for 'path'");
    }

    // TODO: remove contents before

    pool = nx_pool_create_core();
    rv = apr_dir_remove(path.string->buf, pool);
    if ( APR_STATUS_IS_ENOENT(rv) )
    {
    }
    else if ( rv == APR_SUCCESS )
    {
    }
    else
    {
	    log_aprerror(rv, "failed to remove directory '%s'", path.string->buf);
    }
    apr_pool_destroy(pool);
    nx_value_kill(&path);
}
Exemplo n.º 9
0
static void test_rmkdir_nocwd(abts_case *tc, void *data)
{
    char *cwd, *path;

    APR_ASSERT_SUCCESS(tc, "make temp dir",
                       apr_dir_make("dir3", APR_OS_DEFAULT, p));

    APR_ASSERT_SUCCESS(tc, "obtain cwd", apr_filepath_get(&cwd, 0, p));

    APR_ASSERT_SUCCESS(tc, "determine path to temp dir",
                       apr_filepath_merge(&path, cwd, "dir3", 0, p));

    APR_ASSERT_SUCCESS(tc, "change to temp dir", apr_filepath_set(path, p));

    APR_ASSERT_SUCCESS(tc, "restore cwd", apr_filepath_set(cwd, p));

    APR_ASSERT_SUCCESS(tc, "remove cwd", apr_dir_remove(path, p));
}
Exemplo n.º 10
0
term_t bif_del_dir1(term_t Dir, process_t *ctx)
{
	apr_status_t rs;
	apr_pool_t *tmp;
	const char *path;

	if (!is_string(Dir))
		return A_BADARG;

	apr_pool_create(&tmp, 0);
	path = ltoz(Dir, tmp);
	rs = apr_dir_remove(path, tmp);
	if (rs == 0)
		result(A_OK);
	else
		result(make_tuple2(A_ERROR, decipher_status(rs), proc_gc_pool(ctx)));

	apr_pool_destroy(tmp);
	return AI_OK;
}
Exemplo n.º 11
0
static int remove_url(cache_handle_t *h, apr_pool_t *p)
{
    apr_status_t rc;
    disk_cache_object_t *dobj;

    /* Get disk cache object from cache handle */
    dobj = (disk_cache_object_t *) h->cache_obj->vobj;
    if (!dobj) {
        return DECLINED;
    }

    /* Delete headers file */
    if (dobj->hdrsfile) {
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
                     "disk_cache: Deleting %s from cache.", dobj->hdrsfile);

        rc = apr_file_remove(dobj->hdrsfile, p);
        if ((rc != APR_SUCCESS) && !APR_STATUS_IS_ENOENT(rc)) {
            /* Will only result in an output if httpd is started with -e debug.
             * For reason see log_error_core for the case s == NULL.
             */
            ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, NULL,
                   "disk_cache: Failed to delete headers file %s from cache.",
                         dobj->hdrsfile);
            return DECLINED;
        }
    }

     /* Delete data file */
    if (dobj->datafile) {
        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
                     "disk_cache: Deleting %s from cache.", dobj->datafile);

        rc = apr_file_remove(dobj->datafile, p);
        if ((rc != APR_SUCCESS) && !APR_STATUS_IS_ENOENT(rc)) {
            /* Will only result in an output if httpd is started with -e debug.
             * For reason see log_error_core for the case s == NULL.
             */
            ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, NULL,
                      "disk_cache: Failed to delete data file %s from cache.",
                         dobj->datafile);
            return DECLINED;
        }
    }

    /* now delete directories as far as possible up to our cache root */
    if (dobj->root) {
        const char *str_to_copy;

        str_to_copy = dobj->hdrsfile ? dobj->hdrsfile : dobj->datafile;
        if (str_to_copy) {
            char *dir, *slash, *q;

            dir = apr_pstrdup(p, str_to_copy);

            /* remove filename */
            slash = strrchr(dir, '/');
            *slash = '\0';

            /*
             * now walk our way back to the cache root, delete everything
             * in the way as far as possible
             *
             * Note: due to the way we constructed the file names in
             * header_file and data_file, we are guaranteed that the
             * cache_root is suffixed by at least one '/' which will be
             * turned into a terminating null by this loop.  Therefore,
             * we won't either delete or go above our cache root.
             */
            for (q = dir + dobj->root_len; *q ; ) {
                 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
                              "disk_cache: Deleting directory %s from cache",
                              dir);

                 rc = apr_dir_remove(dir, p);
                 if (rc != APR_SUCCESS && !APR_STATUS_IS_ENOENT(rc)) {
                    break;
                 }
                 slash = strrchr(q, '/');
                 *slash = '\0';
            }
        }
    }

    return OK;
}
Exemplo n.º 12
0
/*
 * walk the cache directory tree
 */
static int process_dir(char *path, apr_pool_t *pool)
{
    apr_dir_t *dir;
    apr_pool_t *p;
    apr_hash_t *h;
    apr_hash_index_t *i;
    apr_file_t *fd;
    apr_status_t status;
    apr_finfo_t info;
    apr_size_t len;
    apr_time_t current, deviation;
    char *nextpath, *base, *ext, *orig_basename;
    APR_RING_ENTRY(_direntry) anchor;
    DIRENTRY *d, *t, *n;
    ENTRY *e;
    int skip, retries;
    disk_cache_info_t disk_info;

    APR_RING_INIT(&anchor, _direntry, link);
    apr_pool_create(&p, pool);
    h = apr_hash_make(p);
    fd = NULL;
    skip = 0;
    deviation = MAXDEVIATION * APR_USEC_PER_SEC;

    if (apr_dir_open(&dir, path, p) != APR_SUCCESS) {
        return 1;
    }

    while (apr_dir_read(&info, 0, dir) == APR_SUCCESS && !interrupted) {
        if (!strcmp(info.name, ".") || !strcmp(info.name, "..")) {
            continue;
        }
        d = apr_pcalloc(p, sizeof(DIRENTRY));
        d->basename = apr_pstrcat(p, path, "/", info.name, NULL);
        APR_RING_INSERT_TAIL(&anchor, d, _direntry, link);
    }

    apr_dir_close(dir);

    if (interrupted) {
        return 1;
    }

    skip = baselen + 1;

    for (d = APR_RING_FIRST(&anchor);
         !interrupted && d != APR_RING_SENTINEL(&anchor, _direntry, link);
         d=n) {
        n = APR_RING_NEXT(d, link);
        base = strrchr(d->basename, '/');
        if (!base++) {
            base = d->basename;
        }
        ext = strchr(base, '.');

        /* there may be temporary files which may be gone before
         * processing, always skip these if not in realclean mode
         */
        if (!ext && !realclean) {
            if (!strncasecmp(base, AP_TEMPFILE_BASE, AP_TEMPFILE_BASELEN)
                && strlen(base) == AP_TEMPFILE_NAMELEN) {
                continue;
            }
        }

        /* this may look strange but apr_stat() may return errno which
         * is system dependent and there may be transient failures,
         * so just blindly retry for a short while
         */
        retries = STAT_ATTEMPTS;
        status = APR_SUCCESS;
        do {
            if (status != APR_SUCCESS) {
                apr_sleep(STAT_DELAY);
            }
            status = apr_stat(&info, d->basename, DIRINFO, p);
        } while (status != APR_SUCCESS && !interrupted && --retries);

        /* what may happen here is that apache did create a file which
         * we did detect but then does delete the file before we can
         * get file information, so if we don't get any file information
         * we will ignore the file in this case
         */
        if (status != APR_SUCCESS) {
            if (!realclean && !interrupted) {
                continue;
            }
            return 1;
        }

        if (info.filetype == APR_DIR) {
            /* Make a copy of the basename, as process_dir modifies it */
            orig_basename = apr_pstrdup(pool, d->basename);
            if (process_dir(d->basename, pool)) {
                return 1;
            }

            /* If asked to delete dirs, do so now. We don't care if it fails.
             * If it fails, it likely means there was something else there.
             */
            if (deldirs && !dryrun) {
                apr_dir_remove(orig_basename, pool);
            }
            continue;
        }

        if (info.filetype != APR_REG) {
            continue;
        }

        if (!ext) {
            if (!strncasecmp(base, AP_TEMPFILE_BASE, AP_TEMPFILE_BASELEN)
                && strlen(base) == AP_TEMPFILE_NAMELEN) {
                d->basename += skip;
                d->type = TEMP;
                d->dsize = info.size;
                apr_hash_set(h, d->basename, APR_HASH_KEY_STRING, d);
            }
            continue;
        }

        if (!strcasecmp(ext, CACHE_HEADER_SUFFIX)) {
            *ext = '\0';
            d->basename += skip;
            /* if a user manually creates a '.header' file */
            if (d->basename[0] == '\0') {
                continue;
            }
            t = apr_hash_get(h, d->basename, APR_HASH_KEY_STRING);
            if (t) {
                d = t;
            }
            d->type |= HEADER;
            d->htime = info.mtime;
            d->hsize = info.size;
            apr_hash_set(h, d->basename, APR_HASH_KEY_STRING, d);
            continue;
        }

        if (!strcasecmp(ext, CACHE_DATA_SUFFIX)) {
            *ext = '\0';
            d->basename += skip;
            /* if a user manually creates a '.data' file */
            if (d->basename[0] == '\0') {
                continue;
            }
            t = apr_hash_get(h, d->basename, APR_HASH_KEY_STRING);
            if (t) {
                d = t;
            }
            d->type |= DATA;
            d->dtime = info.mtime;
            d->dsize = info.size;
            apr_hash_set(h, d->basename, APR_HASH_KEY_STRING, d);
        }
    }

    if (interrupted) {
        return 1;
    }

    path[baselen] = '\0';

    for (i = apr_hash_first(p, h); i && !interrupted; i = apr_hash_next(i)) {
        void *hvalue;
        apr_uint32_t format;

        apr_hash_this(i, NULL, NULL, &hvalue);
        d = hvalue;

        switch(d->type) {
        case HEADERDATA:
            nextpath = apr_pstrcat(p, path, "/", d->basename,
                                   CACHE_HEADER_SUFFIX, NULL);
            if (apr_file_open(&fd, nextpath, APR_FOPEN_READ | APR_FOPEN_BINARY,
                              APR_OS_DEFAULT, p) == APR_SUCCESS) {
                len = sizeof(format);
                if (apr_file_read_full(fd, &format, len,
                                       &len) == APR_SUCCESS) {
                    if (format == DISK_FORMAT_VERSION) {
                        apr_off_t offset = 0;

                        apr_file_seek(fd, APR_SET, &offset);

                        len = sizeof(disk_cache_info_t);

                        if (apr_file_read_full(fd, &disk_info, len,
                                               &len) == APR_SUCCESS) {
                            apr_file_close(fd);
                            e = apr_palloc(pool, sizeof(ENTRY));
                            APR_RING_INSERT_TAIL(&root, e, _entry, link);
                            e->expire = disk_info.expire;
                            e->response_time = disk_info.response_time;
                            e->htime = d->htime;
                            e->dtime = d->dtime;
                            e->hsize = d->hsize;
                            e->dsize = d->dsize;
                            e->basename = apr_pstrdup(pool, d->basename);
                            break;
                        }
                        else {
                            apr_file_close(fd);
                        }
                    }
                    else if (format == VARY_FORMAT_VERSION) {
                        /* This must be a URL that added Vary headers later,
                         * so kill the orphaned .data file
                         */
                        apr_file_close(fd);
                        apr_file_remove(apr_pstrcat(p, path, "/", d->basename,
                                                    CACHE_DATA_SUFFIX, NULL),
                                        p);
                    }
                }
                else {
                    apr_file_close(fd);
                }

            }
            /* we have a somehow unreadable headers file which is associated
             * with a data file. this may be caused by apache currently
             * rewriting the headers file. thus we may delete the file set
             * either in realclean mode or if the headers file modification
             * timestamp is not within a specified positive or negative offset
             * to the current time.
             */
            current = apr_time_now();
            if (realclean || d->htime < current - deviation
                || d->htime > current + deviation) {
                delete_entry(path, d->basename, p);
                unsolicited += d->hsize;
                unsolicited += d->dsize;
            }
            break;

        /* single data and header files may be deleted either in realclean
         * mode or if their modification timestamp is not within a
         * specified positive or negative offset to the current time.
         * this handling is necessary due to possible race conditions
         * between apache and this process
         */
        case HEADER:
            current = apr_time_now();
            nextpath = apr_pstrcat(p, path, "/", d->basename,
                                   CACHE_HEADER_SUFFIX, NULL);
            if (apr_file_open(&fd, nextpath, APR_FOPEN_READ | APR_FOPEN_BINARY,
                              APR_OS_DEFAULT, p) == APR_SUCCESS) {
                len = sizeof(format);
                if (apr_file_read_full(fd, &format, len,
                                       &len) == APR_SUCCESS) {
                    if (format == VARY_FORMAT_VERSION) {
                        apr_time_t expires;

                        len = sizeof(expires);

                        apr_file_read_full(fd, &expires, len, &len);

                        apr_file_close(fd);

                        if (expires < current) {
                            delete_entry(path, d->basename, p);
                        }
                        break;
                    }
                }
                apr_file_close(fd);
            }

            if (realclean || d->htime < current - deviation
                || d->htime > current + deviation) {
                delete_entry(path, d->basename, p);
                unsolicited += d->hsize;
            }
            break;

        case DATA:
            current = apr_time_now();
            if (realclean || d->dtime < current - deviation
                || d->dtime > current + deviation) {
                delete_entry(path, d->basename, p);
                unsolicited += d->dsize;
            }
            break;

        /* temp files may only be deleted in realclean mode which
         * is asserted above if a tempfile is in the hash array
         */
        case TEMP:
            delete_file(path, d->basename, p);
            unsolicited += d->dsize;
            break;
        }
    }

    if (interrupted) {
        return 1;
    }

    apr_pool_destroy(p);

    if (benice) {
        apr_sleep(NICE_DELAY);
    }

    if (interrupted) {
        return 1;
    }

    return 0;
}
Exemplo n.º 13
0
void test_client_teardown(CuTest *tc) {
    aos_pool_t *p;
    aos_pool_create(&p, NULL);
    apr_dir_remove(TEST_DIR"/data/", p);
    aos_pool_destroy(p);    
}
	bool remove(const std::string& path){
		return APR_SUCCESS == check_apr(apr_dir_remove(path.c_str(), mPool));
	}
Exemplo n.º 15
0
int lua_apr_dir_remove_recursive(lua_State *L)
{
  apr_status_t status;
  apr_pool_t *outer_pool; /* used to store todo/done arrays and directory pathnames */
  apr_pool_t *middle_pool; /* used to store directory handles (apr_dir_t structs) */
  apr_pool_t *inner_pool; /* used to store pathnames of non-subdirectory entries */
  apr_array_header_t *todo, *done;
  apr_dir_t *directory;
  apr_finfo_t info;
  char **top, *tmp;
  const char *filepath;
  int allocation_counter;

  directory = NULL;
  outer_pool = middle_pool = inner_pool = NULL;
  filepath = luaL_checkstring(L, 1);
  allocation_counter = 0;

  status = apr_pool_create(&outer_pool, NULL);
  if (APR_SUCCESS == status)
    status = apr_pool_create(&middle_pool, NULL);
  if (APR_SUCCESS == status)
    status = apr_pool_create(&inner_pool, NULL);
  if (APR_SUCCESS != status)
    goto cleanup;

# define out_of_memory do { \
    status = APR_ENOMEM; \
    goto cleanup; \
  } while (0)

# define push_filepath(stack, filepath) do { \
    const char **p = apr_array_push(stack); \
    if (p != NULL) *p = filepath; else out_of_memory; \
  } while (0)

  todo = apr_array_make(outer_pool, 0, sizeof filepath);
  done = apr_array_make(outer_pool, 0, sizeof filepath);
  if (todo == NULL || done == NULL)
    out_of_memory;

  push_filepath(todo, filepath);

  while ((top = apr_array_pop(todo))) {
    filepath = *(char**)top;
    apr_pool_clear(middle_pool);
    status = apr_dir_open(&directory, filepath, middle_pool);
    if (status != APR_SUCCESS) {
      directory = NULL;
      goto cleanup;
    }
    for (;;) {
      /* This is a compromise between having `inner_pool' grow almost unbounded
       * on very large directories (e.g. ~/Maildir/) and clearing it for every
       * non-subdirectory pathname that's allocated (very inefficient). */
      if (allocation_counter % 1000 == 0)
        apr_pool_clear(inner_pool);
      /* FIXME?! apr_dir_read() uses directory->pool (middle_pool) for allocation */
      status = apr_dir_read(&info, APR_FINFO_NAME|APR_FINFO_TYPE|APR_FINFO_LINK, directory);
      if (APR_STATUS_IS_ENOENT(status))
        break; /* no more entries */
      else if (status != APR_SUCCESS && status != APR_INCOMPLETE)
        goto cleanup; /* something went wrong */
      else if (filename_symbolic(info.name))
        continue; /* bogus entry */
      else if (info.filetype == APR_DIR) {
        /* recurse into subdirectory */
        status = apr_filepath_merge(&tmp, filepath, info.name, 0, outer_pool);
        if (status != APR_SUCCESS)
          goto cleanup;
        push_filepath(todo, tmp);
      } else {
        /* delete non-subdirectory entry */
        status = apr_filepath_merge(&tmp, filepath, info.name, 0, inner_pool);
        allocation_counter++;
        if (APR_SUCCESS == status)
          status = apr_file_remove(tmp, inner_pool);
        if (APR_SUCCESS != status)
          goto cleanup;
      }
    }
    status = apr_dir_close(directory);
    directory = NULL;
    if (status != APR_SUCCESS)
      goto cleanup;
    push_filepath(done, filepath);
  }

# undef out_of_memory
# undef push_filepath

  while ((top = apr_array_pop(done))) {
    filepath = *(char**)top;
    if (allocation_counter++ % 100 == 0)
      apr_pool_clear(middle_pool);
    status = apr_dir_remove(filepath, middle_pool);
    if (status != APR_SUCCESS)
      goto cleanup;
  }

cleanup:

  if (directory != NULL)
    apr_dir_close(directory);
  if (inner_pool != NULL)
    apr_pool_destroy(inner_pool);
  if (middle_pool != NULL)
    apr_pool_destroy(middle_pool);
  if (outer_pool != NULL)
    apr_pool_destroy(outer_pool);

  return push_status(L, status);
}