vfsbase.cpp revision d73d8e542691e11ef04e58585073956555272810
/* $Id$ */
/** @file
* IPRT - Virtual File System, Base.
*/
/*
* Copyright (C) 2010 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* The contents of this file may alternatively be used under the terms
* of the Common Development and Distribution License Version 1.0
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
* VirtualBox OSE distribution, in which case the provisions of the
* CDDL are applicable instead of those of the GPL.
*
* You may elect to license modified versions of this file under the
* terms and conditions of either the GPL or the CDDL or both.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <iprt/vfslowlevel.h>
#include <iprt/semaphore.h>
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
#define RTVFS_MAGIC_DEAD (~RTVFS_MAGIC)
#define RTVFSDIR_MAGIC_DEAD (~RTVFSDIR_MAGIC)
#define RTVFSFILE_MAGIC_DEAD (~RTVFSFILE_MAGIC)
#define RTVFSIOSTREAM_MAGIC_DEAD (~RTVFSIOSTREAM_MAGIC)
#define RTVFSSYMLINK_MAGIC_DEAD (~RTVFSSYMLINK_MAGIC)
/** The instance data alignment. */
#define RTVFS_INST_ALIGNMENT 16U
/** The max number of symbolic links to resolve in a path. */
#define RTVFS_MAX_LINKS 20U
/** Takes a write lock. */
#define RTVFS_WRITE_LOCK(hSemRW) \
do { \
if ((hSemRW) != NIL_RTSEMRW) \
{ \
AssertRC(rcSemEnter); \
} \
} while (0)
/** Releases a write lock. */
#define RTVFS_WRITE_UNLOCK(hSemRW) \
do { \
if ((hSemRW) != NIL_RTSEMRW) \
{ \
AssertRC(rcSemLeave); \
} \
} while (0)
/** Takes a read lock. */
#define RTVFS_READ_LOCK(hSemRW) \
do { \
if ((hSemRW) != NIL_RTSEMRW) \
{ \
AssertRC(rcSemEnter); \
} \
} while (0)
/** Releases a read lock. */
#define RTVFS_READ_UNLOCK(hSemRW) \
do { \
if ((hSemRW) != NIL_RTSEMRW) \
{ \
AssertRC(rcSemLeave); \
} \
} while (0)
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* The VFS handle data.
*/
typedef struct RTVFSINTERNAL
{
/** The VFS magic (RTVFS_MAGIC). */
/** Creation flags (RTVFS_C_XXX). */
/** Pointer to the instance data. */
void *pvThis;
/** The vtable. */
/** Read-write semaphore protecting all access to the VFS
* Only valid RTVFS_C_THREAD_SAFE is set, otherwise it is NIL_RTSEMRW. */
/** The number of references to this VFS.
* This count includes objects within the file system, so that the VFS
* won't be destroyed before all objects are closed. */
/**
* The VFS directory handle data.
*/
typedef struct RTVFSDIRINTERNAL
{
/** The VFS magic (RTVFSDIR_MAGIC). */
/** Reserved for flags or something. */
/** Pointer to the instance data. */
void *pvThis;
/** The vtable. */
/** The VFS RW sem if serialized. */
/** Reference back to the VFS containing this directory. */
/** The number of references to this directory handle. This does not
* include files or anything. */
/**
* The VFS symbolic link handle data.
*/
typedef struct RTVFSSYMLINKINTERNAL
{
/** The VFS magic (RTVFSSYMLINK_MAGIC). */
/** Reserved for flags or something. */
/** Pointer to the instance data. */
void *pvThis;
/** The vtable. */
/** The VFS RW sem if serialized. */
/** Reference back to the VFS containing this symbolic link. */
/** The number of references to this symbolic link handle. */
/**
* The VFS I/O stream handle data.
*
* This is normally part of a type specific handle, like a file or pipe.
*/
typedef struct RTVFSIOSTREAMINTERNAL
{
/** The VFS magic (RTVFSIOSTREAM_MAGIC). */
/** File open flags, at a minimum the access mask. */
/** Pointer to the instance data. */
void *pvThis;
/** The vtable. */
/** The VFS RW sem if serialized. */
/** Reference back to the VFS containing this directory. */
/** The number of references to this file VFS. */
/**
* The VFS file handle data.
*
* @extends RTVFSIOSTREAMINTERNAL
*/
typedef struct RTVFSFILEINTERNAL
{
/** The VFS magic (RTVFSFILE_MAGIC). */
/** Reserved for flags or something. */
/** The vtable. */
/** The stream handle data. */
/**
* Internal object retainer that asserts sanity in strict builds.
*
* @returns The new reference count.
* @param pcRefs The reference counter.
*/
{
return cRefs;
}
/**
* Internal object retainer that asserts sanity in strict builds.
*
* @param pcRefs The reference counter.
*/
{
(void)rtVfsRetain(pcRefs);
}
/**
* Internal object releaser that asserts sanity in strict builds.
*
* @returns The new reference count.
* @param pcRefs The reference counter.
*/
{
return cRefs;
}
/*
*
* U T I L U T I L U T I L
* U T I L U T I L U T I L
* U T I L U T I L U T I L
*
*/
/**
* Removes dots from the path.
*
* @returns The new @a pszDst value.
* @param pPath The path parsing buffer.
* @param pszDst The current szPath position. This will be
* updated and returned.
* @param fTheEnd Indicates whether we're at the end of the path
* or not.
* @param piRestartComp The component to restart parsing at.
*/
static char *rtVfsParsePathHandleDots(PRTVFSPARSEDPATH pPath, char *pszDst, bool fTheEnd, uint16_t *piRestartComp)
{
return pszDst;
{
pPath->cComponents--;
}
{
}
else
return pszDst;
/*
* Drop the trailing slash if we're at the end of the source path.
*/
pszDst--;
return pszDst;
}
RTDECL(int) RTVfsParsePathAppend(PRTVFSPARSEDPATH pPath, const char *pszPath, uint16_t *piRestartComp)
{
/* In case *piRestartComp was set higher than the number of components
before making the call to this function. */
/*
* Append a slash to the destination path if necessary.
*/
if (pPath->cComponents > 0)
{
*pszDst++ = '/';
return VERR_FILENAME_TOO_LONG;
}
/*
* Parse and append the relative path.
*/
while (pszSrc[0])
{
/* Skip unncessary slashes. */
while (pszSrc[0] == '/')
pszSrc++;
/* Copy until we encounter the next slash. */
while (pszSrc[0])
{
if (pszSrc[0] == '/')
{
pszSrc++;
if (pszSrc[0])
*pszDst++ = '/';
else
break;
}
return VERR_FILENAME_TOO_LONG;
}
}
/* Terminate the string and enter its length. */
pszDst[0] = '\0';
return VINF_SUCCESS;
}
{
if (*pszPath != '/')
{
/*
* Relative, recurse and parse pszCwd first.
*/
if (RT_FAILURE(rc))
return rc;
}
else
{
/*
* Make pszPath relative, i.e. set up pPath for the root and skip
* leading slashes in pszPath before appending it.
*/
pPath->cComponents = 0;
while (pszPath[0] == '/')
pszPath++;
if (!pszPath[0])
return VINF_SUCCESS;
}
}
{
/*
* Allocate the output buffer and hand the problem to rtVfsParsePath.
*/
int rc;
if (pPath)
{
if (RT_FAILURE(rc))
{
}
}
else
return rc;
}
{
if (pPath)
{
}
}
/**
* Handles a symbolic link, adding it to
*
* @returns IPRT status code.
* @param pPath The parsed path to update.
* @param piComponent The component iterator to update.
* @param hSymlink The symbolic link to process.
*/
static int rtVfsTraverseHandleSymlink(PRTVFSPARSEDPATH pPath, uint16_t *piComponent, RTVFSSYMLINK hSymlink)
{
/*
* Read the link.
*/
char szPath[RTPATH_MAX];
if (RT_SUCCESS(rc))
{
if (szPath[0] == '/')
{
/*
* Absolute symlink.
*/
if (RT_SUCCESS(rc))
{
*piComponent = 0;
return VINF_SUCCESS;
}
}
else
{
/*
* Relative symlink, must replace the current component with the
* link value. We do that by using the remainder of the symlink
* buffer as temporary storage.
*/
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
return VINF_SUCCESS;
}
}
}
}
}
/**
* Internal worker for various open functions as well as RTVfsTraverseToParent.
*
* @returns IPRT status code.
* @param pThis The VFS.
* @param pPath The parsed path. This may be changed as symbolic
* links are processed during the path traversal.
* @param fFollowSymlink Whether to follow the final component if it is a
* symbolic link.
* @param ppVfsParentDir Where to return the parent directory handle
* (referenced).
*/
{
/*
* Assert sanity.
*/
*ppVfsParentDir = NULL;
/*
* Open the root directory.
*/
/** @todo Union mounts, traversal optimization methods, races, ++ */
if (RT_FAILURE(rc))
return rc;
/*
* The traversal loop.
*/
unsigned cLinks = 0;
uint16_t iComponent = 0;
for (;;)
{
/*
* Are we done yet?
*/
if (fFinal && !fFollowSymlink)
{
return VINF_SUCCESS;
}
/*
* Try open the next entry.
*/
*pszEntryEnd = '\0';
if (fFinal)
{
*pszEntryEnd = '\0';
if (rc == VERR_PATH_NOT_FOUND)
rc = VINF_SUCCESS;
if (RT_FAILURE(rc))
break;
if (hSymlink == NIL_RTVFSSYMLINK)
{
return VINF_SUCCESS;
}
}
else
{
*pszEntryEnd = '/';
if (RT_FAILURE(rc))
break;
if ( hDir == NIL_RTVFSDIR
&& hSymlink == NIL_RTVFSSYMLINK
{
break;
}
}
if (hDir != NIL_RTVFSDIR)
{
/*
* Directory - advance down the path.
*/
iComponent++;
}
else if (hSymlink != NIL_RTVFSSYMLINK)
{
/*
* Symbolic link - deal with it and retry the current component.
*/
cLinks++;
if (cLinks >= RTVFS_MAX_LINKS)
{
break;
}
if (RT_FAILURE(rc))
break;
if (iRestartComp != iComponent)
{
/* Must restart from the root (optimize this). */
if (RT_FAILURE(rc))
{
break;
}
iComponent = 0;
}
}
else
{
/*
* Mount point - deal with it and retry the current component.
*/
if (RT_FAILURE(rc))
{
break;
}
iComponent = 0;
/** @todo union mounts. */
}
}
if (pCurDir)
return rc;
}
/*
*
* D I R D I R D I R
* D I R D I R D I R
* D I R D I R D I R
*
*/
{
}
{
if (!cRefs)
{
}
return cRefs;
}
/*
*
* S Y M B O L I C L I N K
* S Y M B O L I C L I N K
* S Y M B O L I C L I N K
*
*/
{
}
{
if (!cRefs)
{
}
return cRefs;
}
{
return rc;
}
/*
*
* I / O S T R E A M I / O S T R E A M I / O S T R E A M
* I / O S T R E A M I / O S T R E A M I / O S T R E A M
* I / O S T R E A M I / O S T R E A M I / O S T R E A M
*
*/
RTDECL(int) RTVfsNewIoStream(PCRTVFSIOSTREAMOPS pIoStreamOps, size_t cbInstance, uint32_t fOpen, RTVFS hVfs, RTSEMRW hSemRW,
{
/*
* Validate the input, be extra strict in strict builds.
*/
Assert(cbInstance > 0);
{
}
/*
* Allocate the handle + instance data.
*/
if (!pThis)
return VERR_NO_MEMORY;
return VINF_SUCCESS;
}
{
}
{
if (!cRefs)
{
/*
* That was the last reference, close the stream.
*
* This is a little bit more complicated than when releasing a file or
* directory handle because the I/O stream can be a sub-object and we
* need to get to the real one before handing it to RTMemFree.
*/
{
case RTVFSOBJTYPE_IOSTREAM:
break;
case RTVFSOBJTYPE_FILE:
{
break;
}
/* Add new I/O stream compatible handle types here. */
default:
break;
}
}
return cRefs;
}
{
{
}
/* this is no crime, so don't assert. */
return NIL_RTVFSFILE;
}
RTDECL(int) RTVfsIoStrmQueryInfo(RTVFSIOSTREAM hVfsIos, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
{
return rc;
}
{
int rc = pThis->pOps->pfnRead(pThis->pvThis, -1 /*off*/, &SgBuf, pcbRead == NULL /*fBlocking*/, pcbRead);
return rc;
}
RTDECL(int) RTVfsIoStrmWrite(RTVFSIOSTREAM hVfsIos, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
{
int rc = pThis->pOps->pfnWrite(pThis->pvThis, -1 /*off*/, &SgBuf, pcbWritten == NULL /*fBlocking*/, pcbWritten);
return rc;
}
RTDECL(int) RTVfsIoStrmSgRead(RTVFSIOSTREAM hVfsIos, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
{
return rc;
}
RTDECL(int) RTVfsIoStrmSgWrite(RTVFSIOSTREAM hVfsIos, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
{
return rc;
}
{
return rc;
}
RTDECL(RTFOFF) RTVfsIoStrmPoll(RTVFSIOSTREAM hVfsIos, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
{
return rc;
}
{
if (RT_FAILURE(rc))
return off;
}
{
int rc;
{
}
else
{
if (pvBuf)
{
rc = VINF_SUCCESS;
while (cb > 0)
{
if (RT_FAILURE(rc))
break;
}
}
else
}
return rc;
}
{
int rc;
{
}
else
{
if (pvBuf)
{
rc = VINF_SUCCESS;
while (cb > 0)
{
if (RT_FAILURE(rc))
break;
}
}
else
}
return rc;
}
/*
*
* F I L E F I L E F I L E
* F I L E F I L E F I L E
* F I L E F I L E F I L E
*
*/
{
/*
* Validate the input, be extra strict in strict builds.
*/
Assert(cbInstance > 0);
{
}
/*
* Allocate the handle + instance data.
*/
if (!pThis)
return VERR_NO_MEMORY;
return VINF_SUCCESS;
}
RTDECL(int) RTVfsFileOpen(RTVFS hVfs, const char *pszFilename, uint32_t fOpen, PRTVFSFILE phVfsFile)
{
/*
* Validate input.
*/
if (RT_FAILURE(rc))
return rc;
/*
* Parse the path, assume current directory is root since we've got no
* caller context here.
*/
if (RT_SUCCESS(rc))
{
{
/*
* Tranverse the path, resolving the parent node and any symlinks
* in the final element, and ask the directory to open the file.
*/
if (RT_SUCCESS(rc))
{
/** @todo there is a symlink creation race here. */
if (RT_SUCCESS(rc))
{
}
}
}
else
}
return rc;
}
{
}
{
if (!cRefs)
{
}
return cRefs;
}
{
}