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


/*********************************************************************** 
 * fs_public.c 
 * 
 * Public interface for EFS2 
 * Copyright (C) 2002--2006 Qualcomm, Inc. 
 * 
 * This file contains POSIX interface functions using which EFS2 
 * can be utilized. 
 ***********************************************************************/ 
 
/*=========================================================================== 
 
                        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_public.c#42 $ $DateTime: 2006/11/13 09:30:29 $ $Author: davidb $ 
 
when          who     what, where, why 
--------      ---     ------------------------------------------------------ 
2006-11-10    s h     Add support for efs_chmod(). 
2006-11-02     rp     Added efs_stdio_init() in efs_initialize(). 
2006-10-26    s h     Removed all references to obsolete fs_testapp 
2006-10-05    umr     Added call to initialize deltree lock in efs_initialize 
2006-09-20    dlb     Lint cleanup. 
2006-08-15    dlb     Initialize upgrade system. 
2006-07-19    dlb     Move transaction limits down to each FS. 
2006-07-06    dlb     Remove 'size' field from vnode. 
2006-07-05    dlb     Move read-end check down to VFS code. 
2006-06-27    dlb     Pass stat through readdir. 
2006-06-23     sh     Removed conditional FEATURE_FS_BENCHMARK.. stubs now. 
2006-06-19    dlb     Enable diag in off-target test. 
2006-06-19    nrs     Allow renames on different size items 
2006-06-07     sh     Featurized hotplug_init() 
2006-06-02     sh     Removed FTL layer initialization. (moved to hotplug) 
2006-05-23     yg     Added FTL layer initialization. 
2006-05-11     sh     Renamed efs_hotplug_... to hotplug_... 
2006-05-09    dlb     Add nodevfs. 
2006-03-31     sh     Lint cleanup 
2006-03-29     sh     Hotplug init is no longer optional. 
2006-01-31    dlb     Correct errno of access call. 
2006-01-26     sh     Added TIMETEST2 messages for public API 
2006-01-26     sh     Added TIMETEST2 message support 
2006-01-11    nrs     Fixed Copyright header 
2005-12-20    dlb     Initialize descriptor table. 
2005-11-28    nrs     Merge 7500 orphans 
2005-11-12     sh     Use rex tcb err_num instead of global efs_errno 
2005-11-10    nrs     Merge Fix 
2005-11-09    sh      Renamed FS_FEATURE_EXTFS to FEATURE_EFS_EXTFS 
2005-10-28    nrs     Changed raw_put and raw_get length from int to fs_size_t 
2005-10-26     sh     Lint cleanup 
2005-10-20    sh      Initialize fs_benchmark task and cpu_hog task 
2005-10-05    nrs     Clean and fix quotas and reservations 
2005-09-02    dlb     Don't follow last symlink on symlink creation. 
2005-09-02    dlb     Add sfat mounting feature on /mmc1. 
2005-08-26    sh      Don't ERR_FATAL on efs_chmod, even unimplemented 
2005-08-23    sh      Add support for mknod, device files 
2005-08-10    dlb     Umount of opened dirs. 
2005-08-10    dlb     Nodev root of mpoint properly. 
2005-08-09    dlb     Add nodev support, for cleanup on umount. 
2005-08-08    sh      Enforce identical item size for put 
2005-08-03    dlb     Add umount support. 
2005-07-22    nrs     Added implementation for quotas & reservations 
2005-07-15    nrs     Added implementation for efs_chown 
2005-07-13    dlb     Mountpoint cleanup. 
2005-06-06    dlb     Extensions for SFAT. 
2005-05-26    sd      Compilation fixes for L4. 
2005-05-10     sh     Lock filesystem before starting superblock reset 
2005-04-26    dlb     Add 2K page support. 
2005-04-13    dlb     Fix testapp build for remote. 
2005-04-06    nrs     Removed fs_diag dependencies from Unit Test 
2005-03-28    dlb     Move unref of mkdir vp up one level. 
2005-02-17    dlb     Remove transaction mention from mkdir. 
2005-01-27    dlb     Allow standalone builds. 
2004-11-02    dlb     Remove all mention of transactions here. 
2004-10-21    nrs     Modified fs_public_desc_write() to not use transactions 
2004-10-13    dlb     Fixed flush outside of xact on unlink. 
2004-10-12    dlb     Add remote EFS client support. 
2004-10-11    nrs     Modified rename to work with files that currently exist 
2004-10-11    nrs     Modified unlink to remove transaction calls 
2004-10-07    nrs     Changed efs_put, efs_get to efs_raw_put, efs_raw_get 
2004-10-07    dlb     Whitespace cleanup. 
2004-07-26    dlb     Support extfs. 
2004-07-19    dlb     Restructure vnode interface. 
2004-02-20    dlb     Disallow renames of symlinks. 
2003-11-18     gr     Restored call to FS_GLOBAL_UNLOCK in efs_rename. 
2003-10-22    dlb     Fix error code on write of 0 bytes. 
2003-09-09    dlb     Factory image works with bitmap and tree. 
2003-08-20    dlb     Remove delayed truncate code. 
2003-07-26     gr     Modified efs_lseek to return an error if an attempt to 
                      seek before the beginning of the file is made. 
2003-07-23     gr     Added check in ftruncate to return an error if the input 
                      file was opened in read-only mode. 
2003-06-17    jkl     Clean up code. 
2003-06-13     gr     Modified efs_unlink and efs_rmdir to return an error if 
                      the file or directory being deleted is open. 
2003-06-12    adm     Add support for factory start. 
2003-06-12     cr     Featurize testapp init. 
2003-05-30     gr     Fixed a bug in efs_open. Modified efs_rename to return 
                      failure if called on a directory. 
2003-05-23     cr     Add initialization of on target test application. 
                      The init won't happen unless one takes a breakpoint in 
                      debugger and changes state on static variables. 
2003-05-16     gr     Fixed a bug in efs_rename. 
2003-05-02     gr     Added master reset functionality. 
2003-04-15    cr      Moved global lock to fs_efs2.h as part of delayed 
                      truncation implementation to fix CR#28410. 
2003-03-24     gr     Code modifications to ensure that all writes to flash 
                      have transaction wrappers. 
2003-03-12     gr     Implemented efs_rename. 
2003-03-06    bgc     Moved vnode flush to inside the transaction in 
                      fs_public_desc_write(). 
2003-03-04     gr     Renamed the parameters to efs_rename so that the C++ 
                      reserved word "new" is not used. 
2002-08-20    adm     Created. 
 
===========================================================================*/ 
 
#include  
#include  
#include  
 
#include "fs_sys_types.h" 
#include "fs_err.h" 
#include "fs_fcntl.h" 
#include "fs_errno.h" 
#include "fs_public.h" 
#include "fs_desc.h" 
#include "fs_vnode.h" 
#include "fs_namei.h" 
#include "fs_mount.h" 
#include "fs_efs2.h" 
#include "fs_romfs.h" 
#include "fs_nodev.h" 
#include "fs_db.h" 
#include "fs_timetest2.h" 
#include "fs_hotplug.h" 
#include "fs_benchmark.h" 
#include "fs_upgrade.h" 
#include "fs_pathbuff.h" 
#include "fs_stdio.h" 
#include "fs_privatedir.h" 
 
#include "jzap.h" 
 
#ifdef FEATURE_EFS_EXTFS 
#include "fs_extfs.h" 
#endif 
 
#ifdef FS_FEATURE_RMTEFS_CLIENT 
#include "fs_rmtefs.h" 
#endif 
 
#ifndef FS_STANDALONE 
/* FS_GLOBAL_LOCK critical section definition memory allocation */ 
rex_crit_sect_type fs_crit_sect; 
#endif /*FS_STANDALONE*/ 
 
#ifdef FS_STANDALONE 
int efs_errno = 0; 
#endif 
 
 
struct nameidata nameidata_buffer; 
struct nameidata nameidata_buf2; 
 
/* Vops. */ 
 
static int fs_public_desc_close (struct fs_descriptor *file); 
static fs_ssize_t fs_public_desc_write (struct fs_descriptor *file, 
    const void *buf, fs_size_t count); 
static fs_ssize_t fs_public_desc_read (struct fs_descriptor *file, 
    void *buf, fs_size_t count); 
 
static struct desc_ops public_desc_ops = { 
  fs_public_desc_close, 
  fs_public_desc_write, 
  fs_public_desc_read, 
}; 
 
/********************************************************************** 
 * Iterators. 
 * 
 * All iterators are allocated and deallocated from a common pool.  We make 
 * the assumption that there will not be many iterators in use. 
 */ 
 
struct fs_dir_data { 
  int                   busy; 
  void                 *iter; 
  struct fs_dirent      dirent; 
  struct fs_mount      *mp; 
  struct fs_vnode      *vp; 
}; 
 
static struct fs_dir_data all_iters[FS_MAX_ITERATORS]; 
 
#ifdef FEATURE_EFS_COMPATIBILITY 
extern void fs_compat_init (void); 
#endif /* FEATURE_EFS_COMPATIBILITY */ 
 
#ifndef FEATURE_IG_EFS_EXT_SERVER 
extern void fs_diag_init (void); 
#endif 
 
/* Data for exceptions. */ 
struct efs_catch_block *_fs_catch = NULL; 
struct efs_catch_block _fs_static_catch; 
 
/*********************************************************************** 
FUNCTION      efs_initialize 
 
DESCRIPTION   This function should be called to mount the 
              root file system. It calls the start function specified 
              in the fs_mount_ops structure to do the actual initialization 
              where both the file system and the dbtree are initialized. 
 
DEPENDENCIES 
 
RETURN VALUE  If the mount was successful 0, else the error no. 
 
SIDE EFFECTS 
**********************************************************************/ 
int 
efs_initialize () 
{ 
  int result; 
  int i; 
 
  FS_GLOBAL_LOCK_INIT(); 
 
  FS_PATH_BUFF_LOCK_INIT(); 
 
  EFS_TT_API (EFS_TT_INITIALIZE); 
 
  _fs_catch = NULL; 
 
#ifdef FEATURE_JZAP 
  zap_init(); 
#endif 
 
  fs_upgrade_init (); 
 
  fs_desc_init (); 
  fs_vnode_init (); 
 
  fs_mount_init (); 
  fs_efs2_init (); 
  fs_romfs_init (); 
  fs_nodevfs_init (); 
#ifdef FS_FEATURE_RMTEFS_CLIENT 
  fs_rmtefs_init (); 
#endif 
#ifdef FEATURE_EFS_EXTFS 
  fs_extfs_init (); 
#endif 
 
  /* Mount the root filesystem. */ 
  result = fs_mount_root ("EFS2", ""); 
 
  if (result != 0) 
    FS_ERR_FATAL ("No flash device found", 0, 0, 0); 
 
  /* All of the iterators are available. */ 
  for (i = 0; i < FS_MAX_ITERATORS; i++) 
    all_iters[i].busy = 0; 
 
 
#ifdef FEATURE_EFS_COMPATIBILITY 
  fs_compat_init (); 
#endif /* FEATURE_EFS_COMPATIBILITY */ 
 
#ifndef FEATURE_IG_EFS_EXT_SERVER 
  fs_diag_init (); 
#endif 
 
  fs_benchmark_init(); 
  fs_cpu_hog_init(); 
 
#ifdef FEATURE_L4 
  efs_mkdir ("/export", 0755); 
#endif /* FEATURE_L4 */ 
 
#if defined FEATURE_EFS_HOTPLUG || defined FEATURE_EFS_COLDPLUG 
  hotplug_init (); 
#endif 
 
#if defined(FS_FEATURE_RMTEFS_CLIENT) && defined(FEATURE_EXTERNAL_APPS_MOUNT) 
  efs_mkdir ("/apps", 0755); 
  efs_mount ("0", "/apps", "rmtefs", 0, "/"); 
#endif 
 
  /* create the efs's private meta-data folder */ 
  efs_privatedir_init (); 
 
  /* Initialize stdio related functions */ 
  efs_stdio_init (); 
 
  return result; 
} 
 
 
/*********************************************************************** 
FUNCTION      efs_power_down 
 
DESCRIPTION   This function calls the file system finalize function to 
              make sure the shut down is handled correctly and normally. 
 
DEPENDENCIES  None 
 
RETURN VALUE  If the shut down was successful 0. 
 
SIDE EFFECTS  None 
**********************************************************************/ 
int 
efs_power_down () 
{ 
  EFS_TT_API (EFS_TT_POWER_DOWN); 
  /* XXX: Should unmount everything. */ 
  return fs_root_mpoint->ops->stop (fs_root_mpoint); 
} 
 
/* Open/create a file. */ 
int 
efs_open (const char *path, int oflag, ...) 
{ 
  fs_mode_t             mode = 0; 
  struct nameidata *ndp = &nameidata_buffer; 
  int result = 0; 
  struct fs_descriptor *file = 0; 
 
  /* Posix requires the optional third argument for the mode.  Extract this 
   * if we are creating a file. 
   * Lint is so very very confused by va_arg.  Suppress everything it finds. */ 
/*lint -save -e746 -e10 -e64 -e718 -e529 */ 
  if ((oflag & O_CREAT) != 0) 
  { 
    va_list arg; 
    va_start (arg, oflag); 
    mode = va_arg (arg, int); 
    va_end (arg); 
  } 
/*lint -restore */ 
 
  FS_GLOBAL_LOCK (); 
 
  EFS_TT_PRINT (EFS_TT_OPEN, path); 
  EFS_TT_VALUE (EFS_TT_OPEN, (uint32) oflag, FALSE, FALSE, TRUE); 
 
  /* Allocate a descriptor. */ 
  file = fs_desc_alloc (); 
  if (file == NULL) { 
    efs_errno = EMFILE; 
    goto error_case; 
  } 
 
  result = fs_namei (ndp, path, oflag); 
 
  if (result != 0) { 
    goto error_case; 
  } 
 
  /* Don't allow open on device special files */ 
  if (ndp->vp && (S_ISBLK(ndp->vp->mode) || S_ISCHR(ndp->vp->mode))) { 
    efs_errno = EEXIST; 
    fs_vnode_unref (ndp->dvp); 
    fs_vnode_unref (ndp->vp); 
    goto error_case; 
  } 
 
  /* If the name lookup succeeds, return an error if the user is attempting 
   * to open a directory with O_WRONLY or O_RDWR specified. 
   */ 
  if ((ndp->vp != NULL) && S_ISDIR (ndp->vp->mode) && 
      ((oflag & O_RDWR) || (oflag & O_WRONLY))) { 
    efs_errno = EISDIR; 
    fs_vnode_unref (ndp->dvp); 
    fs_vnode_unref (ndp->vp); 
    goto error_case; 
  } 
 
  /* fs_namei already checked the open flags for O_CREAT and O_EXCL, so, if 
   * we get this far, we can either create, or open the file. */ 
 
  if (ndp->vp == NULL) { 
    result = ndp->dvp->vops->create (ndp, mode); 
    if (result < 0) { 
      fs_vnode_unref (ndp->dvp); 
      goto error_case; 
    } 
  } 
 
  /* Now, ndp->vp points to our file.  We don't need the directory any 
   * more, so unreference that. */ 
  fs_vnode_unref (ndp->dvp); 
  ndp->dvp = 0; 
 
  /* If the file should be truncated (and was opened for writing). */ 
  if ((oflag & O_TRUNC) && 
      (((oflag & O_ACCMODE) + 1 ) & 2) != 0) 
  { 
    result = ndp->vp->vops->truncate (ndp->vp, 0); 
    /* XXX: What do we do if there was a problem truncating the file? */ 
  } 
 
  /* XXX: Handle different operation types. */ 
  /* Fill in the descriptor */ 
  file->state = FS_DESC_STATE_FILE; 
 
  /* Move the referenced vnode into the descriptor. */ 
  file->vp = ndp->vp; 
  ndp->vp = 0; 
 
  file->mode = oflag; 
  file->file_pos = 0; 
 
  file->dops = &public_desc_ops; 
 
  FS_GLOBAL_UNLOCK (); 
 
  EFS_TT_RETURN (file->fd); 
  return file->fd; 
 
error_case: 
  if (file != NULL) 
    fs_desc_free (file); 
  if (result < 0) 
    efs_errno = -result; 
  FS_GLOBAL_UNLOCK (); 
  EFS_TT_RETURN (-result); 
  return -1; 
} 
 
static fs_ssize_t 
_efs_read_core (int filedes, void *buf, fs_size_t nbyte) 
{ 
  struct fs_descriptor *desc_ptr; 
  int result; 
 
  FS_GLOBAL_LOCK (); 
 
  if ((desc_ptr = fs_desc_lookup (filedes)) == NULL) 
  { 
    efs_errno = EBADF; 
    FS_GLOBAL_UNLOCK (); 
    return -1; 
  } 
 
  result = desc_ptr->dops->read (desc_ptr, buf, nbyte); 
 
  FS_GLOBAL_UNLOCK (); 
 
  if (result < 0) { 
    efs_errno = -result; 
    return -1; 
  } else 
    return result; 
} 
 
fs_ssize_t 
efs_read (int filedes, void *buf, fs_size_t nbyte) 
{ 
  fs_ssize_t result; 
  EFS_TT_API (EFS_TT_READ); 
  EFS_TT_VALUE (EFS_TT_READ, (uint16)filedes, TRUE, TRUE, FALSE); 
  EFS_TT_VALUE (EFS_TT_READ, (uint32)nbyte, TRUE, TRUE, TRUE); 
  result = _efs_read_core (filedes, buf, nbyte); 
  EFS_TT_RETURN (result); 
  return result; 
} 
 
static fs_ssize_t 
fs_public_desc_read (struct fs_descriptor *file, 
    void *buf, fs_size_t count) 
{ 
  fs_ssize_t result; 
 
  /* Check permissions, use the same trick described in 
   * fs_public_desc_write. */ 
  if ((((file->mode & O_ACCMODE) + 1) & 1) == 0) { 
    return -EBADF; 
  } 
 
  /* Do the actual read. */ 
  if (count > 0) 
    result = file->vp->vops->read (file->vp, file->file_pos, buf, count); 
  else 
    result = 0; 
 
  /* Adjust the file pointer. */ 
  if (result > 0) { 
    file->file_pos += result; 
  } 
 
  return result; 
} 
 
static fs_ssize_t 
_efs_write_core (int filedes, const void *buf, fs_size_t nbyte) 
{ 
  struct fs_descriptor *desc_ptr; 
  int result; 
 
  /* Just return if asked to write zero bytes. 
   */ 
  if (nbyte == 0) 
    return 0; 
 
  FS_GLOBAL_LOCK (); 
 
  if ((desc_ptr = fs_desc_lookup (filedes)) == NULL) 
  { 
    efs_errno = EBADF; 
    FS_GLOBAL_UNLOCK (); 
    return -1; 
  } 
 
  result = desc_ptr->dops->write (desc_ptr, buf, nbyte); 
 
  FS_GLOBAL_UNLOCK (); 
 
  /* It is not an error to write fewer bytes than requested. However, we 
   * consider it an error if not even one byte could be written. 
   */ 
  if (result <= 0) { 
    if (result == 0) 
      result = -ENOSPC; 
    efs_errno = -result; 
    return -1; 
  } else 
    return result; 
} 
 
fs_ssize_t 
efs_write (int filedes, const void *buf, fs_size_t nbyte) 
{ 
  fs_ssize_t result; 
  EFS_TT_API (EFS_TT_WRITE); 
  EFS_TT_VALUE (EFS_TT_WRITE, (uint16)filedes, TRUE, TRUE, FALSE); 
  EFS_TT_VALUE (EFS_TT_WRITE, (uint32)nbyte, TRUE, TRUE, TRUE); 
  result = _efs_write_core (filedes, buf, nbyte); 
  EFS_TT_RETURN (result); 
  return result; 
} 
 
static fs_ssize_t 
fs_public_desc_write (struct fs_descriptor *file, 
    const void *buf, fs_size_t count) 
{ 
  fs_ssize_t result; 
 
  /* Check that we have permissions to write this file.  This depends on 
   * the specific values of O_RDONLY=0, O_WRONLY=1, and O_RDWR=2 that posix 
   * use.  By adding 1, the value is just a pair of bits with the lowest 
   * bit indicating read, the the next bit indicating write.  Neat, isn't 
   * it. */ 
 
  if ((((file->mode & O_ACCMODE) + 1) & 2) == 0) { 
    return -EBADF; 
  } 
 
  /* Check for the append case.  In append, all writes always seek to the 
   * end of the file. */ 
  if ((file->mode & O_APPEND) != 0) { 
    file->file_pos = FS_OFFSET_APPEND; 
  } 
 
  /* Do the actual write. */ 
  if (count > 0) { 
    result = file->vp->vops->write (file->vp, file->file_pos, buf, count); 
 
    /* Adjust the file pointer appropriately. */ 
    /* XXX: This won't have the correct value if the file is opened in 
     * append mode. */ 
    if (result > 0) { 
      file->file_pos += result; 
    } 
  } 
  else 
    result = 0; 
 
  return result; 
} 
 
/* efs_mkdir */ 
int 
efs_mkdir (const char *path, fs_mode_t mode) 
{ 
  struct nameidata *ndp = &nameidata_buffer; 
  int result; 
 
  FS_GLOBAL_LOCK (); 
  EFS_TT_PRINT (EFS_TT_MKDIR, path); 
 
  result = fs_namei (ndp, path, O_EXCL | O_CREAT); 
 
  if (result < 0) 
  { 
    goto clean_up; 
  } 
 
  result = ndp->dvp->vops->mkdir (ndp, mode); 
 
  if (ndp->dvp != NULL) 
    fs_vnode_unref (ndp->dvp); 
  if (ndp->vp != NULL) 
    fs_vnode_unref (ndp->vp); 
 
clean_up: 
  EFS_TT_RETURN (-result); 
  FS_GLOBAL_UNLOCK (); 
  if (result == 0) 
    return 0; 
  else { 
    efs_errno = -result; 
    return -1; 
  } 
} 
 
int 
efs_creat (const char *path, fs_mode_t mode) 
{ 
  EFS_TT_API (EFS_TT_CREAT); 
  return efs_open (path, O_CREAT | O_WRONLY | O_TRUNC, mode); 
} 
 
int 
efs_close (int filedes) 
{ 
  struct fs_descriptor *desc_ptr; 
  int result; 
 
  FS_GLOBAL_LOCK (); 
  EFS_TT_VALUE (EFS_TT_CLOSE, (uint16)filedes, TRUE, TRUE, FALSE); 
 
  /* Check if file descriptor is valid */ 
  if ((desc_ptr = fs_desc_lookup (filedes)) == NULL) 
  { 
    efs_errno = EBADF; 
    FS_GLOBAL_UNLOCK (); 
    return -1; 
  } 
 
  result = desc_ptr->dops->close (desc_ptr); 
 
  fs_desc_free (desc_ptr); 
 
  FS_GLOBAL_UNLOCK (); 
 
  return result; 
} 
 
/* Close handler for filesystem (through vnode) operations. */ 
static int 
fs_public_desc_close (struct fs_descriptor *file) 
{ 
  /* Dereference the vnode.  There should be no need to flush. */ 
  file->state = 0; 
  fs_vnode_unref (file->vp); 
  file->vp = 0; 
 
  return 0; 
} 
 
fs_off_t 
efs_lseek (int filedes, fs_off_t offset, int whence) 
{ 
  struct fs_descriptor *desc_ptr; 
  fs_off_t              base; 
  static struct  fs_stat sbuf; 
  int result; 
 
  FS_GLOBAL_LOCK (); 
  EFS_TT_VALUE (EFS_TT_LSEEK, (uint16)filedes, TRUE, TRUE, FALSE); 
  EFS_TT_VALUE (EFS_TT_LSEEK, (uint32)offset,  TRUE, TRUE, TRUE); 
 
  /* Check if file descriptor is valid */ 
  if ((desc_ptr = fs_desc_lookup (filedes)) == NULL) 
  { 
    efs_errno = EBADF; 
    FS_GLOBAL_UNLOCK (); 
    EFS_TT_RETURN (-EBADF); 
    return -1; 
  } 
 
  /* This is only valid with files. */ 
  if (desc_ptr->state != FS_DESC_STATE_FILE) { 
    efs_errno = ESPIPE; 
    FS_GLOBAL_UNLOCK (); 
    EFS_TT_RETURN (-ESPIPE); 
    return -1; 
  } 
 
  switch (whence) { 
    case SEEK_SET: 
      base = 0; 
      break; 
 
    case SEEK_CUR: 
      base = desc_ptr->file_pos; 
      break; 
 
    case SEEK_END: 
      // base = desc_ptr->vp->size; 
      result = desc_ptr->vp->vops->getstat (desc_ptr->vp, &sbuf); 
      if (result != 0) { 
        efs_errno = -result; 
        FS_GLOBAL_UNLOCK (); 
        EFS_TT_RETURN (-efs_errno); 
        return -1; 
      } 
      base = sbuf.st_size; 
      break; 
 
    default: 
      EFS_TT_RETURN (-EINVAL); 
      FS_GLOBAL_UNLOCK (); 
      efs_errno = EINVAL; 
      return -1; 
  } 
 
  if (base + offset >= 0) { 
    desc_ptr->file_pos = base + offset; 
    base = desc_ptr->file_pos; 
  } 
  else { 
    efs_errno = EINVAL; 
    base = -1; 
  } 
 
  FS_GLOBAL_UNLOCK (); 
 
  EFS_TT_RETURN (base); 
  return base; 
} 
 
fs_off_t 
efs_truncate (const char *path, fs_off_t length) 
{ 
  struct nameidata *ndp = &nameidata_buffer; 
  int result; 
 
  FS_GLOBAL_LOCK (); 
  EFS_TT_API (EFS_TT_TRUNCATE); 
 
  result = fs_namei (ndp, path, 0); 
 
  if (result != 0) 
    goto error_case; 
 
  result = ndp->vp->vops->truncate (ndp->vp, length); 
 
error_case: 
  if (ndp->dvp != NULL) 
    fs_vnode_unref (ndp->dvp); 
  if (ndp->vp != NULL) 
    fs_vnode_unref (ndp->vp); 
 
  FS_GLOBAL_UNLOCK (); 
 
  if (result < 0) { 
    efs_errno = -result; 
    return -1; 
  } else 
    return 0; 
 
} 
 
fs_off_t 
efs_ftruncate (int fd, fs_off_t length) 
{ 
  struct fs_descriptor *desc; 
  int result; 
 
  FS_GLOBAL_LOCK (); 
  EFS_TT_API (EFS_TT_FTRUNCATE); 
 
  desc = fs_desc_lookup (fd); 
  if (desc == NULL) { 
    result = -EBADF; 
    goto clean_up; 
  } 
 
  /* Check that we have permissions to write this file.  Use the trick 
   * described in fs_public_desc_write. 
   */ 
  if ((((desc->mode & O_ACCMODE) + 1) & 2) == 0) { 
    result = -EINVAL; 
    goto clean_up; 
  } 
 
  result = desc->vp->vops->truncate (desc->vp, length); 
 
clean_up: 
  FS_GLOBAL_UNLOCK (); 
  if (result < 0) { 
    efs_errno = -result; 
    return -1; 
  } else 
    return result; 
} 
 
int 
efs_stat (const char *path, struct fs_stat *buf) 
{ 
  struct nameidata *ndp = &nameidata_buffer; 
  int result; 
  struct fs_vnode *vp; 
 
  FS_GLOBAL_LOCK (); 
  EFS_TT_PRINT (EFS_TT_STAT, path); 
 
  result = fs_namei (ndp, path, 0); 
 
  if (result != 0) 
    goto error_case; 
 
  vp = ndp->vp; 
 
  result = vp->vops->getstat (vp, buf); 
 
error_case: 
  if (ndp->dvp != NULL) 
    fs_vnode_unref (ndp->dvp); 
  if (ndp->vp != NULL) 
    fs_vnode_unref (ndp->vp); 
 
  FS_GLOBAL_UNLOCK (); 
 
  if (result < 0) { 
    EFS_TT_RETURN(-result); 
    efs_errno = -result; 
    return -1; 
  } else 
    return 0; 
} 
 
int 
efs_fstat (int fd, struct fs_stat *buf) 
{ 
  struct fs_descriptor *desc_ptr; 
  struct fs_vnode      *vp; 
  int result; 
 
  FS_GLOBAL_LOCK (); 
  EFS_TT_VALUE (EFS_TT_FSTAT, (uint16)fd, TRUE, TRUE, FALSE); 
 
  /* Check if file descriptor is valid */ 
  if ((desc_ptr = fs_desc_lookup (fd)) == NULL) 
  { 
    efs_errno = EBADF; 
    FS_GLOBAL_UNLOCK (); 
    return -1; 
  } 
 
  /* This is only valid with files. */ 
  if (desc_ptr->state != FS_DESC_STATE_FILE) { 
    efs_errno = ESPIPE; 
    FS_GLOBAL_UNLOCK (); 
    return -1; 
  } 
 
  vp = desc_ptr->vp; 
 
  result = vp->vops->getstat (vp, buf); 
 
  FS_GLOBAL_UNLOCK (); 
 
  if (result == 0) 
    return 0; 
  else { 
    efs_errno = -result; 
    return -1; 
  } 
} 
 
int 
efs_lstat (const char *path, struct fs_stat *buf) 
{ 
  struct nameidata *ndp = &nameidata_buffer; 
  int result; 
  struct fs_vnode *vp; 
 
  FS_GLOBAL_LOCK (); 
  EFS_TT_PRINT (EFS_TT_LSTAT, path); 
 
  result = fs_namei (ndp, path, O_NOFOLLOW); 
 
  if (result != 0) 
    goto error_case; 
 
  vp = ndp->vp; 
 
  result = vp->vops->getstat (vp, buf); 
 
error_case: 
  if (ndp->dvp != NULL) 
    fs_vnode_unref (ndp->dvp); 
  if (ndp->vp != NULL) 
    fs_vnode_unref (ndp->vp); 
 
  FS_GLOBAL_UNLOCK (); 
 
  if (result < 0) { 
    EFS_TT_RETURN (-result); 
    efs_errno = -result; 
    return -1; 
  } else 
    return 0; 
} /* efs_lstat */ 
 
int 
efs_statvfs (const char *path, struct fs_statvfs *buf) 
{ 
  struct nameidata *ndp = &nameidata_buffer; 
  int result; 
  struct fs_vnode *vp; 
 
  FS_GLOBAL_LOCK (); 
  EFS_TT_PRINT (EFS_TT_STATVFS, path); 
 
  result = fs_namei (ndp, path, 0); 
 
  if (result != 0) 
    goto error_case; 
 
  vp = ndp->vp; 
 
  result = vp->vops->getstatvfs (vp, buf); 
 
error_case: 
  if (ndp->dvp != NULL) 
    fs_vnode_unref (ndp->dvp); 
  if (ndp->vp != NULL) 
    fs_vnode_unref (ndp->vp); 
 
  FS_GLOBAL_UNLOCK (); 
 
  if (result < 0) 
  { 
    EFS_TT_RETURN (-result); 
    efs_errno = -result; 
    return -1; 
  } 
 
  return 0; 
} /* END efs_statvfs */ 
 
int 
efs_fstatvfs(int fd, struct fs_statvfs *buf) 
{ 
  struct fs_descriptor * desc_ptr; 
  struct fs_vnode * vp; 
  int result; 
 
  FS_GLOBAL_LOCK(); 
  EFS_TT_API (EFS_TT_FSTATVFS); 
  /*Check if file descriptor is valid*/ 
  if ((desc_ptr = fs_desc_lookup (fd)) == NULL) 
  { 
    efs_errno = EBADF; 
    FS_GLOBAL_UNLOCK (); 
    return -1; 
  } 
 
  /* This is only valid with files. */ 
  if (desc_ptr->state != FS_DESC_STATE_FILE) { 
    efs_errno = ESPIPE; 
    FS_GLOBAL_UNLOCK (); 
    return -1; 
  } 
 
  vp = desc_ptr->vp; 
 
  result = vp->vops->getstatvfs (vp, buf); 
 
  FS_GLOBAL_UNLOCK (); 
 
  if (result < 0) { 
    efs_errno = -result; 
    return -1; 
  } 
  return 0; 
} 
 
int 
efs_unlink (const char *path) 
{ 
  struct nameidata *ndp = &nameidata_buffer; 
  int result; 
 
  FS_GLOBAL_LOCK (); 
  EFS_TT_PRINT (EFS_TT_UNLINK, path); 
 
  result = fs_namei (ndp, path, O_NOFOLLOW); 
 
  if (result != 0) { 
    goto error_case; 
  } 
 
  if (!S_ISREG (ndp->vp->mode) && !S_ISLNK (ndp->vp->mode) && 
      !S_ISBLK (ndp->vp->mode) && !S_ISCHR (ndp->vp->mode) && 
      !S_ISITM (ndp->vp->mode)) { 
    /* Posix actually says to return EPERM for this case.  EISDIR seems to 
     * make more sense, but we'll comply with Posix. */ 
    result = -EPERM; 
    fs_vnode_unref (ndp->vp); 
    goto error_case; 
  } 
 
  if (S_ISREG (ndp->vp->mode)) { 
    /* If the file is already open, return an error. 
     * XXX: This is not ideal behavior. At some point, we should handle 
     * unlinking open files. 
     */ 
    if (fs_vnode_getref (ndp->vp) > 1) { 
      result = -ETXTBSY; 
      fs_vnode_unref (ndp->vp); 
      goto error_case; 
    } 
  } 
 
  result = ndp->dvp->vops->unlink (ndp); 
 
error_case: 
  if (ndp->dvp != NULL) 
    fs_vnode_unref (ndp->dvp); 
  /* The entry vnode was unreferenced by the unlink operation itself. */ 
 
  FS_GLOBAL_UNLOCK (); 
 
  if (result < 0) { 
    EFS_TT_RETURN(-result); 
    efs_errno = -result; 
    return -1; 
  } else 
    return 0; 
 
} /* END efs_unlink */ 
 
EFSDIR * 
efs_opendir (const char *dirname) 
{ 
  int i; 
  EFSDIR *iter; 
  struct nameidata *ndp = &nameidata_buffer; 
  int result; 
 
  FS_GLOBAL_LOCK (); 
  EFS_TT_PRINT (EFS_TT_OPENDIR, dirname); 
 
  for (i = 0; i < FS_MAX_ITERATORS; i++) { 
    if (!all_iters[i].busy) 
      break; 
  } 
  if (i == FS_MAX_ITERATORS) { 
    efs_errno = EMFILE; 
    EFS_TT_RETURN (-EMFILE); 
    FS_GLOBAL_UNLOCK (); 
    return NULL; 
  } 
  iter = &all_iters[i]; 
 
  result = fs_namei (ndp, dirname, 0); 
 
  if (result != 0) { 
    iter->iter = NULL; 
    goto error_case; 
  } 
 
  iter->iter = ndp->vp->vops->opendir (ndp->vp); 
  if (iter->iter == NULL) { 
    fs_vnode_unref (ndp->vp); 
    result = -ENOTDIR; 
  } else { 
    iter->busy = 1; 
 
    /* Copy the reference in, so don't unref it until close. */ 
    iter->vp = ndp->vp; 
    iter->mp = ndp->vp->mp; 
  } 
 
error_case: 
  if (ndp->dvp != NULL) 
    fs_vnode_unref (ndp->dvp); 
 
  FS_GLOBAL_UNLOCK (); 
 
  if (result < 0) { 
    efs_errno = -result; 
    EFS_TT_RETURN (-result); 
    return NULL; 
  } else 
    return iter; 
} 
 
struct fs_dirent * 
efs_readdir (EFSDIR *dirp) 
{ 
  int result; 
 
  FS_GLOBAL_LOCK (); 
  EFS_TT_API (EFS_TT_READDIR); 
  if (dirp == NULL || !dirp->busy) { 
    FS_GLOBAL_UNLOCK (); 
    efs_errno = EINVAL; 
    EFS_TT_RETURN (-EINVAL); 
    return 0; 
  } 
 
  result = dirp->vp->vops->readdir (dirp->vp, dirp->iter, 
      &dirp->dirent); 
 
  FS_GLOBAL_UNLOCK (); 
 
  if (result == -EEOF) { 
    dirp->dirent.d_name[0] = '\0'; 
    EFS_TT_RETURN (-EEOF); 
    return 0; 
  } 
  else if (result < 0) { 
    EFS_TT_RETURN (-result); 
    efs_errno = -result; 
    return 0; 
  } else { 
    return &dirp->dirent; 
  } 
 
} 
 
int 
efs_closedir (EFSDIR *dirp) 
{ 
  int result; 
 
  FS_GLOBAL_LOCK (); 
  EFS_TT_API (EFS_TT_CLOSEDIR); 
  if (dirp == NULL || !dirp->busy) { 
    FS_GLOBAL_UNLOCK (); 
    efs_errno = EINVAL; 
    return -1; 
  } 
 
  result = dirp->vp->vops->closedir (dirp->vp, dirp->iter); 
 
  dirp->busy = 0; 
  fs_vnode_unref (dirp->vp); 
 
  FS_GLOBAL_UNLOCK (); 
 
  if (result < 0) { 
    efs_errno = -result; 
    return -1; 
  } else 
    return 0; 
} 
 
int 
efs_rename (const char *oldpath, const char *newpath) 
{ 
  struct nameidata *ndp1 = &nameidata_buffer; 
  struct nameidata *ndp2 = &nameidata_buf2; 
  int result; 
  struct fs_stat sbuf; 
 
  FS_GLOBAL_LOCK (); 
  EFS_TT_PRINT (EFS_TT_RENAME, oldpath); 
  EFS_TT_PRINT (EFS_TT_RENAME, newpath); 
 
  ndp1->dvp = ndp1->vp = NULL; 
  ndp2->dvp = ndp2->vp = NULL; 
 
  result = fs_namei (ndp1, oldpath, O_NOFOLLOW); 
  if (result == 0) { 
    result = ndp1->vp->vops->getstat (ndp1->vp, &sbuf); 
    if (result == 0 && (sbuf.st_mode & S_IFMT) != S_IFREG) { 
      EFS_TT_RETURN (-EISDIR); 
      result = -EISDIR; 
    } 
  } 
 
  if (result == 0) { 
    result = fs_namei (ndp2, newpath, O_CREAT); 
  } 
 
  if (result == 0) { 
    result = ndp1->dvp->vops->rename (ndp1, ndp2); 
  } 
  /* rename call unrefs on error. */ 
  if (ndp1->dvp != NULL) { 
    fs_vnode_unref (ndp1->dvp); 
  } 
  if (ndp1->vp != NULL) { 
    fs_vnode_unref (ndp1->vp); 
  } 
  if (ndp2->dvp != NULL) { 
    fs_vnode_unref (ndp2->dvp); 
  } 
  if (ndp2->vp != NULL) { 
    fs_vnode_unref (ndp2->vp); 
  } 
 
  if (result != 0) { 
    EFS_TT_RETURN (-result); 
    efs_errno = -result; 
    result = -1; 
  } 
 
  FS_GLOBAL_UNLOCK (); 
 
  return result; 
} 
 
int 
efs_rmdir (const char *path) 
{ 
  struct nameidata *ndp = &nameidata_buffer; 
  int result; 
 
  FS_GLOBAL_LOCK (); 
  EFS_TT_PRINT (EFS_TT_RMDIR, path); 
 
  result = fs_namei (ndp, path, 0); 
 
  if (result != 0) 
    goto error_case; 
 
  /* If they try to delete '.', or '..', or trailing slash. */ 
  if (ndp->vp == ndp->dvp || 
      (ndp->length == 2 && ndp->next[0] == '.' && ndp->next[1] == '.')) 
  { 
    result = -EINVAL; 
    goto error_case; 
  } 
 
  result = ndp->dvp->vops->rmdir (ndp); 
 
  if (result != 0) 
    fs_vnode_unref (ndp->vp); 
  ndp->vp = NULL; 
 
error_case: 
  if (ndp->dvp != NULL) 
    fs_vnode_unref (ndp->dvp); 
  if (ndp->vp != NULL) 
    fs_vnode_unref (ndp->vp); 
  FS_GLOBAL_UNLOCK (); 
 
  if (result < 0) { 
    efs_errno = -result; 
    return -1; 
  } else 
    return 0; 
 
} /* END efs_rmdir */ 
 
int 
efs_chmod (const char *path, fs_mode_t mode) 
{ 
  int result; 
  struct nameidata *ndp = &nameidata_buffer; 
 
  EFS_TT_API (EFS_TT_CHMOD); 
 
  FS_GLOBAL_LOCK (); 
  /* Check to see if the path is valid */ 
  result = fs_namei (ndp, path, 0); 
 
  /* If file exists, change the mode on this vp */ 
  if (result == 0) 
    result = ndp->vp->vops->chmod (ndp->vp, mode); 
 
  if (ndp->dvp != NULL) 
    fs_vnode_unref (ndp->dvp); 
  if (ndp->vp != NULL) 
    fs_vnode_unref (ndp->vp); 
  FS_GLOBAL_UNLOCK (); 
 
  if (result < 0) 
  { 
    efs_errno = -result; 
    return -1; 
  } 
  else 
    return 0; 
} /* END efs_chmod */ 
 
/********************************************************************** 
 * Mountpoints. 
 */ 
int 
efs_mount (const char *special, const char *dir, 
    const char *fstype, int flags, const void *data) 
{ 
  int result; 
  struct nameidata *ndp = &nameidata_buffer; 
 
  (void) flags; 
  (void) special; 
 
  FS_GLOBAL_LOCK (); 
  EFS_TT_PRINT (EFS_TT_MOUNT, dir); 
 
  result = fs_namei (ndp, dir, 0); 
 
  if (result == 0 && !S_ISDIR (ndp->vp->mode)) { 
    result = -ENOTDIR; 
  } 
 
  if (result == 0) { 
    result = fs_mount_mount (ndp->vp, fstype, data); 
  } 
 
  if (ndp->dvp != NULL) 
    fs_vnode_unref (ndp->dvp); 
  if (ndp->vp != NULL) 
    fs_vnode_unref (ndp->vp); 
 
  FS_GLOBAL_UNLOCK (); 
 
  if (result < 0) { 
    efs_errno = -result; 
    return -1; 
  } else 
    return 0; 
} 
 
int 
efs_umount (const char *target) 
{ 
  int result; 
  struct nameidata *ndp = &nameidata_buffer; 
  struct fs_mount *mp = NULL; 
  int i; 
 
  FS_GLOBAL_LOCK (); 
  EFS_TT_PRINT (EFS_TT_UMOUNT, target); 
 
  result = fs_namei (ndp, target, 0); 
 
  if (result == 0) { 
    result = fs_mount_umount (ndp->vp); 
    mp = ndp->vp->mp; 
  } 
 
  if (result == 0 && mp != NULL) { 
    fs_desc_make_nodev (mp); 
 
    /* printf ("umount ref = %d\n", ndp->vp->ref); */ 
    fs_vnode_make_nodev (ndp->vp); 
 
    /* Cleanup any iterators. */ 
    for (i = 0; i < FS_MAX_ITERATORS; i++) { 
      if (all_iters[i].busy && 
          all_iters[i].vp != NULL && 
          all_iters[i].mp == mp) 
      { 
        /* If this vnode hasn't been obliterated already, clean it up. */ 
        if (all_iters[i].vp->mp == mp) 
          fs_vnode_make_nodev (all_iters[i].vp); 
      } 
    } 
  } 
 
  if (ndp->dvp != NULL) 
    fs_vnode_unref (ndp->dvp); 
  if (ndp->vp != NULL) 
    fs_vnode_unref (ndp->vp); 
 
#ifdef FS_UNIT_TEST 
#error code not present 
#endif 
 
  FS_GLOBAL_UNLOCK (); 
 
  if (result < 0) { 
    efs_errno = -result; 
    return -1; 
  } else 
    return 0; 
} 
 
int 
efs_remount (const char *oldtarget, const char *newtarget) 
{ 
  int result; 
  struct nameidata *ndp1 = &nameidata_buffer; 
  struct nameidata *ndp2 = &nameidata_buf2; 
  FS_GLOBAL_LOCK (); 
 
  EFS_TT_PRINT (EFS_TT_REMOUNT, oldtarget); 
  EFS_TT_PRINT (EFS_TT_REMOUNT, newtarget); 
  ndp1->dvp = ndp1->vp = NULL; 
  ndp2->dvp = ndp2->vp = NULL; 
 
  result = fs_namei (ndp1, oldtarget, 0); 
 
  if (result == 0 && !S_ISDIR (ndp1->vp->mode)) { 
    result = -EINVAL; 
  } 
 
  if (result == 0) { 
    result = fs_namei (ndp2, newtarget, 0); 
  } 
 
  if (result == 0 && !S_ISDIR (ndp2->vp->mode)) { 
    result = -ENOTDIR; 
  } 
 
  if (result == 0) { 
    result = fs_mount_remount (ndp1->vp, ndp2->vp); 
  } 
 
  if (ndp1->dvp != NULL) { 
    fs_vnode_unref (ndp1->dvp); 
  } 
  if (ndp1->vp != NULL) { 
    fs_vnode_unref (ndp1->vp); 
  } 
  if (ndp2->dvp != NULL) { 
    fs_vnode_unref (ndp2->dvp); 
  } 
  if (ndp2->vp != NULL) { 
    fs_vnode_unref (ndp2->vp); 
  } 
 
  if (result != 0) { 
    efs_errno = -result; 
    result = -1; 
  } 
 
  FS_GLOBAL_UNLOCK (); 
 
  return result; 
} 
 
int 
efs_symlink (const char *oldpath, const char *newpath) 
{ 
  struct nameidata *ndp = &nameidata_buffer; 
  int result; 
 
  FS_GLOBAL_LOCK (); 
  EFS_TT_PRINT (EFS_TT_SYMLINK, oldpath); 
  EFS_TT_PRINT (EFS_TT_SYMLINK, newpath); 
 
  result = fs_namei (ndp, newpath, O_EXCL | O_CREAT | O_NOFOLLOW); 
  if (result < 0) { 
    goto error_case; 
  } 
 
  result = ndp->dvp->vops->symlink (ndp, oldpath); 
 
error_case: 
  if (ndp->dvp != NULL) 
    fs_vnode_unref (ndp->dvp); 
 
  FS_GLOBAL_UNLOCK (); 
 
  if (result < 0) { 
    efs_errno = -result; 
    return -1; 
  } else 
    return 0; 
} 
 
int 
efs_readlink (const char *path, char *buf, fs_size_t bufsiz) 
{ 
  struct nameidata *ndp = &nameidata_buffer; 
  int result; 
 
  FS_GLOBAL_LOCK (); 
  EFS_TT_PRINT (EFS_TT_READLINK, path); 
 
  result = fs_namei (ndp, path, O_NOFOLLOW); 
  if (result < 0) { 
    goto error_case; 
  } 
 
  result = ndp->vp->vops->readlink (ndp->vp, buf, bufsiz); 
 
error_case: 
  if (ndp->dvp != NULL) 
    fs_vnode_unref (ndp->dvp); 
  if (ndp->vp != NULL) 
    fs_vnode_unref (ndp->vp); 
 
  FS_GLOBAL_UNLOCK (); 
 
  if (result < 0) { 
    efs_errno = -result; 
    return -1; 
  } else 
    return result; 
} 
 
 
int 
efs_access (const char *path, int amode) 
{ 
  (void) path; 
  (void) amode; 
 
  /* efs_access is not implemented in EFS2.  At this point, there is no 
   * intent on doing so.  It is part of a security model not well suited to 
   * mobile devices. */ 
  EFS_TT_API (EFS_TT_ACCESS); 
  efs_errno = EINVAL; 
  return -1; 
} 
 
int 
efs_mknod (const char *path, int mode, fs_devspecial_t dev) 
{ 
  struct nameidata *ndp = &nameidata_buffer; 
  int result = 0; 
 
  FS_GLOBAL_LOCK(); 
  EFS_TT_PRINT (EFS_TT_MKNOD, path); 
 
  result = fs_namei (ndp, path, O_EXCL | O_CREAT); 
 
  /* If the name lookup succeeds, the file already exists */ 
  if (ndp->vp != NULL) 
    result = -EEXIST; 
 
  /* Just attempt to create the device file (XXX ndp->dvp safe???)*/ 
  if (result == 0) 
    result = ndp->dvp->vops->mknod (ndp, mode, dev); 
 
  /* Done.  */ 
  if (ndp->dvp) 
    fs_vnode_unref (ndp->dvp); 
 
  if (ndp->vp) 
    fs_vnode_unref (ndp->vp); 
 
  FS_GLOBAL_UNLOCK (); 
 
  if (result != 0) { 
    efs_errno = -result; 
    result = -1; 
  } 
 
  return result; 
} 
 
 
/********************************************************************** 
 * FUNCTION      efs_raw_put 
 * 
 * Store a value in a special item file. 
 */ 
int 
efs_raw_put (const char *path, void *data, fs_size_t length, 
    int oflag, int mode) 
{ 
  struct nameidata *ndp = &nameidata_buffer; 
  int result = 0; 
 
  FS_GLOBAL_LOCK (); 
  EFS_TT_PRINT (EFS_TT_RAW_PUT, path); 
 
  if (result == 0) 
    result = fs_namei (ndp, path, oflag); 
 
  if (result == 0 && ndp->vp) { 
    /* Verify that if we did recover a 'node' that it is really an item 
     * type. */ 
    if (!S_ISITM (ndp->vp->mode)) 
      result = -EEXIST; 
  } 
 
  if (result == 0) { 
    result = ndp->dvp->vops->put_item (ndp, data, length, mode); 
  } 
 
  if (ndp->dvp) { 
    fs_vnode_unref (ndp->dvp); 
  } 
  if (ndp->vp) 
    fs_vnode_unref (ndp->vp); 
 
  if (result != 0) { 
    efs_errno = -result; 
    result = -1; 
  } 
 
  FS_GLOBAL_UNLOCK (); 
 
  return result; 
} 
 
/* Read contents of item. */ 
int 
efs_raw_get (const char *path, void *data, fs_size_t length) 
{ 
  struct nameidata *ndp = &nameidata_buffer; 
  int result; 
  struct fs_vnode *vp; 
 
  FS_GLOBAL_LOCK (); 
  EFS_TT_PRINT (EFS_TT_RAW_GET, path); 
 
  result = fs_namei (ndp, path, 0); 
 
  if (result == 0) { 
    vp = ndp->vp; 
    result = vp->vops->get_item (vp, data, length); 
  } 
 
  if (ndp->dvp != NULL) 
    fs_vnode_unref (ndp->dvp); 
  if (ndp->vp != NULL) 
    fs_vnode_unref (ndp->vp); 
 
  FS_GLOBAL_UNLOCK (); 
 
  if (result < 0) { 
    efs_errno = -result; 
    return -1; 
  } else 
    return result; 
} 
 
/*********************************************************************** 
 * FUNCTION      efs_wait_for_xact 
 * 
 * DESCRIPTION   This function waits for a transaction to finish. 
 * 
 ************************************************************************/ 
void 
efs_wait_for_xact (void) 
{ 
  EFS_TT_API (EFS_TT_WAIT_FOR_XACT); 
  FS_GLOBAL_LOCK(); 
} /* efs_wait_for_xact */ 
 
/********************************************************************** 
 * FUNCTION      efs_reset 
 * 
 * DESCRIPTION 
 *             Resets the filesystem and stops the system. The mobile must be 
 *             rebooted before it can be used again. 
 ************************************************************************/ 
void 
efs_reset (void) 
{ 
  /* Invalidate all superblocks and halt the system. This will force a 
   * fresh start on the next reboot. 
   * 
   * Make sure we prevent all filesystem operations until we reboot, so we 
   * don't trip on the inconsistent state while the erase is running. 
   */ 
  FS_GLOBAL_LOCK(); 
  EFS_TT_API (EFS_TT_RESET); 
 
  fs_pm_super_invalidate_superblocks (); 
 
#if defined(FEATURE_FS_REMOTE_APPS_PROC) && defined(FEATURE_REMOTE_EFS_RESET) 
  /* For dual proc targets, also reset efs on the modem processor */ 
  remote_efs_reset (); 
#endif 
 
  FS_ERR_FATAL ("filesystem reset. reboot needed", 0, 0, 0); 
} /* efs_reset */ 
 
/*********************************************************************** 
 * FUNCTION    efs_reset_nostop (void) 
 * 
 * DESCRIPTION 
 *   Same as efs_reset, except that it returns.  Should only be used from 
 *   within the FS_ERR_FATAL handler. 
 */ 
void 
efs_reset_nostop (void) 
{ 
  EFS_TT_API (EFS_TT_RESET_NOSTOP); 
  fs_pm_super_invalidate_superblocks (); 
} 
 
/*********************************************************************** 
FUNCTION      efs_get_fs_data 
 
DESCRIPTION 
 
DEPENDENCIES  None 
 
RETURN VALUE  If successful, returns 0. 
              Otherwise, returns -1, and sets errno to indicate the error. 
 
SIDE EFFECTS  None 
**********************************************************************/ 
extern int fs_get_fs_data(struct fs_factimage_read_info * read_info_ptr, 
                          byte * page_buffer); 
 
int 
efs_get_fs_data (struct fs_factimage_read_info * image_info_ptr, 
                 byte* page_buffer) 
{ 
  int result; 
 
  EFS_TT_API (EFS_TT_GET_FS_DATA); 
  if (image_info_ptr == NULL) { 
    efs_errno = EINVAL; 
    return -1; 
  } 
 
  if (image_info_ptr->stream_state < 0) { 
    efs_errno=EINVAL; 
    return -1; 
  } 
 
  FS_GLOBAL_LOCK (); 
  result = fs_get_fs_data(image_info_ptr, page_buffer); 
  FS_GLOBAL_UNLOCK (); 
 
  if (result < 0) { 
    efs_errno = -result; 
    return -1; 
  } else { 
    return 0; 
  } 
 
} /* end of efs_get_fs_data */ 
 
/*********************************************************************** 
 * FUNCTION      efs_get_device_info 
 * 
 * DESCRIPTION   This function will get the attributes of the flash device 
 * 
 * DEPENDENCIES  None 
 * 
 * RETURN VALUE  If successful efs_access will return zero else -1. 
 * 
 * SIDE EFFECTS  None 
 ***********************************************************************/ 
extern int fs_get_device_attr(struct fs_device_attr * dev_attr); 
int 
efs_get_device_info (struct fs_device_attr * dev_attr) 
{ 
  int result; 
 
  EFS_TT_API (EFS_TT_GET_DEVICE_INFO); 
  result = fs_get_device_attr(dev_attr); 
 
  return result; 
} /* efs_get_device_info */ 
 
/*********************************************************************** 
 * FUNCTION 
 *   efs_image_prepare 
 * 
 * DESCRIPTION 
 *   Make progress toward preparing this image for a factory dump.  This 
 *   does a small amount of work, of whatever kind is needed to prepare to 
 *   make an exportable image of this filesystem.  This function should be 
 *   called repeatedly until it returns that there is no work left to do. 
 * 
 * RETURN VALUE 
 *   Returns a count indicating any additional work.  This number should 
 *   only be compared against zero.  Positive indicates that there is work 
 *   to do, and negative indicates an error has occured. 
 ***********************************************************************/ 
/* XXX: To support multiple filesystems, this function needs to take an 
 * argument to indicate which FS to use. */ 
int 
efs_image_prepare (void) 
{ 
  extern int fs_efs2_image_prepare (void); 
 
  EFS_TT_API (EFS_TT_IMAGE_PREPARE); 
  return fs_efs2_image_prepare (); 
} 
 
 
/************************************************************************* 
 * 
 * efs_chown 
 * 
 * change the owner and/or group id 
 * 
 */ 
 
int 
efs_chown (const char *path, int uid_val, int gid_val) 
{ 
 
  int result; 
  struct nameidata *ndp = &nameidata_buffer; 
 
  result = 0; 
 
  FS_GLOBAL_LOCK (); 
  /* Check to see if the path is valid */ 
  result = fs_namei (ndp, path, 0); 
 
  if (result != 0) 
    goto cleanup; 
 
  /* Everything checks out so let us change the group id */ 
 
  result = ndp->vp->vops->chown(ndp->vp, uid_val, gid_val); 
 
cleanup: 
  if (ndp->dvp != NULL) 
    fs_vnode_unref(ndp->dvp); 
  if (ndp->vp != NULL) 
    fs_vnode_unref(ndp->vp); 
  FS_GLOBAL_UNLOCK (); 
 
  if (result < 0) 
  { 
    efs_errno = -result; 
    return -1; 
  } 
  else 
    return 0; 
} 
 
/************************************************************************* 
 * 
 * efs_set_reservation 
 * 
 * sets the amount of space for a reservation 
 * 
 */ 
 
int 
efs_set_reservation(const char *path, uint32 groupid, uint32 size) 
{ 
  int result; 
  struct nameidata *ndp = &nameidata_buffer; 
 
  result = 0; 
 
  FS_GLOBAL_LOCK (); 
 
  result = fs_namei (ndp, path, 0); 
 
  if (result != 0) 
    goto cleanup; 
 
  /* Everything checks out so let us set the reservation 
   */ 
 
  result = ndp->vp->vops->set_reservation(ndp->vp, groupid, size); 
 
cleanup: 
  if (ndp->dvp != NULL) 
    fs_vnode_unref(ndp->dvp); 
  if (ndp->vp != NULL) 
    fs_vnode_unref(ndp->vp); 
  FS_GLOBAL_UNLOCK (); 
 
  if (result < 0) 
  { 
    efs_errno = -result; 
    return -1; 
  } 
  else 
    return 0; 
 
} 
 
/************************************************************************* 
 * 
 * efs_set_quota 
 * 
 * sets the quota limit 
 * 
 */ 
 
int 
efs_set_quota(const char *path, uint32 groupid, uint32 size) 
{ 
  int result; 
  struct nameidata *ndp = &nameidata_buffer; 
 
  result = 0; 
 
  FS_GLOBAL_LOCK (); 
 
  /* We need a buffer to pass in updated values to the superblock so just 
   * get a buffer for where we are 
   */ 
  result = fs_namei (ndp, path, 0); 
 
  if (result != 0) 
    goto cleanup; 
 
  /* Everything checks out so let us set the quota 
   */ 
 
  result = ndp->vp->vops->set_quota(ndp->vp, groupid, size); 
 
cleanup: 
  if (ndp->dvp != NULL) 
    fs_vnode_unref(ndp->dvp); 
  if (ndp->vp != NULL) 
    fs_vnode_unref(ndp->vp); 
  FS_GLOBAL_UNLOCK (); 
 
  if (result < 0) 
  { 
    efs_errno = -result; 
    return -1; 
  } 
  else 
    return 0; 
 
} 
/************************************************************************* 
 * 
 * efs_get_group_info 
 * 
 * gets the information for quotas and reservations for a specific group 
 * id 
 * 
 */ 
 
int 
efs_get_group_info(const char *path, uint32 groupid, 
    struct fs_group_info * ginfo) 
{ 
  int result; 
  struct nameidata *ndp = &nameidata_buffer; 
 
  result = 0; 
 
  FS_GLOBAL_LOCK (); 
 
  result = fs_namei (ndp, path, 0); 
 
  if (result != 0) 
    goto cleanup; 
 
  /* Everything checks out so let us get the reservation 
   */ 
 
  result = ndp->vp->vops->get_group_info(ndp->vp, groupid, ginfo); 
 
cleanup: 
  if (ndp->vp != NULL) 
    fs_vnode_unref(ndp->vp); 
  FS_GLOBAL_UNLOCK (); 
 
  if (result < 0) 
  { 
    efs_errno = -result; 
    return -1; 
  } 
  else 
    return 0; 
 
}