/* $Id$ */
/** @file
* IPRT - TAR Command.
*/
/*
* Copyright (C) 2013 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/buildconfig.h>
#include <iprt/initterm.h>
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** The max directory entry size. */
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/** Interactive option. */
typedef enum
{
/** @todo possible that we should by default prompt if removing read-only
* files or files owned by someone else. We currently don't. */
/**
* IPRT rm option structure.
*/
typedef struct RTPATHRMCMDOPTS
{
/** Whether to delete recursively. */
bool fRecursive;
/** Whether to delete directories as well as other kinds of files. */
bool fDirsAndOther;
/** Whether to remove files without prompting and ignoring non-existing
* files. */
bool fForce;
/** Machine readable output. */
bool fMachineReadable;
/** Don't try remove root ('/') if set, otherwise don't treat root specially. */
bool fPreserveRoot;
/** Whether to keep to one file system. */
bool fOneFileSystem;
/** Whether to safely delete files (overwrite 3x before unlinking). */
bool fSafeDelete;
/** Whether to be verbose about the operation. */
bool fVerbose;
/** The interactive setting. */
/** Pointer to the IPRT rm options. */
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** A bunch of zeros. */
/** A bunch of 0xFF bytes. (lazy init) */
{
if (!pOpts->fMachineReadable)
}
const char *pszFormat, ...)
{
if (pOpts->fMachineReadable)
else
{
}
return rc;
}
/**
* Worker that removes a symbolic link.
*
* @returns IPRT status code, errors go via rtPathRmError.
* @param pOpts The RM options.
* @param pszPath The path to the symbolic link.
*/
{
if (RT_FAILURE(rc))
return rc;
}
/**
* Worker that removes a file.
*
* Currently used to delete both regular and special files.
*
* @returns IPRT status code, errors go via rtPathRmError.
* @param pOpts The RM options.
* @param pszPath The path to the file.
* @param pObjInfo The FS object info for the file.
*/
{
int rc;
/*
* Wipe the file if requested and possible.
*/
{
/* Lazy init of the 0xff buffer. */
if (RT_FAILURE(rc))
{
if (RT_FAILURE(rc))
{
break;
}
{
if (RT_FAILURE(rc))
{
break;
}
}
}
if (RT_FAILURE(rc))
return rc;
}
/*
* Remove the file.
*/
if (RT_FAILURE(rc))
? "Error removing regular file '%s': %Rrc\n"
: "Error removing special file '%s': %Rrc\n",
return rc;
}
/**
* Deletes one directory (if it's empty).
*
* @returns IPRT status code, errors go via rtPathRmError.
* @param pOpts The RM options.
* @param pszPath The path to the directory.
*/
{
if (RT_FAILURE(rc))
return rc;
}
/**
* Recursively delete a directory.
*
* @returns IPRT status code, errors go via rtPathRmError.
* @param pOpts The RM options.
* @param pszPath Pointer to a writable buffer holding the path to
* the directory.
* @param cchPath The length of the path (avoid strlen).
* @param pDirEntry Pointer to a directory entry buffer that is
* RTPATHRM_DIR_MAX_ENTRY_SIZE bytes big.
*/
static int rtPathRmRecursive(PRTPATHRMCMDOPTS pOpts, char *pszPath, size_t cchPath, PRTDIRENTRYEX pDirEntry)
{
/*
* Make sure the path ends with a slash.
*/
{
return rtPathRmError(pOpts, pszPath, VERR_BUFFER_OVERFLOW, "Buffer overflow fixing up '%s'.\n", pszPath);
}
/*
* Traverse the directory.
*/
if (RT_FAILURE(rc))
for (;;)
{
/*
* Read the next entry, constructing an full path for it.
*/
if (rc == VERR_NO_MORE_FILES)
{
/*
* Reached the end of the directory.
*/
if (RT_FAILURE(rc))
/* Delete the directory. */
return rc2;
return rcRet;
}
if (RT_FAILURE(rc))
{
break;
}
/* Skip '.' and '..'. */
continue;
/* Construct full path. */
{
rc = rtPathRmError(pOpts, pszPath, VERR_BUFFER_OVERFLOW, "Path buffer overflow in directory '%s'.", pszPath);
break;
}
/*
* Take action according to the type.
*/
{
case RTFS_TYPE_FILE:
break;
case RTFS_TYPE_DIRECTORY:
break;
case RTFS_TYPE_SYMLINK:
break;
case RTFS_TYPE_FIFO:
case RTFS_TYPE_DEV_CHAR:
case RTFS_TYPE_DEV_BLOCK:
case RTFS_TYPE_SOCKET:
break;
case RTFS_TYPE_WHITEOUT:
default:
"Object '%s' has an unknown file type: %o\n",
break;
}
}
/*
* Some error occured, close and return.
*/
return rc;
}
/**
* Validates the specified file or directory.
*
* @returns IPRT status code, errors go via rtPathRmError.
* @param pOpts The RM options.
* @param pszPath The path to the file, directory, whatever.
*/
{
/*
* RTPathFilename doesn't do the trailing slash thing the way we need it to.
* E.g. both '..' and '../' should be rejected.
*/
cchPath--;
if ( ( cchPath == 0
|| 0 /** @todo drive letter + UNC crap */)
&& pOpts->fPreserveRoot)
return rtPathRmError(pOpts, pszPath, VERR_CANT_DELETE_DIRECTORY, "Cannot remove root directory ('%s').\n", pszPath);
offLast--;
&& ( cchLast == 1
return rtPathRmError(pOpts, pszPath, VERR_CANT_DELETE_DIRECTORY, "Cannot remove special directory '%s'.\n", pszPath);
return VINF_SUCCESS;
}
/**
* Remove one user specified file or directory.
*
* @returns IPRT status code, errors go via rtPathRmError.
* @param pOpts The RM options.
* @param pszPath The path to the file, directory, whatever.
*/
{
/*
* RM refuses to delete some directories.
*/
if (RT_FAILURE(rc))
return rc;
/*
* Query file system object info.
*/
if (RT_FAILURE(rc))
{
return VINF_SUCCESS;
}
/*
* Take type specific action.
*/
{
case RTFS_TYPE_FILE:
case RTFS_TYPE_DIRECTORY:
if (pOpts->fRecursive)
{
if (RT_FAILURE(rc))
union
{
} DirEntry;
}
if (pOpts->fDirsAndOther)
return rtPathRmError(pOpts, pszPath, VERR_IS_A_DIRECTORY, "Cannot remove '%s': %Rrc\n", pszPath, VERR_IS_A_DIRECTORY);
case RTFS_TYPE_SYMLINK:
case RTFS_TYPE_FIFO:
case RTFS_TYPE_DEV_CHAR:
case RTFS_TYPE_DEV_BLOCK:
case RTFS_TYPE_SOCKET:
case RTFS_TYPE_WHITEOUT:
default:
}
}
{
/*
* Parse the command line.
*/
{
/* operations */
/* IPRT extensions */
{ "--machinereadable", RTPATHRMCMD_OPT_MACHINE_READABLE, RTGETOPT_REQ_NOTHING }, /* bad long option style */
};
if (RT_FAILURE(rc))
Opts.fPreserveRoot = true;
&& rc != VINF_GETOPT_NOT_OPTION)
{
switch (rc)
{
case 'd':
Opts.fDirsAndOther = true;
break;
case 'f':
break;
case 'i':
break;
case 'I':
break;
else
return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown --interactive option value: '%s'\n", ValueUnion.psz);
break;
Opts.fOneFileSystem = true;
break;
Opts.fPreserveRoot = true;
break;
Opts.fPreserveRoot = false;
break;
case 'R':
case 'r':
Opts.fRecursive = true;
Opts.fDirsAndOther = true;
break;
case 'P':
Opts.fSafeDelete = true;
break;
case 'v':
break;
Opts.fMachineReadable = true;
break;
case 'h':
RTPrintf("Usage: to be written\nOption dump:\n");
for (unsigned i = 0; i < RT_ELEMENTS(s_aOptions); i++)
else
return RTEXITCODE_SUCCESS;
case 'V':
return RTEXITCODE_SUCCESS;
default:
}
}
/*
* Options we don't support.
*/
if (Opts.fOneFileSystem)
return RTMsgErrorExit(RTEXITCODE_FAILURE, "The --one-file-system option is not yet implemented.\n");
return RTMsgErrorExit(RTEXITCODE_FAILURE, "The -i, -I and --interactive options are not implemented yet.\n");
/*
* No files means error.
*/
/*
* Machine readable init + header.
*/
if (Opts.fMachineReadable)
{
if (RT_FAILURE(rc))
}
/*
*/
while (rc == VINF_GETOPT_NOT_OPTION)
{
if (RT_FAILURE(rc))
/* next */
}
if (rc != 0)
/*
* Terminate the machine readable stuff.
*/
if (Opts.fMachineReadable)
{
}
return rcExit;
}