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;
}