Skip to content

File es_posix.c

File List > cubrid > src > storage > es_posix.c

Go to the documentation of this file

/*
 * Copyright 2008 Search Solution Corporation
 * Copyright 2016 CUBRID Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

/*
 * es_posix.c - POSIX FS API for external storage supports (at server)
 */

#include "config.h"

#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#if defined(WINDOWS)
#include <io.h>
#define S_ISDIR(m) ((m) & S_IFDIR)
typedef int mode_t;
#else
#include <unistd.h>
#include <sys/vfs.h>
#include <string.h>
#endif /* !WINDOWS */

#include "porting.h"
#include "thread_compat.hpp"
#include "error_manager.h"
#include "system_parameter.h"
#include "error_code.h"
#include "es_posix.h"
#if defined (SERVER_MODE)
#include "thread_entry.hpp"
#include "thread_manager.hpp"   // for thread_get_thread_entry_info
#endif // SERVER_MODE
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"

#define ES_POSIX_COPY_BUFSIZE       (4096 * 4)  /* 16K */

#if defined (SA_MODE) || defined (SERVER_MODE)
/* es_posix_base_dir - */
char es_base_dir[PATH_MAX] = { 0 };

static void es_get_unique_name (char *dirname1, char *dirname2, const char *metaname, char *filename);
static void es_rename_path (const char *src, char *tgt, char *metaname);

static int es_abs_open (const char *abs_path, int flags);
static int es_abs_open (const char *abs_path, int flags, mode_t mode);
static int es_make_abs_path (char *abs_path, const char *src_path);
static int es_os_rename_file_abs (const char *src, const char *dst);


/*
 * es_posix_get_unique_name - make unique path string for external file
 *
 * return: none
 * dirname1(out): first level directory name which is generated
 * dirname2(out): second level directory name which is generated
 * filename(out): generated file name
 */
static void
es_get_unique_name (char *dirname1, char *dirname2, const char *metaname, char *filename)
{
  UINT64 unum;
  int hashval;
  int r;

#if defined(SERVER_MODE)
  THREAD_ENTRY *thread_p;

  thread_p = thread_get_thread_entry_info ();
  assert (thread_p != NULL);

  r = rand_r (&thread_p->rand_seed);
#else
  r = rand ();
#endif

  /* defensive, we don't want minus in filename */
  r = r < 0 ? r * (-1) : r;

  /* get unique numbers */
  unum = es_get_unique_num ();

  /* make a file name & a dir name */
  snprintf (filename, NAME_MAX, "%s.%020llu_%04d", metaname, (unsigned long long) unum, r % 10000);

  hashval = es_name_hash_func (ES_POSIX_HASH1, filename);
  snprintf (dirname1, NAME_MAX, "ces_%03d", hashval);

  hashval = es_name_hash_func (ES_POSIX_HASH2, filename);
  snprintf (dirname2, NAME_MAX, "ces_%03d", hashval);
}

/*
 * es_posix_make_dirs -
 *
 * return: error code, ER_ES_GENERAL or NO_ERROR
 * dirname1(in): first level directory name
 * dirname2(in): second level directory name
 */
int
es_make_dirs (const char *dirname1, const char *dirname2)
{
  char dirbuf[PATH_MAX];
  int ret;

#if defined (CUBRID_OWFS_POSIX_TWO_DEPTH_DIRECTORY)
retry:
  if (snprintf (dirbuf, PATH_MAX - 1, "%s%c%s%c%s", es_base_dir, PATH_SEPARATOR, dirname1, PATH_SEPARATOR, dirname2)
      < 0)
    {
      assert (false);
      return ER_ES_INVALID_PATH;
    }
  ret = mkdir (dirbuf, 0755);
  if (ret < 0 && errno == ENOENT)
    {
      n = snprintf (dirbuf, PATH_MAX - 1, "%s%c%s", es_base_dir, PATH_SEPARATOR, dirname1);
      ret = mkdir (dirbuf, 0755);
      if (ret == 0 || errno == EEXIST)
    {
      goto retry;
    }
    }
#else
  if (snprintf (dirbuf, PATH_MAX - 1, "%s%c%s", es_base_dir, PATH_SEPARATOR, dirname1) < 0)
    {
      assert (false);
      return ER_ES_INVALID_PATH;
    }
  ret = mkdir (dirbuf, 0744);
#endif

  if (ret < 0 && errno != EEXIST)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", dirbuf);
      return ER_ES_GENERAL;
    }
  return NO_ERROR;
}

/*
 * es_rename_path -
 *
 * return:
 * src(in): source path to be converted
 * metaname(in): new file name hint replacing a part of the src
 * tgt(out): target path
 */
static void
es_rename_path (const char *src, char *tgt, char *metaname)
{
  const char *s;
  char *t;

  assert (metaname != NULL);

  /*
   * src: /.../ces_000/ces_tmp.123456789
   *                  ^
   *                  s
   */
  s = strrchr (src, PATH_SEPARATOR);
  assert (s != NULL);
  strcpy (tgt, src);
  if (s == NULL)
    {
      return;
    }
  /*
   * tgt: /.../ces_000/ces_tmp.123456789
   *                  ^
   *                  t
   */
  t = tgt + (s - src) + 1;

  /*
   * tgt: /.../ces_000/ces_tmp.123456789
   *                          ^
   *                          s
   */
  s = strchr (s, '.');
  assert (s != NULL);
  if (s == NULL)
    {
      return;
    }

  sprintf (t, "%s%s", metaname, s);
}

/*
 * es_abs_open -
 *
 * return: return code of file open
 * path(in): file path for open, relative or absolute
 *           if relative, (lob_base_path) + path will be opened
 */

static int
es_abs_open (const char *path, int flags)
{
  const char *abs_path = path;
  char path_buf[PATH_MAX];
  int ret = NO_ERROR;

  if ((ret = es_make_abs_path (path_buf, path)) < 0)
    {
      return ret;
    }

  if (ret > 0)
    {
      abs_path = path_buf;
    }

  return open (abs_path, flags);
}

static int
es_abs_open (const char *path, int flags, mode_t mode)
{
  const char *abs_path = path;
  char path_buf[PATH_MAX];
  int ret = NO_ERROR;

  if ((ret = es_make_abs_path (path_buf, path)) < 0)
    {
      return ret;
    }

  if (ret > 0)
    {
      abs_path = path_buf;
    }

  return open (abs_path, flags, mode);
}

/*
 * es_make_abs_path -
 *
 * return: return code, 0: already absolute path
 * path(in): file path for open, relative or absolute
 *           if relative, (lob_base_path) + path will be opened
 */
static int
es_make_abs_path (char *dst, const char *src)
{
  int ret = 0;

  if ((IS_ABS_PATH (src)) == false)
    {
      ret = snprintf (dst, PATH_MAX, "%s%c%s", es_base_dir, PATH_SEPARATOR, src);
    }

  return ret;
}
#endif /* SA_MODE || SERVER_MODE */


/*
 * es_posix_init - initialize posix module
 *         set the directory for external files
 *
 * return: error code, ER_ES_GENERAL or NO_ERRROR
 * basedir(in): base directory path
 */
int
es_posix_init (const char *base_path)
{
#if defined(SA_MODE) || defined(SERVER_MODE)
  int ret;
  struct stat sbuf;

  ret = stat (base_path, &sbuf);
  if (ret != 0 || !S_ISDIR (sbuf.st_mode))
    {
      /* failed to open base dir */
      er_set_with_oserror (ER_NOTIFICATION_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", base_path);
      return ER_ES_GENERAL;
    }

  /* set base dir */
  strlcpy (es_base_dir, base_path, PATH_MAX);
  return NO_ERROR;
#else /* SA_MODE || SERVER_MODE */
  return NO_ERROR;
#endif /* CS_MODE */
}

/*
 * es_posix_final - finalize posix module
 *
 * return: none
 */
void
es_posix_final (void)
{
  return;
}

#if defined(SA_MODE) || defined(SERVER_MODE)
/*
 * xes_posix_create_file - create a new external file with auto generated name
 *
 * return: error code, ER_ES_GENERAL or NO_ERRROR
 * new_path(out): file path newly created
 */
int
xes_posix_create_file (char *new_path)
{
  int fd;
  int ret, n;
  char dirname1[NAME_MAX], dirname2[NAME_MAX], filename[NAME_MAX];

retry:
  es_get_unique_name (dirname1, dirname2, "ces_temp", filename);
#if defined (CUBRID_OWFS_POSIX_TWO_DEPTH_DIRECTORY)
  n = snprintf (new_path, PATH_MAX - 1, "%s%c%s%c%s%c%s", es_base_dir, PATH_SEPARATOR, dirname1, PATH_SEPARATOR,
        dirname2, PATH_SEPARATOR, filename);
#else
  /* default */
  n = snprintf (new_path, PATH_MAX - 1, "%s%c%s", dirname1, PATH_SEPARATOR, filename);
#endif
  if (n < 0)
    {
      assert (false);
      return ER_ES_INVALID_PATH;
    }

  es_log ("xes_posix_create_file(): %s\n", new_path);

#if defined (WINDOWS)
  fd = es_abs_open (new_path, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, S_IRWXU);
#else /* WINDOWS */
  fd = es_abs_open (new_path, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_LARGEFILE);
#endif /* !WINDOWS */
  if (fd < 0)
    {
      if (errno == ENOENT)
    {
      ret = es_make_dirs (dirname1, dirname2);
      if (ret != NO_ERROR)
        {
          return ret;
        }
#if defined (WINDOWS)
      fd = es_abs_open (new_path, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, S_IRWXU);
#else /* WINDOWs */
      fd = es_abs_open (new_path, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_LARGEFILE);
#endif /* !WINDOWS */
    }
    }

  if (fd < 0)
    {
      if (errno == EEXIST)
    {
      goto retry;
    }
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", new_path);
      return ER_ES_GENERAL;
    }
  close (fd);
  return NO_ERROR;
}

/*
 * xes_posix_write_file - write to the external file
 *
 * return: error code, ER_ES_GENERAL or NO_ERRROR
 * path(in): file path
 */
ssize_t
xes_posix_write_file (const char *path, const void *buf, size_t count, off_t offset)
{
  struct stat pstat;
  int fd;
  ssize_t nbytes;
  size_t total = 0;
  const char *abs_path = path;
  char path_buf[PATH_MAX];

  if (es_make_abs_path (path_buf, path) > 0)
    {
      abs_path = path_buf;
    }

  es_log ("xes_posix_write_file(%s, count %d offset %ld)\n", abs_path, count, offset);

  /*
   * TODO: This block of codes prevents partial update or writing at advanced
   * position or something like that.
   * This restriction is introduced due to OwFS's capability.
   * We need to reconsider about this specification.
   */
  if (stat (abs_path, &pstat) < 0)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", abs_path);
      return ER_ES_GENERAL;
    }
  if (offset != pstat.st_size)
    {
      char buf[PATH_MAX];
      snprintf (buf, PATH_MAX, "offset error %s", path);
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", buf);
      return ER_ES_GENERAL;
    }

#if defined (WINDOWS)
  fd = es_abs_open (abs_path, O_WRONLY | O_APPEND | O_BINARY, S_IRWXU);
#else /* WINDOWs */
  fd = es_abs_open (abs_path, O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_LARGEFILE);
#endif /* !WINDOWS */
  if (fd < 0)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", path);
      return ER_ES_GENERAL;
    }

  while (count > 0)
    {
      if (lseek (fd, offset, SEEK_SET) != offset)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", path);
      close (fd);
      return ER_ES_GENERAL;
    }

      nbytes = write (fd, buf, (unsigned) count);
      if (nbytes <= 0)
    {
      switch (errno)
        {
        case EINTR:
        case EAGAIN:
          continue;
        default:
          {
        er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", path);
        close (fd);
        return ER_ES_GENERAL;
          }
        }
    }

      offset += nbytes;
      count -= nbytes;
      buf = (char *) buf + nbytes;
      total += nbytes;
    }
  close (fd);

  return total;
}

/*
 * xes_posix_read_file - read from the external file
 *
 * return: error code, ER_ES_GENERAL or NO_ERRROR
 * path(in): file path
 */
ssize_t
xes_posix_read_file (const char *path, void *buf, size_t count, off_t offset)
{
  int fd;
  ssize_t nbytes;
  size_t total = 0;

  es_log ("xes_posix_read_file(%s, count %d offset %ld)\n", path, count, offset);

#if defined (WINDOWS)
  fd = es_abs_open (path, O_RDONLY | O_BINARY);
#else /* WINDOWS */
  fd = es_abs_open (path, O_RDONLY | O_LARGEFILE);
#endif /* !WINDOWS */
  if (fd < 0)
    {
      if (errno == ENOENT)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_FILE_NOT_FOUND, 1, path);
      return ER_ES_FILE_NOT_FOUND;
    }
      else
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", path);
      return ER_ES_GENERAL;
    }
    }

  while (count > 0)
    {
      if (lseek (fd, offset, SEEK_SET) != offset)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", path);
      close (fd);
      return ER_ES_GENERAL;
    }

      nbytes = read (fd, buf, (unsigned) count);
      if (nbytes < 0)
    {
      switch (errno)
        {
        case EINTR:
        case EAGAIN:
          continue;
        default:
          {
        er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", path);
        close (fd);
        return ER_ES_GENERAL;
          }
        }
    }
      if (nbytes == 0)
    {
      break;
    }

      offset += nbytes;
      count -= nbytes;
      buf = (char *) buf + nbytes;
      total += nbytes;
    }
  close (fd);

  return total;
}

/*
 * xes_posix_delete_file - delete the external file
 *
 * return: error code, ER_ES_GENERAL or NO_ERRROR
 * path(in): file path
 */
int
xes_posix_delete_file (const char *path)
{
  int ret;
  const char *abs_path = path;
  char buf[PATH_MAX];

  if (es_make_abs_path (buf, path) > 0)
    {
      abs_path = buf;
    }

  es_log ("xes_posix_delete_file(%s)\n", abs_path);

  ret = unlink (abs_path);
  if (ret < 0)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", abs_path);
      return ER_ES_GENERAL;
    }

  return NO_ERROR;
}

/*
 * xes_posix_copy_file - copy the external file to new one
 *
 * return: error code, ER_ES_GENERAL or NO_ERRROR
 * src_path(in): file path to be copied
 * new_path(out): file path newly created
 */
int
xes_posix_copy_file (const char *src_path, char *metaname, char *new_path)
{
  int rd_fd, wr_fd, n;
  ssize_t ret;
  char dirname1[NAME_MAX], dirname2[NAME_MAX], filename[NAME_MAX];
  char buf[ES_POSIX_COPY_BUFSIZE];

  /* open a source file */
#if defined (WINDOWS)
  rd_fd = es_abs_open (src_path, O_RDONLY | O_BINARY);
#else /* WINDOWS */
  rd_fd = es_abs_open (src_path, O_RDONLY | O_LARGEFILE);
#endif /* !WINDOWS */
  if (rd_fd < 0)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", src_path);
      return ER_ES_GENERAL;
    }

retry:
  /* create a target file */
  es_get_unique_name (dirname1, dirname2, metaname, filename);
#if defined (CUBRID_OWFS_POSIX_TWO_DEPTH_DIRECTORY)
  n = snprintf (new_path, PATH_MAX - 1, "%s%c%s%c%s%c%s", es_base_dir, PATH_SEPARATOR, dirname1, PATH_SEPARATOR,
        dirname2, PATH_SEPARATOR, filename);
#else
  /* default */
  n = snprintf (new_path, PATH_MAX - 1, "%s%c%s", dirname1, PATH_SEPARATOR, filename);
#endif
  if (n < 0)
    {
      close (rd_fd);

      assert (false);
      return ER_ES_INVALID_PATH;
    }

  es_log ("xes_posix_copy_file(%s, %s): %s\n", src_path, metaname, new_path);

#if defined (WINDOWS)
  wr_fd = es_abs_open (new_path, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, S_IRWXU);
#else /* WINDOWS */
  wr_fd = es_abs_open (new_path, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_LARGEFILE);
#endif /* !WINDOWS */
  if (wr_fd < 0)
    {
      if (errno == ENOENT)
    {
      ret = es_make_dirs (dirname1, dirname2);
      if (ret != NO_ERROR)
        {
          close (rd_fd);
          return ER_ES_GENERAL;
        }
#if defined (WINDOWS)
      wr_fd = es_abs_open (new_path, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, S_IRWXU);
#else /* WINDOWS */
      wr_fd =
        es_abs_open (new_path, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_LARGEFILE);
#endif /* !WINDOWS */
    }
    }

  if (wr_fd < 0)
    {
      if (errno == EEXIST)
    {
      goto retry;
    }
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", new_path);
      close (rd_fd);
      return ER_ES_GENERAL;
    }

  /* copy data */
  do
    {
      ret = read (rd_fd, buf, ES_POSIX_COPY_BUFSIZE);
      if (ret == 0)
    {
      break;        /* end of file */
    }
      else if (ret < 0)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", src_path);
      break;
    }

      ret = write (wr_fd, buf, (unsigned) ret);
      if (ret <= 0)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", new_path);
      break;
    }
    }
  while (ret > 0);

  close (rd_fd);
  close (wr_fd);

  return (ret < 0) ? ER_ES_GENERAL : NO_ERROR;
}

/*
 * xes_posix_copy_file_with_prefix - Similar to xes_posix_copy_file(), but handles only the ES_POSIX type and performs file copy
                              while adding a prefix to the destination path.
 *
 * return: error code
 * src_path(in): path of the original source file
 * metaname(in) : meta name combined with in_uri
 * prefix(in): prefix that will be added to the destination path when copying
 * new_path(out): new path of the copied file
 */
int
xes_posix_copy_file_with_prefix (const char *src_path, char *metaname, const char *prefix, char *new_path)
{
  int rd_fd, wr_fd, n = 0;
  ssize_t ret;
  char dirname1[NAME_MAX], dirname2[NAME_MAX], filename[NAME_MAX];
  char buf[ES_POSIX_COPY_BUFSIZE];
  char *p;

  /* Check the existence of the source file by trying to open it */
  rd_fd = es_abs_open (src_path, O_RDONLY | O_LARGEFILE);
  if (rd_fd < 0)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", src_path);
      return ER_ES_GENERAL;
    }

retry:
  /* create a target file */
  es_get_unique_name (dirname1, dirname2, metaname, filename);

  n = snprintf (new_path, PATH_MAX - 1, "%s%c%s%c%s", prefix, PATH_SEPARATOR, dirname1, PATH_SEPARATOR, filename);
  if (n < 0 || n >= PATH_MAX - 1)
    {
      close (rd_fd);
      return ER_ES_INVALID_PATH;
    }

  es_log ("xes_posix_copy_file_with_prefix(%s, %s): %s\n", src_path, metaname, new_path);

  /* check file existence */
  wr_fd = es_abs_open (new_path, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_LARGEFILE);
  if (wr_fd < 0)
    {
      if (errno == ENOENT)
    {
      p = strrchr (new_path, PATH_SEPARATOR);
      if (p != NULL)
        {
          *p = '\0';    /* Temporarily truncate the path to extract the directory portion */
        }
      else
        {
          close (rd_fd);
          return ER_ES_GENERAL;
        }

      ret = es_make_dirs (new_path, dirname2);
      if (ret != NO_ERROR)
        {
          close (rd_fd);

          er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", src_path);
          return ER_ES_GENERAL;
        }

      *p = PATH_SEPARATOR;
      wr_fd =
        es_abs_open (new_path, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_LARGEFILE);
    }
    }

  if (wr_fd < 0)
    {
      if (errno == EEXIST)
    {
      goto retry;
    }
      close (rd_fd);

      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", new_path);
      return ER_ES_GENERAL;
    }

  /* copy data */
  do
    {
      ret = read (rd_fd, buf, ES_POSIX_COPY_BUFSIZE);
      if (ret == 0)
    {
      break;        /* end of file */
    }
      else if (ret < 0)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", src_path);
      break;
    }

      ret = write (wr_fd, buf, (unsigned) ret);
      if (ret <= 0)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", new_path);
      break;
    }
    }
  while (ret > 0);

  close (rd_fd);
  close (wr_fd);

  return (ret < 0) ? ER_ES_GENERAL : NO_ERROR;
}

/*
 * xes_posix_rename_file - convert a locator & file path according to the metaname
 *
 * return: error code, ER_ES_GENERAL or NO_ERRROR
 * src_path(in): file path to rename
 * metaname(in) : meta name combined with src_path
 * new_path(out): new file path
 */
int
xes_posix_rename_file (const char *src_path, const char *metaname, char *new_path)
{
  int ret;

  es_rename_path ((char *) src_path, new_path, (char *) metaname);

  es_log ("xes_posix_rename_file(%s, %s): %s\n", src_path, metaname, new_path);

  ret = es_os_rename_file_abs (src_path, new_path);

  return (ret < 0) ? ER_ES_GENERAL : NO_ERROR;
}

/*
 * xes_posix_move_file_with_prefix - Moves a LOB file from the source path to a destination directory defined by the prefix.
 *
 * return: error code
 * src_path(in): Source file path
 * metaname(in) : Metadata used for the new file name
 * prefix(in) : prefix to be added to the destination path when moving the file
 * new_path(out): Resulting path of the moved file
 */
int
xes_posix_move_file_with_prefix (const char *src_path, const char *metaname, const char *prefix, char *new_path)
{
  ssize_t ret;
  char dirname1[NAME_MAX], dirname2[NAME_MAX], filename[NAME_MAX];
  char *p;

  /* Create a target file */
  es_get_unique_name (dirname1, dirname2, metaname, filename);

  ret = snprintf (new_path, PATH_MAX, "%s%c%s%c%s", prefix, PATH_SEPARATOR, dirname1, PATH_SEPARATOR, filename);
  if (ret < 0 || ret >= PATH_MAX)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", new_path);
      return ER_ES_INVALID_PATH;
    }
  es_log ("xes_posix_move_file_with_prefix(%s, %s): %s\n", src_path, metaname, new_path);

  p = strrchr (new_path, PATH_SEPARATOR);
  if (p != NULL)
    {
      *p = '\0';        /* Temporarily truncate the path to extract the directory portion */
    }
  else
    {
      return ER_ES_GENERAL;
    }

  /* Try create directory */
  ret = es_make_dirs (new_path, dirname2);

  *p = PATH_SEPARATOR;

  if (ret != NO_ERROR)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", new_path);
      return ER_ES_GENERAL;
    }

  ret = es_os_rename_file_abs (src_path, new_path);
  if (ret != NO_ERROR)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", src_path);
      return ER_ES_GENERAL;
    }

  return NO_ERROR;
}

static int
es_os_rename_file_abs (const char *src, const char *dst)
{
  const char *abs_dst_path = dst, *abs_src_path = src;
  char dst_buf[PATH_MAX], src_buf[PATH_MAX];

  if (es_make_abs_path (src_buf, src) > 0)
    {
      abs_src_path = src_buf;
    }

  if (es_make_abs_path (dst_buf, dst) > 0)
    {
      abs_dst_path = dst_buf;
    }

  return os_rename_file (abs_src_path, abs_dst_path);
}


/*
 * xes_posix_get_file_size - get the size of the external file
 *
 * return: file size, or ER_ES_GENERAL
 * path(in): file path
 */
off_t
xes_posix_get_file_size (const char *path)
{
  int ret;
  struct stat pstat;
  const char *abs_path = path;
  char buf[PATH_MAX];

  if (es_make_abs_path (buf, path) > 0)
    {
      abs_path = buf;
    }

  es_log ("xes_posix_get_file_size(%s)\n", abs_path);

  ret = stat (abs_path, &pstat);
  if (ret < 0)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "POSIX", abs_path);
      return -1;
    }

  return pstat.st_size;
}
#endif /* SA_MODE || SERVER_MODE */

/*
 * es_local_read_file - read from the local file
 *
 * return: error code, ER_ES_GENERAL or NO_ERRROR
 * path(in): file path
 */
int
es_local_read_file (const char *path, void *buf, size_t count, off_t offset)
{
  int fd;
  ssize_t nbytes;
  size_t total = 0;

  es_log ("es_local_read_file(%s, count %d offset %ld)\n", path, count, offset);

#if defined (WINDOWS)
  fd = open (path, O_RDONLY | O_BINARY);
#else /* WINDOWS */
  fd = open (path, O_RDONLY | O_LARGEFILE);
#endif /* !WINDOWS */
  if (fd < 0)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "LOCAL", path);
      return ER_ES_GENERAL;
    }

  while (count > 0)
    {
      if (lseek (fd, offset, SEEK_SET) != offset)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "LOCAL", path);
      close (fd);
      return ER_ES_GENERAL;
    }

      nbytes = read (fd, buf, (unsigned) count);
      if (nbytes < 0)
    {
      switch (errno)
        {
        case EINTR:
        case EAGAIN:
          continue;
        default:
          {
        er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "LOCAL", path);
        close (fd);
        return ER_ES_GENERAL;
          }
        }
    }
      if (nbytes == 0)
    {
      break;
    }

      offset += nbytes;
      count -= nbytes;
      buf = (char *) buf + nbytes;
      total += nbytes;
    }
  close (fd);

  return (int) total;
}

/*
 * es_local_get_file_size - get the size of the external file
 *
 * return: file size, or ER_ES_GENERAL
 * path(in): file path
 */
off_t
es_local_get_file_size (const char *path)
{
  int ret;
  struct stat pstat;

  es_log ("es_local_get_file_size(%s)\n", path);

  ret = stat (path, &pstat);
  if (ret < 0)
    {
      er_set_with_oserror (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_ES_GENERAL, 2, "LOCAL", path);
      return -1;
    }

  return pstat.st_size;
}