www.pudn.com > efs.rar > fs_lib.c


/***********************************************************************
 * fs_lib.c
 *
 * Public interface for EFS2
 * Copyright (C) 2002, 2003, 2004, 2005, 2006 Qualcomm, Inc.
 *
 * This file contains Library API functions which EFS2
 * can utilize.
 ***********************************************************************/

/*===========================================================================

                        EDIT HISTORY FOR MODULE

  This section contains comments describing changes made to the module.
  Notice that changes are listed in reverse chronological order.

  $Header: //depot/asic/MSMSHARED/services/efs/MSM_EFS.01.02/fs_lib.c#10 $ $DateTime: 2006/11/13 09:20:40 $ $Author: davidb $

when          who     what, where, why
--------      ---     ------------------------------------------------------
2006-11-02     rp     Moved fs_path_buffer[] to fs_patbuff.c
2006-10-17    umr     Added FS_PATH_BUFFER_LOCK around fs_path_buffer
2006-09-25    umr     Added efs_deltree() for deleting a whole tree.
2006-09-20    dlb     Lint fixes
2006-06-19    nrs     Allow renames on different size items
2006-03-30    sh      Lint fixes
2005-11-17    nrs     Fixed bug in efs_get, efs_errno
2005-10-28    nrs     Changed length fields from int to fs_size_t
2004-12-16    nrs     Fixed possible cases where Global Unlock may not occur
2004-11-01    nrs     Removed excessive string copies from auto create
2004-11-01    nrs     Changed l_temp_path from local stack to global variable.
2004-10-28    nrs     Add functionality to autocreate directories on efs_put ()
2004-10-08    nrs     Added rename in efs_put () for robustness.
2004-10-08    dlb     Cleanup indenting.
2004-09-30    nrs     Created.

===========================================================================*/

#include "fs_errno.h"
#include "fs_lib.h"
#include "fs_pathbuff.h"
#include "fs_public.h"
#include "fs_efs2.h"
#include "assert.h"

static int efs_put_mkdir (const char * path);
static int efs_put_aux (const char *path, void *data, fs_size_t length,
                        int oflag, int mode);

static char g_temp_path [FS_PATH_MAX+1];


/**********************************************************************
 * FUNCTION      efs_put
 *
 * Store a value in a special item file.
 */
int
efs_put (const char *path, void *data, fs_size_t length, int oflag, int mode)
{
  int result = 0;
  result = efs_put_aux(path, data, length, oflag, mode);
  if (result < 0)
  {
    if ((efs_errno == ENOENT) && (O_AUTODIR & oflag))
    {
      result = efs_put_mkdir((char *) path);
      if (result >= 0)
        result = efs_put_aux(path, data, length, oflag, mode);
    }
  }
  return result;
}
/* Read contents of item. */
int
efs_get (const char *path, void *data, fs_size_t length)
{
  int result = 0, fd = 0, index = 0;

  result = efs_raw_get (path, data, length);

  /* efs_raw_get is successful, kick it back */
  if (result >= 0)
    return result;

  /* efs_raw_get couldn't read the item. Return an error unless the path
   * wasn't an S_ITM type. */

  if (efs_errno != EINVAL)
  {
    return result;
  }

  fd = efs_open (path, O_RDONLY);

  /* If the file does not exist, then the current file handle will be less
   * than zero */

  if (fd < 0)
  {
    efs_errno = -EBADF;
    return -1;
  }

  while (length > 0)
  {
    if (length > 8192)
      index = 8192;
    else
      index = length;

    result = efs_read (fd, data, index);
    /* Check to see if the read failed. If it did, then deterime
     * why it failed. */

    if ((result <= 0) || (result != index))
    {
      if ((result == 0) || (result < index))
        result = -FS_ERANGE;
      efs_errno = -result;
      if (efs_close (fd))
        efs_errno = -EBADF;
      return -1;
    }

    data = (void *) ((uint8 *) data + index);
    length -= index;
  }

  if (efs_close (fd))
  {
    efs_errno = -EBADF;
    return -1;
  }
  return result;
}

/**********************************************************************
 * deltree_truncate_path
 * Chops off the characters starting from the last character in the passed
 * string all the way upto the occurrence of '/' character.
 */
static int
deltree_truncate_path (char* path, word max_count)
{
  word slen = 0;
  int status = -1, i;

  slen = strlen (path);
  for (i = slen - 1; i >= max_count; i--)
  {
    if (path[i] == '/')
    {
      /* Found the delimitation */
      path[i] = 0;
      status = 0;
      break;
    }
  }
  /* Have not found any sub directories */
  return status;
}

/**********************************************************************
 * deltree_readdir
 *
 * Reads a directory and returns the next occurring node by skipping if a
 * "." or ".." is found. Assumes the directory stream pointer is valid.
 * No stats or any validation is performed. It is up to the calling functions
 * to make sure the passed pointer is validated.
 */

static struct fs_dirent*
deltree_readdir (EFSDIR *dir_stream_ptr)
{
  struct fs_dirent  *dirent = 0;
  int skip = 0;

  do
  {
    skip = 0;
    dirent = efs_readdir (dir_stream_ptr);
    if (dirent
        && dirent->d_name[0] == '.'
        && dirent->d_name[1] == 0)
    {
      /* We have found a '.' file so proceed to read further */
      skip = TRUE;
    }
    else if (dirent
             && dirent->d_name[0] == '.'
             && dirent->d_name[1] == '.'
             && dirent->d_name[2] == 0)
    {
      /* We have found a '..' file so proceed for next entry */
      skip = TRUE;
    }
  } while (skip);

  return dirent;
}

/**********************************************************************
 * efs_deltree
 *
 * Deletes the directory and all its contents. 'path' can be a directory
 * file, node or sym link. If a non directory is passed as argument it is
 * simply deleted. If the argument is a directory all its contents and itself
 * is deleted. Returns 0 on success and -1 on failure. If a failure occurs,
 * the tree is only partially deleted. On a failure it is unknown as to how
 * many files or directories are left in the parent directory.
 */

int
efs_deltree (const char *path)
{
  EFSDIR            *dir_stream_ptr = 0;
  struct fs_dirent  *dirent = 0;
  struct fs_stat     stat_buf;
  int status = TRUE;
  word dlen = 0;

  if (efs_lstat (path, &stat_buf) != 0)
  {
    /* Unable to stat the path. Return an error */
    return -1;
  }

  if (!S_ISDIR (stat_buf.st_mode))
  {
    /* This is not a directory */
    if (efs_unlink (path) != 0)
    {
      /* Failed to delete the entity return fail */
      status = -1;
    }
    /* We are done here so return 0 */
    status = 0;
  }
  else
  {
    /* This is a directory */
    dir_stream_ptr = efs_opendir (path);
    if (!dir_stream_ptr)
    {
      /* Unable to open the directory */
      status = -1;
    }
    dlen = strlen (path);

    /*
     * Lock the Buffer so that other functions do not use it while the buffer
     * is in use.
     */
    FS_PATH_BUFF_LOCK ();
    strncpy (fs_path_buff[0], path, FS_PATH_MAX);

    while (status > 0)
    {
      int slen;

      dirent = deltree_readdir (dir_stream_ptr);

      if (dirent == NULL)
      {
        /* Empty. Delete this directory and return */
        if (efs_closedir (dir_stream_ptr) < 0)
        {
          status = -1;
        }
        if ((status > 0) && (efs_rmdir (fs_path_buff[0]) < 0))
        {
          status = -1;
        }
        /* Make sure we have reached the head directory */
        if ((status > 0) && (dlen == strlen (fs_path_buff[0])))
        {
          /* We have completely erased the tree successfully return. */
          status = 0;
          break;
        }
        else
        {
          /* Go one path back. There is more to do */
          if (deltree_truncate_path (fs_path_buff[0], dlen) == 0)
          {
            /* It has to be a directory don't have to lstat again */
            /* Open the directory and continue the while loop */
            dir_stream_ptr = efs_opendir (fs_path_buff[0]);
            if (!dir_stream_ptr)
            {
              status = -1;
            }
          }
        }
      }
      else
      {
        /* There is some thing more, check it out and process */
        strcat (fs_path_buff[0], "/");
        slen = strlen (fs_path_buff[0]);
        strncat (fs_path_buff[0], dirent->d_name, FS_PATH_MAX - slen);

        if (efs_lstat (fs_path_buff[0], &stat_buf) == -1)
        {
          /* Unable to stat the file or directory. Return an error */
          status = -1;
        }
        else if (S_ISDIR (stat_buf.st_mode))
        {
          /* Close the parent directory so that we could reuse
           * dir_stream_ptr.
           */
          if (efs_closedir (dir_stream_ptr) < 0)
          {
            /* Unable to Close directory. Return an error */
            status = -1;
          }

          /* Open the directory and continue the while loop */
          if (status > 0)
          {
            dir_stream_ptr = efs_opendir (fs_path_buff[0]);
          }
          if (!dir_stream_ptr)
          {
            status = -1;
          }
        }
        else
        {
          /* It should be a file or a link delete this and get back to
           * while loop to look for another.
           */
          if (efs_unlink (fs_path_buff[0]) != 0)
          {
            /* Failed to unlink */
            status = -1;
          }

          /* Get the directory name to the path so as to use again */
          if (deltree_truncate_path (fs_path_buff[0], dlen) < 0)
          {
            status = -1;
          }
        }
      }
    }
    /*
     * Release the buffer so other functions could use.
     */
    FS_PATH_BUFF_UNLOCK ();
  }
  /* We might get here even if one of the above efs_closedir () fails
   * It should be OK to try closing the directory once more.
   */
  efs_closedir (dir_stream_ptr);
  /* Lets make sure one last time if the function was successful */
  if ((status == 0) && (efs_lstat (path, &stat_buf) == 0))
  {
    /* Status is 0, no errors found and the directory still exists,
     * which is seriously wrong. Directory should have not exist if there
     * were no errors. ERR_FATAL for further investigation.
     */
    ERR_FATAL (" efs_deltree () Failed ",0,0,0);
  }

  return status;
}

/**********************************************************************
 * efs_put_mkdir
 *
 * When using efs_put to insert a new item, the directory desired may
 * not exist. Therefore this function will scan the path, and then create
 * the directory. After the directory is created, it will return to efs_put ()
 * to continue inserting the item.
 *
 * Returns a zero on success, or -1 for an error.  Upon error, efs_errno
 * will be set to one of the following:
 *   ENOENT - The item or a component of the path does not exist.
 *   FS_ERANGE - The item file is larger than the specified buffer.
 */

static int
efs_put_mkdir(const char * path)
{
  int result = 0;
  int index = 0;
  int len = 0;

  if (path == NULL)
  {
    return -1;
  }

  len = index = strlen(path) - 1;
  len++;

  memcpy (g_temp_path, path, len);


  do
  {
    while ((path[index] != '/') && (index > 0))
    {
      index--;
    }

    if (index > 0)
      g_temp_path[index] = 0x00;

    /* Attempt to make subdirectories */
    result = efs_mkdir (g_temp_path, 0777);
    if (result == 0)
      break;

    /* Move the index pointer down for next subpath */
    index--;

  } while ((index >= 0) || (efs_errno == EEXIST));

  if ((EEXIST == efs_errno) && (result != 0))
  {
    /* The subdirectory exists, so we just need to exit */
    return 0;
  }

  if (result == 0)
  {
    while (index < len)
    {
      /* Go back up the path until we reach the end */

      ASSERT (index >= 0);
      g_temp_path[index] = '/';
      /* Need to increment the array index to start going back up the path */
      index++;
      while ((path[index] != '/') && (index < len))
      {
        index++;
      }

      if (index >= len)
      {
        /* It's the end of the path, now we have to stop */
        break;
      }

      /* Attempt to make subdirectories */
      result = efs_mkdir(g_temp_path, 0777);

      if (0 != result)
        return -1;
    }
  }

  if (result != 0)
    return -1;

  return 0;
}


static int
efs_put_aux (const char *path, void *data, fs_size_t length, int oflag,
    int mode)
{
  int fd = 0;
  int result = 0;
  int index = 0;

  index = strlen(path);

  if (index > FS_PATH_MAX)
  {
    efs_errno = ENAMETOOLONG;
    return -1;
  }

  FS_GLOBAL_LOCK ();
  strcpy (g_temp_path, path);

  if (index < FS_PATH_MAX)
    strcat(g_temp_path, "$");
  else
    g_temp_path[FS_PATH_MAX-1] = '$';

  index = 0;
  /* This check forces the behavior to be exactly as before if a normal item
   * is being retrieved.
   */
  if (length <= FS_ITEM_MAX)
  {
    result = efs_raw_put (path, data, length, oflag, mode);
    /* If it is not an item, then we need to create the item and
     * then we need to handle things differently. */
    if ((result < 0)  && (efs_errno == EEXIST))
    {
      result = efs_raw_put (g_temp_path, data, length, oflag, mode);
      if (result == 0)
        result = efs_rename (g_temp_path, path);
    }
    FS_GLOBAL_UNLOCK ();
    return result;
  }

  /* Since the length is too long for a normal item, we need to open/create the
   * file at the path specified
   */


  /* In order to properly handle large files we need to loop the writes.
   * For normal items since the length isn't greater than 8k, we can call
   * the efs_raw_put function from fs_public.c
   */

  /* In order to make this function robust, when the rename() function is
   * fixed close the newly opened file, open a new temp file with a unique
   * name, write the data to the temp file, then after completion rename the
   * file to the desired name. */


  index = 0;

  fd = efs_open(g_temp_path, O_RDWR | O_CREAT | O_TRUNC, 0666);

  if (fd < 0)
  {
    /* There was an error opening the temporary file */
    FS_GLOBAL_UNLOCK ();
    return -1;
  }

  do
  {
    if (length > 8192)
      index = 8192;
    else
      index = length;
    result = efs_write (fd, data, index);
    if ((result <= 0) || (result < index))
    {
      if (result == 0)
        result = -ENOSPC;
      efs_errno = -result;
      if (result < index)
        efs_errno = -FS_ERANGE;
      if (efs_close (fd))
        efs_errno = -EBADF;
      FS_GLOBAL_UNLOCK ();
      return -1;
    }
    data = (void *) ((uint8 *) data + index);
    length -= index;
  }
  while (length > 0);

  /* If it comes out of the loop properly, then the write was successful. Now
   * we need to move the file from the temporary file to the desired file using
   * rename() */

  /* And now don't forget to close the file */

  if (efs_close (fd))
  {
    efs_errno = -EBADF;
    FS_GLOBAL_UNLOCK ();
    return -1;
  }

  result = efs_rename (g_temp_path, path);

  FS_GLOBAL_UNLOCK ();

  return result;

}