You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2648 lines
66 KiB
C
2648 lines
66 KiB
C
/*
|
|
* Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of version 2 of the GNU General Public License as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it would be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
*
|
|
* Further, this software is distributed without any warranty that it is
|
|
* free of the rightful claim of any third person regarding infringement
|
|
* or the like. Any license provided herein, whether implied or
|
|
* otherwise, applies only to this software file. Patent licenses, if
|
|
* any, provided herein do not apply to combinations of this program with
|
|
* other software, or any other product whatsoever.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
|
|
* Mountain View, CA 94043, or:
|
|
*
|
|
* http://www.sgi.com
|
|
*
|
|
* For further information regarding this notice, see:
|
|
*
|
|
* http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
|
|
*/
|
|
|
|
/** @file
|
|
* @brief File system stress test.
|
|
*
|
|
* This version of SGI fsstress has been modified to be single-threaded and to
|
|
* work with the Reliance Edge POSIX-like API.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <time.h>
|
|
|
|
#include <redposix.h>
|
|
#include <redtests.h>
|
|
|
|
#if FSSTRESS_SUPPORTED
|
|
|
|
#include "redposixcompat.h"
|
|
|
|
#include <redosserv.h>
|
|
#include <redutils.h>
|
|
#include <redmacs.h>
|
|
#include <redvolume.h>
|
|
#include <redgetopt.h>
|
|
#include <redtoolcmn.h>
|
|
|
|
#if REDCONF_CHECKER == 1
|
|
#include <redcoreapi.h>
|
|
#endif
|
|
|
|
|
|
/* Create POSIX types. Use #define to avoid name conflicts in those
|
|
* environments where the type names already exist.
|
|
*/
|
|
#define off_t int64_t
|
|
#define off64_t off_t
|
|
#define ino_t uint32_t
|
|
#define mode_t uint16_t
|
|
#define __int64_t int64_t
|
|
|
|
|
|
/** @brief Generate a random number.
|
|
*
|
|
* @return A nonnegative random number.
|
|
*/
|
|
#define random() ( ( int ) ( RedRand32( NULL ) & 0x7FFFFFFF ) )
|
|
|
|
|
|
/** @brief Seed the random number generator.
|
|
*/
|
|
#define srandom( seed ) RedRandSeed( seed )
|
|
|
|
|
|
#define _exit( status ) exit( status )
|
|
#define getpagesize() 4096U
|
|
#define getpid() 1
|
|
|
|
|
|
/** @brief Determine the maximum file size.
|
|
*
|
|
* This is used for the MAXFSSIZE macro.
|
|
*/
|
|
static uint64_t MaxFileSize( void )
|
|
{
|
|
REDSTATFS info;
|
|
int32_t iStatus;
|
|
REDSTATUS errnoSave = errno;
|
|
uint64_t ullMaxFileSize;
|
|
|
|
iStatus = red_statvfs( "", &info );
|
|
|
|
if( iStatus == 0 )
|
|
{
|
|
ullMaxFileSize = info.f_maxfsize;
|
|
}
|
|
else
|
|
{
|
|
/* This function does not change errno.
|
|
*/
|
|
errno = errnoSave;
|
|
|
|
ullMaxFileSize = 0x7FFFFFFFU;
|
|
}
|
|
|
|
return ullMaxFileSize;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Simulated current working directory support
|
|
* -------------------------------------------------------------------*/
|
|
|
|
|
|
/* Forward declaration for red_chdir().
|
|
*/
|
|
static int red_stat( const char * pszPath,
|
|
REDSTAT * pStat );
|
|
|
|
/* The simulated CWD functions.
|
|
*/
|
|
#undef chdir
|
|
#undef getcwd
|
|
#define chdir( path ) red_chdir( path )
|
|
#define getcwd( buf, size ) red_getcwd( buf, size )
|
|
|
|
|
|
/* Redefine the path-based APIs to call MakeFullPath() on their arguments
|
|
* since there is no CWD support in the red_*() APIs.
|
|
*/
|
|
#undef open
|
|
#undef unlink
|
|
#undef mkdir
|
|
#undef rmdir
|
|
#undef rename
|
|
#undef link
|
|
#undef opendir
|
|
#define open( path, oflag ) red_open( MakeFullPath( path ), oflag )
|
|
#define unlink( path ) red_unlink( MakeFullPath( path ) )
|
|
#define mkdir( path ) red_mkdir( MakeFullPath( path ) )
|
|
#define rmdir( path ) red_rmdir( MakeFullPath( path ) )
|
|
#define rename( old, new ) red_rename( MakeFullPath( old ), MakeFullPath( new ) )
|
|
#define link( path, hardlink ) red_link( MakeFullPath( path ), MakeFullPath( hardlink ) )
|
|
#define opendir( path ) red_opendir( MakeFullPath( path ) )
|
|
|
|
#define FSSTRESS_BUF_SIZE 1024U
|
|
|
|
/* Stores the simulated current working directory.
|
|
*/
|
|
static char szLocalCwd[ FSSTRESS_BUF_SIZE ] = "/";
|
|
|
|
|
|
/** @brief Change the current working directory.
|
|
*
|
|
* This function only supports a subset of what is possible with POSIX chdir().
|
|
*
|
|
* @param pszPath The new current working directory.
|
|
*
|
|
* @return Upon successful completion, 0 shall be returned. Otherwise, -1
|
|
* shall be returned, and errno shall be set to indicate the error.
|
|
*/
|
|
static int red_chdir( const char * pszPath )
|
|
{
|
|
uint32_t ulIdx;
|
|
int iErrno = 0;
|
|
|
|
if( strcmp( pszPath, ".." ) == 0 )
|
|
{
|
|
uint32_t ulLastSlashIdx = 0U;
|
|
|
|
/* Chop off the last path separator and everything after it, so that
|
|
* "/foo/bar/baz" becomes "/foo/bar", moving the CWD up one directory.
|
|
*/
|
|
for( ulIdx = 0U; szLocalCwd[ ulIdx ] != '\0'; ulIdx++ )
|
|
{
|
|
if( szLocalCwd[ ulIdx ] == '/' )
|
|
{
|
|
ulLastSlashIdx = ulIdx;
|
|
}
|
|
}
|
|
|
|
if( ulLastSlashIdx != 0U )
|
|
{
|
|
szLocalCwd[ ulLastSlashIdx ] = '\0';
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char szOldCwd[ FSSTRESS_BUF_SIZE ];
|
|
|
|
/* chdir() must have no effect on the CWD if it fails, so save the CWD
|
|
* so we can revert it if necessary.
|
|
*/
|
|
strcpy( szOldCwd, szLocalCwd );
|
|
|
|
if( pszPath[ 0U ] == '/' )
|
|
{
|
|
if( strlen( pszPath ) >= sizeof( szLocalCwd ) )
|
|
{
|
|
iErrno = RED_ENAMETOOLONG;
|
|
}
|
|
else
|
|
{
|
|
strcpy( szLocalCwd, pszPath );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ulIdx = strlen( szLocalCwd );
|
|
|
|
if( ( ulIdx + 1U + strlen( pszPath ) ) >= sizeof( szLocalCwd ) )
|
|
{
|
|
iErrno = RED_ENAMETOOLONG;
|
|
}
|
|
else
|
|
{
|
|
if( szLocalCwd[ 1U ] != '\0' )
|
|
{
|
|
szLocalCwd[ ulIdx ] = '/';
|
|
ulIdx++;
|
|
}
|
|
|
|
strcpy( &szLocalCwd[ ulIdx ], pszPath );
|
|
}
|
|
}
|
|
|
|
if( iErrno == 0 )
|
|
{
|
|
REDSTAT s;
|
|
int iStatus;
|
|
|
|
iStatus = red_stat( szLocalCwd, &s );
|
|
|
|
if( iStatus != 0 )
|
|
{
|
|
iErrno = errno;
|
|
}
|
|
else if( !S_ISDIR( s.st_mode ) )
|
|
{
|
|
iErrno = RED_ENOTDIR;
|
|
}
|
|
else
|
|
{
|
|
/* No error, new CWD checks out.
|
|
*/
|
|
}
|
|
}
|
|
|
|
if( iErrno != 0 )
|
|
{
|
|
strcpy( szLocalCwd, szOldCwd );
|
|
}
|
|
}
|
|
|
|
if( iErrno != 0 )
|
|
{
|
|
errno = iErrno;
|
|
}
|
|
|
|
return iErrno == 0 ? 0 : -1;
|
|
}
|
|
|
|
|
|
/** @brief Retrieve the current working directory.
|
|
*
|
|
* @param pszBuf On successful return, populated with the current working
|
|
* directory. If NULL, memory will be allocated for the CWD
|
|
* and returned by this function.
|
|
* @param nSize The size of @p pszBuf.
|
|
*
|
|
* @return On success, if @p pszBuf was non-NULL, returns @p pszBuf; if
|
|
* @p pszBuf was NULL, returns an allocated buffer populated with the
|
|
* CWD which must be freed by the caller. On failure, returns NULL
|
|
* and errno will be set.
|
|
*/
|
|
static char * red_getcwd( char * pszBuf,
|
|
size_t nSize )
|
|
{
|
|
char * pszRet;
|
|
|
|
if( pszBuf == NULL )
|
|
{
|
|
pszRet = malloc( strlen( szLocalCwd ) + 1U );
|
|
|
|
if( pszRet == NULL )
|
|
{
|
|
errno = RED_ENOMEM;
|
|
}
|
|
else
|
|
{
|
|
strcpy( pszRet, szLocalCwd );
|
|
}
|
|
}
|
|
else if( nSize < strlen( szLocalCwd ) + 1U )
|
|
{
|
|
errno = RED_ERANGE;
|
|
pszRet = NULL;
|
|
}
|
|
else
|
|
{
|
|
strcpy( pszBuf, szLocalCwd );
|
|
pszRet = pszBuf;
|
|
}
|
|
|
|
return pszRet;
|
|
}
|
|
|
|
|
|
/** @brief Make a relative path into a fully qualified path.
|
|
*
|
|
* @param pszName The relative path.
|
|
*
|
|
* @return On success, a pointer to a fully qualified path. On error, NULL.
|
|
*/
|
|
static const char * MakeFullPath( const char * pszName )
|
|
{
|
|
#define MAXVOLNAME 64U /* Enough for most configs. */
|
|
static char aszFullPath[ 2U ][ MAXVOLNAME + 1U + FSSTRESS_BUF_SIZE ];
|
|
static uint32_t ulWhich = 0U;
|
|
|
|
char * pszFullPath = aszFullPath[ ulWhich ];
|
|
const char * pszVolume = gpRedVolConf->pszPathPrefix;
|
|
int32_t iLen;
|
|
|
|
if( pszName[ 0U ] == '/' )
|
|
{
|
|
iLen = RedSNPrintf( pszFullPath, sizeof( aszFullPath[ 0U ] ), "%s%s", pszVolume, pszName );
|
|
}
|
|
else if( strcmp( pszName, "." ) == 0U )
|
|
{
|
|
iLen = RedSNPrintf( pszFullPath, sizeof( aszFullPath[ 0U ] ), "%s%s", pszVolume, szLocalCwd );
|
|
}
|
|
else if( ( szLocalCwd[ 0U ] == '/' ) && ( szLocalCwd[ 1U ] == '\0' ) )
|
|
{
|
|
iLen = RedSNPrintf( pszFullPath, sizeof( aszFullPath[ 0U ] ), "%s/%s", pszVolume, pszName );
|
|
}
|
|
else
|
|
{
|
|
iLen = RedSNPrintf( pszFullPath, sizeof( aszFullPath[ 0U ] ), "%s%s/%s", pszVolume, szLocalCwd, pszName );
|
|
}
|
|
|
|
if( iLen == -1 )
|
|
{
|
|
/* Insufficient path buffer space.
|
|
*/
|
|
pszFullPath = NULL;
|
|
}
|
|
else
|
|
{
|
|
/* Toggle between two full path arrays; a kluge to make rename() and
|
|
* link() work correctly.
|
|
*/
|
|
ulWhich ^= 1U;
|
|
}
|
|
|
|
return pszFullPath;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------
|
|
* POSIX functions not implemented by the RED POSIX-like API
|
|
* -------------------------------------------------------------------*/
|
|
|
|
#define stat( p, s ) red_stat( p, s )
|
|
#define stat64( p, s ) stat( p, s )
|
|
#define lstat( p, s ) stat( p, s )
|
|
#define lstat64( p, s ) stat( p, s )
|
|
#define truncate( p, s ) red_truncate( p, s )
|
|
#define truncate64( p, s ) truncate( p, s )
|
|
|
|
|
|
/** @brief Get the status of a file or directory.
|
|
*/
|
|
static int red_stat( const char * pszPath,
|
|
REDSTAT * pStat )
|
|
{
|
|
int iFd;
|
|
int iRet;
|
|
|
|
iFd = open( pszPath, O_RDONLY );
|
|
iRet = iFd;
|
|
|
|
if( iFd != -1 )
|
|
{
|
|
iRet = fstat( iFd, pStat );
|
|
|
|
( void ) close( iFd );
|
|
}
|
|
|
|
return iRet;
|
|
}
|
|
|
|
|
|
/** @brief Truncate a file to a specified length.
|
|
*/
|
|
static int red_truncate( const char * pszPath,
|
|
off_t llSize )
|
|
{
|
|
int iFd;
|
|
int iRet;
|
|
|
|
iFd = open( pszPath, O_WRONLY );
|
|
iRet = iFd;
|
|
|
|
if( iFd != -1 )
|
|
{
|
|
iRet = ftruncate( iFd, llSize );
|
|
|
|
( void ) close( iFd );
|
|
}
|
|
|
|
return iRet;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------
|
|
* Begin ported fsstress code
|
|
* -------------------------------------------------------------------*/
|
|
|
|
/* Stuff from xfscompat.h */
|
|
|
|
#define MAXNAMELEN ( REDCONF_NAME_MAX + 1U ) /* Assumed to include NUL */
|
|
|
|
struct dioattr
|
|
{
|
|
int d_miniosz, d_maxiosz, d_mem;
|
|
};
|
|
|
|
#define MIN( a, b ) ( ( a ) < ( b ) ? ( a ) : ( b ) )
|
|
#define MAX( a, b ) ( ( a ) > ( b ) ? ( a ) : ( b ) )
|
|
|
|
/* End xfscompat.h */
|
|
|
|
|
|
typedef enum
|
|
{
|
|
OP_CREAT,
|
|
OP_FDATASYNC,
|
|
OP_FSYNC,
|
|
OP_GETDENTS,
|
|
OP_LINK,
|
|
OP_MKDIR,
|
|
OP_READ,
|
|
OP_RENAME,
|
|
OP_RMDIR,
|
|
OP_STAT,
|
|
OP_TRUNCATE,
|
|
OP_UNLINK,
|
|
OP_WRITE,
|
|
#if REDCONF_CHECKER == 1
|
|
OP_CHECK,
|
|
#endif
|
|
OP_LAST
|
|
} opty_t;
|
|
|
|
typedef void (* opfnc_t) ( int,
|
|
long );
|
|
|
|
typedef struct opdesc
|
|
{
|
|
opty_t op;
|
|
const char * name;
|
|
opfnc_t func;
|
|
int freq;
|
|
int iswrite;
|
|
} opdesc_t;
|
|
|
|
typedef struct fent
|
|
{
|
|
int id;
|
|
int parent;
|
|
} fent_t;
|
|
|
|
typedef struct flist
|
|
{
|
|
int nfiles;
|
|
int nslots;
|
|
int tag;
|
|
fent_t * fents;
|
|
} flist_t;
|
|
|
|
typedef struct pathname
|
|
{
|
|
int len;
|
|
char * path;
|
|
} pathname_t;
|
|
|
|
#define FT_DIR 0
|
|
#define FT_DIRm ( 1 << FT_DIR )
|
|
#define FT_REG 1
|
|
#define FT_REGm ( 1 << FT_REG )
|
|
#define FT_SYM 2
|
|
#define FT_SYMm ( 1 << FT_SYM )
|
|
#define FT_DEV 3
|
|
#define FT_DEVm ( 1 << FT_DEV )
|
|
#define FT_RTF 4
|
|
#define FT_RTFm ( 1 << FT_RTF )
|
|
#define FT_nft 5
|
|
#define FT_ANYm ( ( 1 << FT_nft ) - 1 )
|
|
#define FT_REGFILE ( FT_REGm | FT_RTFm )
|
|
#define FT_NOTDIR ( FT_ANYm & ~FT_DIRm )
|
|
|
|
#define FLIST_SLOT_INCR 16
|
|
#define NDCACHE 64
|
|
|
|
#define MAXFSIZE MaxFileSize()
|
|
|
|
static void creat_f( int opno,
|
|
long r );
|
|
static void fdatasync_f( int opno,
|
|
long r );
|
|
static void fsync_f( int opno,
|
|
long r );
|
|
static void getdents_f( int opno,
|
|
long r );
|
|
static void link_f( int opno,
|
|
long r );
|
|
static void mkdir_f( int opno,
|
|
long r );
|
|
static void read_f( int opno,
|
|
long r );
|
|
static void rename_f( int opno,
|
|
long r );
|
|
static void rmdir_f( int opno,
|
|
long r );
|
|
static void stat_f( int opno,
|
|
long r );
|
|
static void truncate_f( int opno,
|
|
long r );
|
|
static void unlink_f( int opno,
|
|
long r );
|
|
static void write_f( int opno,
|
|
long r );
|
|
#if REDCONF_CHECKER == 1
|
|
static void check_f( int opno,
|
|
long r );
|
|
#endif
|
|
|
|
static opdesc_t ops[] =
|
|
{
|
|
{ OP_CREAT, "creat", creat_f, 4, 1 },
|
|
{ OP_FDATASYNC, "fdatasync", fdatasync_f, 1, 1 },
|
|
{ OP_FSYNC, "fsync", fsync_f, 1, 1 },
|
|
{ OP_GETDENTS, "getdents", getdents_f, 1, 0 },
|
|
{ OP_LINK, "link", link_f, 1, 1 },
|
|
{ OP_MKDIR, "mkdir", mkdir_f, 2, 1 },
|
|
{ OP_READ, "read", read_f, 1, 0 },
|
|
{ OP_RENAME, "rename", rename_f, 2, 1 },
|
|
{ OP_RMDIR, "rmdir", rmdir_f, 1, 1 },
|
|
{ OP_STAT, "stat", stat_f, 1, 0 },
|
|
{ OP_TRUNCATE, "truncate", truncate_f, 2, 1 },
|
|
{ OP_UNLINK, "unlink", unlink_f, 1, 1 },
|
|
{ OP_WRITE, "write", write_f, 4, 1 },
|
|
#if REDCONF_CHECKER == 1
|
|
{ OP_CHECK, "check", check_f, 1, 1 },
|
|
#endif
|
|
}, * ops_end;
|
|
|
|
static flist_t flist[ FT_nft ] =
|
|
{
|
|
{ 0, 0, 'd', NULL },
|
|
{ 0, 0, 'f', NULL },
|
|
{ 0, 0, 'l', NULL },
|
|
{ 0, 0, 'c', NULL },
|
|
{ 0, 0, 'r', NULL },
|
|
};
|
|
|
|
static int dcache[ NDCACHE ];
|
|
static opty_t * freq_table;
|
|
static int freq_table_size;
|
|
static char * homedir;
|
|
static int * ilist;
|
|
static int ilistlen;
|
|
static off64_t maxfsize;
|
|
static int namerand;
|
|
static int nameseq;
|
|
static int nops;
|
|
static int operations = 1;
|
|
static int procid;
|
|
static int rtpct;
|
|
static unsigned long seed = 0;
|
|
static ino_t top_ino;
|
|
static int verbose = 0;
|
|
|
|
static int delete_tree( const char * path );
|
|
static void add_to_flist( int fd,
|
|
int it,
|
|
int parent );
|
|
static void append_pathname( pathname_t * name,
|
|
const char * str );
|
|
static void check_cwd( void );
|
|
static int creat_path( pathname_t * name,
|
|
mode_t mode );
|
|
static void dcache_enter( int dirid,
|
|
int slot );
|
|
static void dcache_init( void );
|
|
static fent_t * dcache_lookup( int dirid );
|
|
static void dcache_purge( int dirid );
|
|
static void del_from_flist( int ft,
|
|
int slot );
|
|
static void doproc( void );
|
|
static void fent_to_name( pathname_t * name,
|
|
flist_t * flp,
|
|
fent_t * fep );
|
|
static void fix_parent( int oldid,
|
|
int newid );
|
|
static void free_pathname( pathname_t * name );
|
|
static int generate_fname( fent_t * fep,
|
|
int ft,
|
|
pathname_t * name,
|
|
int * idp,
|
|
int * v );
|
|
static int get_fname( int which,
|
|
long r,
|
|
pathname_t * name,
|
|
flist_t ** flpp,
|
|
fent_t ** fepp,
|
|
int * v );
|
|
static void init_pathname( pathname_t * name );
|
|
static int link_path( pathname_t * name1,
|
|
pathname_t * name2 );
|
|
static int lstat64_path( pathname_t * name,
|
|
REDSTAT * sbuf );
|
|
static void make_freq_table( void );
|
|
static int mkdir_path( pathname_t * name,
|
|
mode_t mode );
|
|
static void namerandpad( int id,
|
|
char * buf,
|
|
int len );
|
|
static int open_path( pathname_t * name,
|
|
int oflag );
|
|
static DIR * opendir_path( pathname_t * name );
|
|
static int rename_path( pathname_t * name1,
|
|
pathname_t * name2 );
|
|
static int rmdir_path( pathname_t * name );
|
|
static void separate_pathname( pathname_t * name,
|
|
char * buf,
|
|
pathname_t * newname );
|
|
static int stat64_path( pathname_t * name,
|
|
REDSTAT * sbuf );
|
|
static int truncate64_path( pathname_t * name,
|
|
off64_t length );
|
|
static int unlink_path( pathname_t * name );
|
|
static void usage( const char * progname );
|
|
|
|
|
|
/** @brief Parse parameters for fsstress.
|
|
*
|
|
* @param argc The number of arguments from main().
|
|
* @param argv The vector of arguments from main().
|
|
* @param pParam Populated with the fsstress parameters.
|
|
* @param pbVolNum If non-NULL, populated with the volume number.
|
|
* @param ppszDevice If non-NULL, populated with the device name argument or
|
|
* NULL if no device argument is provided.
|
|
*
|
|
* @return The result of parsing the parameters.
|
|
*/
|
|
PARAMSTATUS FsstressParseParams( int argc,
|
|
char * argv[],
|
|
FSSTRESSPARAM * pParam,
|
|
uint8_t * pbVolNum,
|
|
const char ** ppszDevice )
|
|
{
|
|
int c;
|
|
uint8_t bVolNum;
|
|
const REDOPTION aLongopts[] =
|
|
{
|
|
{ "no-cleanup", red_no_argument, NULL, 'c' },
|
|
{ "loops", red_required_argument, NULL, 'l' },
|
|
{ "nops", red_required_argument, NULL, 'n' },
|
|
{ "namepad", red_no_argument, NULL, 'r' },
|
|
{ "seed", red_required_argument, NULL, 's' },
|
|
{ "verbose", red_no_argument, NULL, 'v' },
|
|
{ "dev", red_required_argument, NULL, 'D' },
|
|
{ "help", red_no_argument, NULL, 'H' },
|
|
{ NULL }
|
|
};
|
|
|
|
/* If run without parameters, treat as a help request.
|
|
*/
|
|
if( argc <= 1 )
|
|
{
|
|
goto Help;
|
|
}
|
|
|
|
/* Assume no device argument to start with.
|
|
*/
|
|
if( ppszDevice != NULL )
|
|
{
|
|
*ppszDevice = NULL;
|
|
}
|
|
|
|
/* Set default parameters.
|
|
*/
|
|
FsstressDefaultParams( pParam );
|
|
|
|
while( ( c = RedGetoptLong( argc, argv, "cl:n:rs:vD:H", aLongopts, NULL ) ) != -1 )
|
|
{
|
|
switch( c )
|
|
{
|
|
case 'c': /* --no-cleanup */
|
|
pParam->fNoCleanup = true;
|
|
break;
|
|
|
|
case 'l': /* --loops */
|
|
pParam->ulLoops = RedAtoI( red_optarg );
|
|
break;
|
|
|
|
case 'n': /* --nops */
|
|
pParam->ulNops = RedAtoI( red_optarg );
|
|
break;
|
|
|
|
case 'r': /* --namepad */
|
|
pParam->fNamePad = true;
|
|
break;
|
|
|
|
case 's': /* --seed */
|
|
pParam->ulSeed = RedAtoI( red_optarg );
|
|
break;
|
|
|
|
case 'v': /* --verbose */
|
|
pParam->fVerbose = true;
|
|
break;
|
|
|
|
case 'D': /* --dev */
|
|
|
|
if( ppszDevice != NULL )
|
|
{
|
|
*ppszDevice = red_optarg;
|
|
}
|
|
|
|
break;
|
|
|
|
case 'H': /* --help */
|
|
goto Help;
|
|
|
|
case '?': /* Unknown or ambiguous option */
|
|
case ':': /* Option missing required argument */
|
|
default:
|
|
goto BadOpt;
|
|
}
|
|
}
|
|
|
|
/* RedGetoptLong() has permuted argv to move all non-option arguments to
|
|
* the end. We expect to find a volume identifier.
|
|
*/
|
|
if( red_optind >= argc )
|
|
{
|
|
RedPrintf( "Missing volume argument\n" );
|
|
goto BadOpt;
|
|
}
|
|
|
|
bVolNum = RedFindVolumeNumber( argv[ red_optind ] );
|
|
|
|
if( bVolNum == REDCONF_VOLUME_COUNT )
|
|
{
|
|
RedPrintf( "Error: \"%s\" is not a valid volume identifier.\n", argv[ red_optind ] );
|
|
goto BadOpt;
|
|
}
|
|
|
|
if( pbVolNum != NULL )
|
|
{
|
|
*pbVolNum = bVolNum;
|
|
}
|
|
|
|
red_optind++; /* Move past volume parameter. */
|
|
|
|
if( red_optind < argc )
|
|
{
|
|
int32_t ii;
|
|
|
|
for( ii = red_optind; ii < argc; ii++ )
|
|
{
|
|
RedPrintf( "Error: Unexpected command-line argument \"%s\".\n", argv[ ii ] );
|
|
}
|
|
|
|
goto BadOpt;
|
|
}
|
|
|
|
return PARAMSTATUS_OK;
|
|
|
|
BadOpt:
|
|
|
|
RedPrintf( "%s - invalid parameters\n", argv[ 0U ] );
|
|
usage( argv[ 0U ] );
|
|
return PARAMSTATUS_BAD;
|
|
|
|
Help:
|
|
|
|
usage( argv[ 0U ] );
|
|
return PARAMSTATUS_HELP;
|
|
}
|
|
|
|
|
|
/** @brief Set default fsstress parameters.
|
|
*
|
|
* @param pParam Populated with the default fsstress parameters.
|
|
*/
|
|
void FsstressDefaultParams( FSSTRESSPARAM * pParam )
|
|
{
|
|
RedMemSet( pParam, 0U, sizeof( *pParam ) );
|
|
pParam->ulLoops = 1U;
|
|
pParam->ulNops = 10000U;
|
|
}
|
|
|
|
|
|
/** @brief Start fsstress.
|
|
*
|
|
* @param pParam fsstress parameters, either from FsstressParseParams() or
|
|
* constructed programatically.
|
|
*
|
|
* @return Zero on success, otherwise nonzero.
|
|
*/
|
|
int FsstressStart( const FSSTRESSPARAM * pParam )
|
|
{
|
|
char buf[ 10 ];
|
|
int fd;
|
|
int i;
|
|
int cleanup;
|
|
int loops;
|
|
int loopcntr = 1;
|
|
|
|
nops = sizeof( ops ) / sizeof( ops[ 0 ] );
|
|
ops_end = &ops[ nops ];
|
|
|
|
/* Copy the already-parsed parameters into the traditional variables.
|
|
*/
|
|
cleanup = pParam->fNoCleanup ? 1 : 0;
|
|
loops = pParam->ulLoops;
|
|
operations = pParam->ulNops;
|
|
namerand = pParam->fNamePad ? 1 : 0;
|
|
seed = pParam->ulSeed;
|
|
verbose = pParam->fVerbose ? 1 : 0;
|
|
|
|
make_freq_table();
|
|
|
|
while( ( loopcntr <= loops ) || ( loops == 0 ) )
|
|
{
|
|
RedSNPrintf( buf, sizeof( buf ), "fss%x", getpid() );
|
|
fd = creat( buf, 0666 );
|
|
maxfsize = ( off64_t ) MAXFSIZE;
|
|
dcache_init();
|
|
|
|
if( !seed )
|
|
{
|
|
seed = ( unsigned long ) RedOsClockGetTime();
|
|
RedPrintf( "seed = %ld\n", seed );
|
|
}
|
|
|
|
close( fd );
|
|
unlink( buf );
|
|
procid = 0;
|
|
doproc();
|
|
|
|
if( cleanup == 0 )
|
|
{
|
|
delete_tree( "/" );
|
|
|
|
for( i = 0; i < FT_nft; i++ )
|
|
{
|
|
flist[ i ].nslots = 0;
|
|
flist[ i ].nfiles = 0;
|
|
free( flist[ i ].fents );
|
|
flist[ i ].fents = NULL;
|
|
}
|
|
}
|
|
|
|
loopcntr++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int delete_tree( const char * path )
|
|
{
|
|
REDSTAT sb;
|
|
DIR * dp;
|
|
REDDIRENT * dep;
|
|
char * childpath;
|
|
size_t len;
|
|
int e;
|
|
|
|
e = stat( path, &sb );
|
|
|
|
if( e )
|
|
{
|
|
return errno;
|
|
}
|
|
|
|
if( !S_ISDIR( sb.st_mode ) )
|
|
{
|
|
return unlink( path ) ? errno : 0;
|
|
}
|
|
|
|
dp = opendir( path );
|
|
|
|
if( dp == NULL )
|
|
{
|
|
return errno;
|
|
}
|
|
|
|
while( ( dep = readdir( dp ) ) != NULL )
|
|
{
|
|
len = strlen( path ) + 1 + strlen( dep->d_name ) + 1;
|
|
childpath = malloc( len );
|
|
|
|
strcpy( childpath, path );
|
|
|
|
if( childpath[ strlen( childpath ) - 1 ] != '/' )
|
|
{
|
|
strcat( childpath, "/" );
|
|
}
|
|
|
|
strcat( childpath, dep->d_name );
|
|
|
|
e = delete_tree( childpath );
|
|
|
|
free( childpath );
|
|
|
|
if( e )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( ( e == 0 ) && ( strcmp( path, "/" ) != 0 ) )
|
|
{
|
|
e = rmdir( path ) ? errno : 0;
|
|
}
|
|
|
|
closedir( dp );
|
|
return e;
|
|
}
|
|
|
|
static void add_to_flist( int ft,
|
|
int id,
|
|
int parent )
|
|
{
|
|
fent_t * fep;
|
|
flist_t * ftp;
|
|
|
|
ftp = &flist[ ft ];
|
|
|
|
if( ftp->nfiles == ftp->nslots )
|
|
{
|
|
ftp->nslots += FLIST_SLOT_INCR;
|
|
ftp->fents = realloc( ftp->fents, ftp->nslots * sizeof( fent_t ) );
|
|
}
|
|
|
|
fep = &ftp->fents[ ftp->nfiles++ ];
|
|
fep->id = id;
|
|
fep->parent = parent;
|
|
}
|
|
|
|
static void append_pathname( pathname_t * name,
|
|
const char * str )
|
|
{
|
|
int len;
|
|
|
|
len = strlen( str );
|
|
#ifdef DEBUG
|
|
if( len && ( *str == '/' ) && ( name->len == 0 ) )
|
|
{
|
|
RedPrintf( "fsstress: append_pathname failure\n" );
|
|
chdir( homedir );
|
|
abort();
|
|
}
|
|
#endif
|
|
name->path = realloc( name->path, name->len + 1 + len );
|
|
strcpy( &name->path[ name->len ], str );
|
|
name->len += len;
|
|
}
|
|
|
|
static void check_cwd( void )
|
|
{
|
|
#ifdef DEBUG
|
|
REDSTAT statbuf;
|
|
|
|
if( ( stat64( ".", &statbuf ) == 0 ) && ( statbuf.st_ino == top_ino ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
chdir( homedir );
|
|
RedPrintf( "fsstress: check_cwd failure\n" );
|
|
abort();
|
|
#endif /* ifdef DEBUG */
|
|
}
|
|
|
|
static int creat_path( pathname_t * name,
|
|
mode_t mode )
|
|
{
|
|
char buf[ MAXNAMELEN ];
|
|
pathname_t newname;
|
|
int rval;
|
|
|
|
rval = creat( name->path, mode );
|
|
|
|
if( ( rval >= 0 ) || ( errno != RED_ENAMETOOLONG ) )
|
|
{
|
|
return rval;
|
|
}
|
|
|
|
separate_pathname( name, buf, &newname );
|
|
|
|
if( chdir( buf ) == 0 )
|
|
{
|
|
rval = creat_path( &newname, mode );
|
|
chdir( ".." );
|
|
}
|
|
|
|
free_pathname( &newname );
|
|
return rval;
|
|
}
|
|
|
|
static void dcache_enter( int dirid,
|
|
int slot )
|
|
{
|
|
dcache[ dirid % NDCACHE ] = slot;
|
|
}
|
|
|
|
static void dcache_init( void )
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; i < NDCACHE; i++ )
|
|
{
|
|
dcache[ i ] = -1;
|
|
}
|
|
}
|
|
|
|
static fent_t * dcache_lookup( int dirid )
|
|
{
|
|
fent_t * fep;
|
|
int i;
|
|
|
|
i = dcache[ dirid % NDCACHE ];
|
|
|
|
if( ( i >= 0 ) && ( ( fep = &flist[ FT_DIR ].fents[ i ] )->id == dirid ) )
|
|
{
|
|
return fep;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void dcache_purge( int dirid )
|
|
{
|
|
int * dcp;
|
|
|
|
dcp = &dcache[ dirid % NDCACHE ];
|
|
|
|
if( ( *dcp >= 0 ) && ( flist[ FT_DIR ].fents[ *dcp ].id == dirid ) )
|
|
{
|
|
*dcp = -1;
|
|
}
|
|
}
|
|
|
|
static void del_from_flist( int ft,
|
|
int slot )
|
|
{
|
|
flist_t * ftp;
|
|
|
|
ftp = &flist[ ft ];
|
|
|
|
if( ft == FT_DIR )
|
|
{
|
|
dcache_purge( ftp->fents[ slot ].id );
|
|
}
|
|
|
|
if( slot != ftp->nfiles - 1 )
|
|
{
|
|
if( ft == FT_DIR )
|
|
{
|
|
dcache_purge( ftp->fents[ ftp->nfiles - 1 ].id );
|
|
}
|
|
|
|
ftp->fents[ slot ] = ftp->fents[ --ftp->nfiles ];
|
|
}
|
|
else
|
|
{
|
|
ftp->nfiles--;
|
|
}
|
|
}
|
|
|
|
static fent_t * dirid_to_fent( int dirid )
|
|
{
|
|
fent_t * efep;
|
|
fent_t * fep;
|
|
flist_t * flp;
|
|
|
|
if( ( fep = dcache_lookup( dirid ) ) )
|
|
{
|
|
return fep;
|
|
}
|
|
|
|
flp = &flist[ FT_DIR ];
|
|
|
|
for( fep = flp->fents, efep = &fep[ flp->nfiles ]; fep < efep; fep++ )
|
|
{
|
|
if( fep->id == dirid )
|
|
{
|
|
dcache_enter( dirid, ( int ) ( fep - flp->fents ) );
|
|
return fep;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void doproc( void )
|
|
{
|
|
REDSTAT statbuf;
|
|
char buf[ 10 ];
|
|
int opno;
|
|
opdesc_t * p;
|
|
|
|
RedSNPrintf( buf, sizeof( buf ), "p%x", procid );
|
|
( void ) mkdir( buf );
|
|
|
|
if( ( chdir( buf ) < 0 ) || ( stat64( ".", &statbuf ) < 0 ) )
|
|
{
|
|
perror( buf );
|
|
_exit( 1 );
|
|
}
|
|
|
|
top_ino = statbuf.st_ino;
|
|
homedir = getcwd( NULL, 0 );
|
|
seed += procid;
|
|
srandom( seed );
|
|
|
|
if( namerand )
|
|
{
|
|
namerand = random();
|
|
}
|
|
|
|
for( opno = 0; opno < operations; opno++ )
|
|
{
|
|
p = &ops[ freq_table[ random() % freq_table_size ] ];
|
|
|
|
if( ( unsigned long ) p->func < 4096 )
|
|
{
|
|
abort();
|
|
}
|
|
|
|
p->func( opno, random() );
|
|
}
|
|
|
|
free( homedir );
|
|
}
|
|
|
|
static void fent_to_name( pathname_t * name,
|
|
flist_t * flp,
|
|
fent_t * fep )
|
|
{
|
|
char buf[ MAXNAMELEN ];
|
|
int i;
|
|
fent_t * pfep;
|
|
|
|
if( fep == NULL )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( fep->parent != -1 )
|
|
{
|
|
pfep = dirid_to_fent( fep->parent );
|
|
fent_to_name( name, &flist[ FT_DIR ], pfep );
|
|
append_pathname( name, "/" );
|
|
}
|
|
|
|
i = RedSNPrintf( buf, sizeof( buf ), "%c%x", flp->tag, fep->id );
|
|
namerandpad( fep->id, buf, i );
|
|
append_pathname( name, buf );
|
|
}
|
|
|
|
static void fix_parent( int oldid,
|
|
int newid )
|
|
{
|
|
fent_t * fep;
|
|
flist_t * flp;
|
|
int i;
|
|
int j;
|
|
|
|
for( i = 0, flp = flist; i < FT_nft; i++, flp++ )
|
|
{
|
|
for( j = 0, fep = flp->fents; j < flp->nfiles; j++, fep++ )
|
|
{
|
|
if( fep->parent == oldid )
|
|
{
|
|
fep->parent = newid;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void free_pathname( pathname_t * name )
|
|
{
|
|
if( name->path )
|
|
{
|
|
free( name->path );
|
|
name->path = NULL;
|
|
name->len = 0;
|
|
}
|
|
}
|
|
|
|
static int generate_fname( fent_t * fep,
|
|
int ft,
|
|
pathname_t * name,
|
|
int * idp,
|
|
int * v )
|
|
{
|
|
char buf[ MAXNAMELEN ];
|
|
flist_t * flp;
|
|
int id;
|
|
int j;
|
|
int len;
|
|
|
|
flp = &flist[ ft ];
|
|
len = RedSNPrintf( buf, sizeof( buf ), "%c%x", flp->tag, id = nameseq++ );
|
|
namerandpad( id, buf, len );
|
|
|
|
if( fep )
|
|
{
|
|
fent_to_name( name, &flist[ FT_DIR ], fep );
|
|
append_pathname( name, "/" );
|
|
}
|
|
|
|
append_pathname( name, buf );
|
|
*idp = id;
|
|
*v = verbose;
|
|
|
|
for( j = 0; !*v && j < ilistlen; j++ )
|
|
{
|
|
if( ilist[ j ] == id )
|
|
{
|
|
*v = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int get_fname( int which,
|
|
long r,
|
|
pathname_t * name,
|
|
flist_t ** flpp,
|
|
fent_t ** fepp,
|
|
int * v )
|
|
{
|
|
int c;
|
|
fent_t * fep;
|
|
flist_t * flp;
|
|
int i;
|
|
int j;
|
|
int x;
|
|
|
|
for( i = 0, c = 0, flp = flist; i < FT_nft; i++, flp++ )
|
|
{
|
|
if( which & ( 1 << i ) )
|
|
{
|
|
c += flp->nfiles;
|
|
}
|
|
}
|
|
|
|
if( c == 0 )
|
|
{
|
|
if( flpp )
|
|
{
|
|
*flpp = NULL;
|
|
}
|
|
|
|
if( fepp )
|
|
{
|
|
*fepp = NULL;
|
|
}
|
|
|
|
*v = verbose;
|
|
return 0;
|
|
}
|
|
|
|
x = ( int ) ( r % c );
|
|
|
|
for( i = 0, c = 0, flp = flist; i < FT_nft; i++, flp++ )
|
|
{
|
|
if( which & ( 1 << i ) )
|
|
{
|
|
if( x < c + flp->nfiles )
|
|
{
|
|
fep = &flp->fents[ x - c ];
|
|
|
|
if( name )
|
|
{
|
|
fent_to_name( name, flp, fep );
|
|
}
|
|
|
|
if( flpp )
|
|
{
|
|
*flpp = flp;
|
|
}
|
|
|
|
if( fepp )
|
|
{
|
|
*fepp = fep;
|
|
}
|
|
|
|
*v = verbose;
|
|
|
|
for( j = 0; !*v && j < ilistlen; j++ )
|
|
{
|
|
if( ilist[ j ] == fep->id )
|
|
{
|
|
*v = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
c += flp->nfiles;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
RedPrintf( "fsstress: get_fname failure\n" );
|
|
abort();
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
static void init_pathname( pathname_t * name )
|
|
{
|
|
name->len = 0;
|
|
name->path = NULL;
|
|
}
|
|
|
|
static int link_path( pathname_t * name1,
|
|
pathname_t * name2 )
|
|
{
|
|
char buf1[ MAXNAMELEN ];
|
|
char buf2[ MAXNAMELEN ];
|
|
int down1;
|
|
pathname_t newname1;
|
|
pathname_t newname2;
|
|
int rval;
|
|
|
|
rval = link( name1->path, name2->path );
|
|
|
|
if( ( rval >= 0 ) || ( errno != RED_ENAMETOOLONG ) )
|
|
{
|
|
return rval;
|
|
}
|
|
|
|
separate_pathname( name1, buf1, &newname1 );
|
|
separate_pathname( name2, buf2, &newname2 );
|
|
|
|
if( strcmp( buf1, buf2 ) == 0 )
|
|
{
|
|
if( chdir( buf1 ) == 0 )
|
|
{
|
|
rval = link_path( &newname1, &newname2 );
|
|
chdir( ".." );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( strcmp( buf1, ".." ) == 0 )
|
|
{
|
|
down1 = 0;
|
|
}
|
|
else if( strcmp( buf2, ".." ) == 0 )
|
|
{
|
|
down1 = 1;
|
|
}
|
|
else if( strlen( buf1 ) == 0 )
|
|
{
|
|
down1 = 0;
|
|
}
|
|
else if( strlen( buf2 ) == 0 )
|
|
{
|
|
down1 = 1;
|
|
}
|
|
else
|
|
{
|
|
down1 = MAX( newname1.len, 3 + name2->len ) <=
|
|
MAX( 3 + name1->len, newname2.len );
|
|
}
|
|
|
|
if( down1 )
|
|
{
|
|
free_pathname( &newname2 );
|
|
append_pathname( &newname2, "../" );
|
|
append_pathname( &newname2, name2->path );
|
|
|
|
if( chdir( buf1 ) == 0 )
|
|
{
|
|
rval = link_path( &newname1, &newname2 );
|
|
chdir( ".." );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
free_pathname( &newname1 );
|
|
append_pathname( &newname1, "../" );
|
|
append_pathname( &newname1, name1->path );
|
|
|
|
if( chdir( buf2 ) == 0 )
|
|
{
|
|
rval = link_path( &newname1, &newname2 );
|
|
chdir( ".." );
|
|
}
|
|
}
|
|
}
|
|
|
|
free_pathname( &newname1 );
|
|
free_pathname( &newname2 );
|
|
return rval;
|
|
}
|
|
|
|
static int lstat64_path( pathname_t * name,
|
|
REDSTAT * sbuf )
|
|
{
|
|
char buf[ MAXNAMELEN ];
|
|
pathname_t newname;
|
|
int rval;
|
|
|
|
rval = lstat64( name->path, sbuf );
|
|
|
|
if( ( rval >= 0 ) || ( errno != RED_ENAMETOOLONG ) )
|
|
{
|
|
return rval;
|
|
}
|
|
|
|
separate_pathname( name, buf, &newname );
|
|
|
|
if( chdir( buf ) == 0 )
|
|
{
|
|
rval = lstat64_path( &newname, sbuf );
|
|
chdir( ".." );
|
|
}
|
|
|
|
free_pathname( &newname );
|
|
return rval;
|
|
}
|
|
|
|
static void make_freq_table( void )
|
|
{
|
|
int f;
|
|
int i;
|
|
opdesc_t * p;
|
|
|
|
for( p = ops, f = 0; p < ops_end; p++ )
|
|
{
|
|
f += p->freq;
|
|
}
|
|
|
|
freq_table = malloc( f * sizeof( *freq_table ) );
|
|
freq_table_size = f;
|
|
|
|
for( p = ops, i = 0; p < ops_end; p++ )
|
|
{
|
|
for( f = 0; f < p->freq; f++, i++ )
|
|
{
|
|
freq_table[ i ] = p->op;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int mkdir_path( pathname_t * name,
|
|
mode_t mode )
|
|
{
|
|
char buf[ MAXNAMELEN ];
|
|
pathname_t newname;
|
|
int rval;
|
|
|
|
rval = mkdir( name->path );
|
|
|
|
if( ( rval >= 0 ) || ( errno != RED_ENAMETOOLONG ) )
|
|
{
|
|
return rval;
|
|
}
|
|
|
|
separate_pathname( name, buf, &newname );
|
|
|
|
if( chdir( buf ) == 0 )
|
|
{
|
|
rval = mkdir_path( &newname, mode );
|
|
chdir( ".." );
|
|
}
|
|
|
|
free_pathname( &newname );
|
|
return rval;
|
|
}
|
|
|
|
static void namerandpad( int id,
|
|
char * buf,
|
|
int len )
|
|
{
|
|
int bucket;
|
|
static int buckets[ 8 ] = { 0 };
|
|
static int bucket_count = 0;
|
|
int bucket_value;
|
|
int i;
|
|
int padlen;
|
|
int padmod;
|
|
|
|
if( namerand == 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* buckets[] used to be a statically initialized array with the following
|
|
* initializer: { 2, 4, 8, 16, 32, 64, 128, MAXNAMELEN - 1 }
|
|
*
|
|
* The problem is that with Reliance Edge, the maximum name length might be
|
|
* less than 128. So the below code populates buckets[] in a similar
|
|
* fashion but avoids name lengths longer than the maximum. For example,
|
|
* if the max name is 20, the resulting array is { 2, 4, 8, 16, 20 }.
|
|
*/
|
|
if( !bucket_count )
|
|
{
|
|
bucket_count = sizeof( buckets ) / sizeof( buckets[ 0 ] );
|
|
bucket_value = 2;
|
|
|
|
for( i = 0; i < bucket_count; i++ )
|
|
{
|
|
if( ( bucket_value > 128 ) || ( bucket_value >= ( int ) MAXNAMELEN - 1 ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
buckets[ i ] = bucket_value;
|
|
bucket_value *= 2;
|
|
}
|
|
|
|
if( i < bucket_count )
|
|
{
|
|
buckets[ i ] = MAXNAMELEN - 1;
|
|
i++;
|
|
}
|
|
|
|
bucket_count = i;
|
|
}
|
|
|
|
bucket = ( id ^ namerand ) % bucket_count;
|
|
padmod = buckets[ bucket ] + 1 - len;
|
|
|
|
if( padmod <= 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
padlen = ( id ^ namerand ) % padmod;
|
|
|
|
if( padlen )
|
|
{
|
|
memset( &buf[ len ], 'X', padlen );
|
|
buf[ len + padlen ] = '\0';
|
|
}
|
|
}
|
|
|
|
static int open_path( pathname_t * name,
|
|
int oflag )
|
|
{
|
|
char buf[ MAXNAMELEN ];
|
|
pathname_t newname;
|
|
int rval;
|
|
|
|
rval = open( name->path, oflag );
|
|
|
|
if( ( rval >= 0 ) || ( errno != RED_ENAMETOOLONG ) )
|
|
{
|
|
return rval;
|
|
}
|
|
|
|
separate_pathname( name, buf, &newname );
|
|
|
|
if( chdir( buf ) == 0 )
|
|
{
|
|
rval = open_path( &newname, oflag );
|
|
chdir( ".." );
|
|
}
|
|
|
|
free_pathname( &newname );
|
|
return rval;
|
|
}
|
|
|
|
static DIR * opendir_path( pathname_t * name )
|
|
{
|
|
char buf[ MAXNAMELEN ];
|
|
pathname_t newname;
|
|
DIR * rval;
|
|
|
|
rval = opendir( name->path );
|
|
|
|
if( rval || ( errno != RED_ENAMETOOLONG ) )
|
|
{
|
|
return rval;
|
|
}
|
|
|
|
separate_pathname( name, buf, &newname );
|
|
|
|
if( chdir( buf ) == 0 )
|
|
{
|
|
rval = opendir_path( &newname );
|
|
chdir( ".." );
|
|
}
|
|
|
|
free_pathname( &newname );
|
|
return rval;
|
|
}
|
|
|
|
static int rename_path( pathname_t * name1,
|
|
pathname_t * name2 )
|
|
{
|
|
char buf1[ MAXNAMELEN ];
|
|
char buf2[ MAXNAMELEN ];
|
|
int down1;
|
|
pathname_t newname1;
|
|
pathname_t newname2;
|
|
int rval;
|
|
|
|
rval = rename( name1->path, name2->path );
|
|
|
|
if( ( rval >= 0 ) || ( errno != RED_ENAMETOOLONG ) )
|
|
{
|
|
return rval;
|
|
}
|
|
|
|
separate_pathname( name1, buf1, &newname1 );
|
|
separate_pathname( name2, buf2, &newname2 );
|
|
|
|
if( strcmp( buf1, buf2 ) == 0 )
|
|
{
|
|
if( chdir( buf1 ) == 0 )
|
|
{
|
|
rval = rename_path( &newname1, &newname2 );
|
|
chdir( ".." );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( strcmp( buf1, ".." ) == 0 )
|
|
{
|
|
down1 = 0;
|
|
}
|
|
else if( strcmp( buf2, ".." ) == 0 )
|
|
{
|
|
down1 = 1;
|
|
}
|
|
else if( strlen( buf1 ) == 0 )
|
|
{
|
|
down1 = 0;
|
|
}
|
|
else if( strlen( buf2 ) == 0 )
|
|
{
|
|
down1 = 1;
|
|
}
|
|
else
|
|
{
|
|
down1 = MAX( newname1.len, 3 + name2->len ) <=
|
|
MAX( 3 + name1->len, newname2.len );
|
|
}
|
|
|
|
if( down1 )
|
|
{
|
|
free_pathname( &newname2 );
|
|
append_pathname( &newname2, "../" );
|
|
append_pathname( &newname2, name2->path );
|
|
|
|
if( chdir( buf1 ) == 0 )
|
|
{
|
|
rval = rename_path( &newname1, &newname2 );
|
|
chdir( ".." );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
free_pathname( &newname1 );
|
|
append_pathname( &newname1, "../" );
|
|
append_pathname( &newname1, name1->path );
|
|
|
|
if( chdir( buf2 ) == 0 )
|
|
{
|
|
rval = rename_path( &newname1, &newname2 );
|
|
chdir( ".." );
|
|
}
|
|
}
|
|
}
|
|
|
|
free_pathname( &newname1 );
|
|
free_pathname( &newname2 );
|
|
return rval;
|
|
}
|
|
|
|
static int rmdir_path( pathname_t * name )
|
|
{
|
|
char buf[ MAXNAMELEN ];
|
|
pathname_t newname;
|
|
int rval;
|
|
|
|
rval = rmdir( name->path );
|
|
|
|
if( ( rval >= 0 ) || ( errno != RED_ENAMETOOLONG ) )
|
|
{
|
|
return rval;
|
|
}
|
|
|
|
separate_pathname( name, buf, &newname );
|
|
|
|
if( chdir( buf ) == 0 )
|
|
{
|
|
rval = rmdir_path( &newname );
|
|
chdir( ".." );
|
|
}
|
|
|
|
free_pathname( &newname );
|
|
return rval;
|
|
}
|
|
|
|
static void separate_pathname( pathname_t * name,
|
|
char * buf,
|
|
pathname_t * newname )
|
|
{
|
|
char * slash;
|
|
|
|
init_pathname( newname );
|
|
slash = strchr( name->path, '/' );
|
|
|
|
if( slash == NULL )
|
|
{
|
|
buf[ 0 ] = '\0';
|
|
return;
|
|
}
|
|
|
|
*slash = '\0';
|
|
strcpy( buf, name->path );
|
|
*slash = '/';
|
|
append_pathname( newname, slash + 1 );
|
|
}
|
|
|
|
static int stat64_path( pathname_t * name,
|
|
REDSTAT * sbuf )
|
|
{
|
|
char buf[ MAXNAMELEN ];
|
|
pathname_t newname;
|
|
int rval;
|
|
|
|
rval = stat64( name->path, sbuf );
|
|
|
|
if( ( rval >= 0 ) || ( errno != RED_ENAMETOOLONG ) )
|
|
{
|
|
return rval;
|
|
}
|
|
|
|
separate_pathname( name, buf, &newname );
|
|
|
|
if( chdir( buf ) == 0 )
|
|
{
|
|
rval = stat64_path( &newname, sbuf );
|
|
chdir( ".." );
|
|
}
|
|
|
|
free_pathname( &newname );
|
|
return rval;
|
|
}
|
|
|
|
static int truncate64_path( pathname_t * name,
|
|
off64_t length )
|
|
{
|
|
char buf[ MAXNAMELEN ];
|
|
pathname_t newname;
|
|
int rval;
|
|
|
|
rval = truncate64( name->path, length );
|
|
|
|
if( ( rval >= 0 ) || ( errno != RED_ENAMETOOLONG ) )
|
|
{
|
|
return rval;
|
|
}
|
|
|
|
separate_pathname( name, buf, &newname );
|
|
|
|
if( chdir( buf ) == 0 )
|
|
{
|
|
rval = truncate64_path( &newname, length );
|
|
chdir( ".." );
|
|
}
|
|
|
|
free_pathname( &newname );
|
|
return rval;
|
|
}
|
|
|
|
static int unlink_path( pathname_t * name )
|
|
{
|
|
char buf[ MAXNAMELEN ];
|
|
pathname_t newname;
|
|
int rval;
|
|
|
|
rval = unlink( name->path );
|
|
|
|
if( ( rval >= 0 ) || ( errno != RED_ENAMETOOLONG ) )
|
|
{
|
|
return rval;
|
|
}
|
|
|
|
separate_pathname( name, buf, &newname );
|
|
|
|
if( chdir( buf ) == 0 )
|
|
{
|
|
rval = unlink_path( &newname );
|
|
chdir( ".." );
|
|
}
|
|
|
|
free_pathname( &newname );
|
|
return rval;
|
|
}
|
|
|
|
static void usage( const char * progname )
|
|
{
|
|
RedPrintf( "usage: %s VolumeID [Options]\n", progname );
|
|
RedPrintf( "File system stress test.\n\n" );
|
|
RedPrintf( "Where:\n" );
|
|
RedPrintf( " VolumeID\n" );
|
|
RedPrintf( " A volume number (e.g., 2) or a volume path prefix (e.g., VOL1: or /data)\n" );
|
|
RedPrintf( " of the volume to test.\n" );
|
|
RedPrintf( "And 'Options' are any of the following:\n" );
|
|
RedPrintf( " --no-cleanup, -c\n" );
|
|
RedPrintf( " Specifies not to remove files (cleanup) after execution\n" );
|
|
RedPrintf( " --loops=count, -l count\n" );
|
|
RedPrintf( " Specifies the number of times the entire test should loop. Use 0 for\n" );
|
|
RedPrintf( " infinite. Default 1.\n" );
|
|
RedPrintf( " --nops=count, -n count\n" );
|
|
RedPrintf( " Specifies the number of operations to run (default 10000).\n" );
|
|
RedPrintf( " --namepad, -r\n" );
|
|
RedPrintf( " Specifies to use random name padding (resulting in longer names).\n" );
|
|
RedPrintf( " --seed=value, -s value\n" );
|
|
RedPrintf( " Specifies the seed for the random number generator (default timestamp).\n" );
|
|
RedPrintf( " --verbose, -v\n" );
|
|
RedPrintf( " Specifies verbose mode (without this, test is very quiet).\n" );
|
|
RedPrintf( " --dev=devname, -D devname\n" );
|
|
RedPrintf( " Specifies the device name. This is typically only meaningful when\n" );
|
|
RedPrintf( " running the test on a host machine. This can be \"ram\" to test on a RAM\n" );
|
|
RedPrintf( " disk, the path and name of a file disk (e.g., red.bin); or an OS-specific\n" );
|
|
RedPrintf( " reference to a device (on Windows, a drive letter like G: or a device name\n" );
|
|
RedPrintf( " like \\\\.\\PhysicalDrive7).\n" );
|
|
RedPrintf( " --help, -H\n" );
|
|
RedPrintf( " Prints this usage text and exits.\n\n" );
|
|
RedPrintf( "Warning: This test will format the volume -- destroying all existing data.\n\n" );
|
|
}
|
|
|
|
static void creat_f( int opno,
|
|
long r )
|
|
{
|
|
int e;
|
|
int e1;
|
|
pathname_t f;
|
|
int fd;
|
|
fent_t * fep;
|
|
int id;
|
|
int parid;
|
|
int type;
|
|
int v;
|
|
int v1;
|
|
int esz = 0;
|
|
|
|
if( !get_fname( FT_DIRm, r, NULL, NULL, &fep, &v1 ) )
|
|
{
|
|
parid = -1;
|
|
}
|
|
else
|
|
{
|
|
parid = fep->id;
|
|
}
|
|
|
|
init_pathname( &f );
|
|
type = rtpct ? ( ( int ) ( random() % 100 ) > rtpct ? FT_REG : FT_RTF ) : FT_REG;
|
|
e = generate_fname( fep, type, &f, &id, &v );
|
|
v |= v1;
|
|
|
|
if( !e )
|
|
{
|
|
if( v )
|
|
{
|
|
fent_to_name( &f, &flist[ FT_DIR ], fep );
|
|
RedPrintf( "%d/%d: creat - no filename from %s\n",
|
|
procid, opno, f.path );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
return;
|
|
}
|
|
|
|
fd = creat_path( &f, 0666 );
|
|
e = fd < 0 ? errno : 0;
|
|
e1 = 0;
|
|
check_cwd();
|
|
esz = 0;
|
|
|
|
if( fd >= 0 )
|
|
{
|
|
add_to_flist( type, id, parid );
|
|
close( fd );
|
|
}
|
|
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: creat %s x:%d %d %d\n", procid, opno, f.path,
|
|
esz, e, e1 );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
}
|
|
|
|
static void fdatasync_f( int opno,
|
|
long r )
|
|
{
|
|
int e;
|
|
pathname_t f;
|
|
int fd;
|
|
int v;
|
|
|
|
init_pathname( &f );
|
|
|
|
if( !get_fname( FT_REGFILE, r, &f, NULL, NULL, &v ) )
|
|
{
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: fdatasync - no filename\n",
|
|
procid, opno );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
return;
|
|
}
|
|
|
|
fd = open_path( &f, O_WRONLY );
|
|
e = fd < 0 ? errno : 0;
|
|
check_cwd();
|
|
|
|
if( fd < 0 )
|
|
{
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: fdatasync - open %s failed %d\n",
|
|
procid, opno, f.path, e );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
return;
|
|
}
|
|
|
|
e = fdatasync( fd ) < 0 ? errno : 0;
|
|
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: fdatasync %s %d\n", procid, opno, f.path, e );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
close( fd );
|
|
}
|
|
|
|
static void fsync_f( int opno,
|
|
long r )
|
|
{
|
|
int e;
|
|
pathname_t f;
|
|
int fd;
|
|
int v;
|
|
|
|
init_pathname( &f );
|
|
|
|
if( !get_fname( FT_REGFILE, r, &f, NULL, NULL, &v ) )
|
|
{
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: fsync - no filename\n", procid, opno );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
return;
|
|
}
|
|
|
|
fd = open_path( &f, O_WRONLY );
|
|
e = fd < 0 ? errno : 0;
|
|
check_cwd();
|
|
|
|
if( fd < 0 )
|
|
{
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: fsync - open %s failed %d\n",
|
|
procid, opno, f.path, e );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
return;
|
|
}
|
|
|
|
e = fsync( fd ) < 0 ? errno : 0;
|
|
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: fsync %s %d\n", procid, opno, f.path, e );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
close( fd );
|
|
}
|
|
|
|
static void getdents_f( int opno,
|
|
long r )
|
|
{
|
|
DIR * dir;
|
|
pathname_t f;
|
|
int v;
|
|
|
|
init_pathname( &f );
|
|
|
|
if( !get_fname( FT_DIRm, r, &f, NULL, NULL, &v ) )
|
|
{
|
|
append_pathname( &f, "." );
|
|
}
|
|
|
|
dir = opendir_path( &f );
|
|
check_cwd();
|
|
|
|
if( dir == NULL )
|
|
{
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: getdents - can't open %s\n",
|
|
procid, opno, f.path );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
return;
|
|
}
|
|
|
|
while( readdir64( dir ) != NULL )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: getdents %s 0\n", procid, opno, f.path );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
closedir( dir );
|
|
}
|
|
|
|
static void link_f( int opno,
|
|
long r )
|
|
{
|
|
int e;
|
|
pathname_t f;
|
|
fent_t * fep;
|
|
flist_t * flp;
|
|
int id;
|
|
pathname_t l;
|
|
int parid;
|
|
int v;
|
|
int v1;
|
|
|
|
init_pathname( &f );
|
|
|
|
if( !get_fname( FT_NOTDIR, r, &f, &flp, NULL, &v1 ) )
|
|
{
|
|
if( v1 )
|
|
{
|
|
RedPrintf( "%d/%d: link - no file\n", procid, opno );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
return;
|
|
}
|
|
|
|
if( !get_fname( FT_DIRm, random(), NULL, NULL, &fep, &v ) )
|
|
{
|
|
parid = -1;
|
|
}
|
|
else
|
|
{
|
|
parid = fep->id;
|
|
}
|
|
|
|
v |= v1;
|
|
init_pathname( &l );
|
|
e = generate_fname( fep, ( int ) ( flp - flist ), &l, &id, &v1 );
|
|
v |= v1;
|
|
|
|
if( !e )
|
|
{
|
|
if( v )
|
|
{
|
|
fent_to_name( &l, &flist[ FT_DIR ], fep );
|
|
RedPrintf( "%d/%d: link - no filename from %s\n",
|
|
procid, opno, l.path );
|
|
}
|
|
|
|
free_pathname( &l );
|
|
free_pathname( &f );
|
|
return;
|
|
}
|
|
|
|
e = link_path( &f, &l ) < 0 ? errno : 0;
|
|
check_cwd();
|
|
|
|
if( e == 0 )
|
|
{
|
|
add_to_flist( ( int ) ( flp - flist ), id, parid );
|
|
}
|
|
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: link %s %s %d\n", procid, opno, f.path, l.path,
|
|
e );
|
|
}
|
|
|
|
free_pathname( &l );
|
|
free_pathname( &f );
|
|
}
|
|
|
|
static void mkdir_f( int opno,
|
|
long r )
|
|
{
|
|
int e;
|
|
pathname_t f;
|
|
fent_t * fep;
|
|
int id;
|
|
int parid;
|
|
int v;
|
|
int v1;
|
|
|
|
if( !get_fname( FT_DIRm, r, NULL, NULL, &fep, &v ) )
|
|
{
|
|
parid = -1;
|
|
}
|
|
else
|
|
{
|
|
parid = fep->id;
|
|
}
|
|
|
|
init_pathname( &f );
|
|
e = generate_fname( fep, FT_DIR, &f, &id, &v1 );
|
|
v |= v1;
|
|
|
|
if( !e )
|
|
{
|
|
if( v )
|
|
{
|
|
fent_to_name( &f, &flist[ FT_DIR ], fep );
|
|
RedPrintf( "%d/%d: mkdir - no filename from %s\n",
|
|
procid, opno, f.path );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
return;
|
|
}
|
|
|
|
e = mkdir_path( &f, 0777 ) < 0 ? errno : 0;
|
|
check_cwd();
|
|
|
|
if( e == 0 )
|
|
{
|
|
add_to_flist( FT_DIR, id, parid );
|
|
}
|
|
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: mkdir %s %d\n", procid, opno, f.path, e );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
}
|
|
|
|
static void read_f( int opno,
|
|
long r )
|
|
{
|
|
char * buf;
|
|
int e;
|
|
pathname_t f;
|
|
int fd;
|
|
uint32_t len;
|
|
__int64_t lr;
|
|
off64_t off;
|
|
REDSTAT stb;
|
|
int v;
|
|
|
|
init_pathname( &f );
|
|
|
|
if( !get_fname( FT_REGFILE, r, &f, NULL, NULL, &v ) )
|
|
{
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: read - no filename\n", procid, opno );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
return;
|
|
}
|
|
|
|
fd = open_path( &f, O_RDONLY );
|
|
e = fd < 0 ? errno : 0;
|
|
check_cwd();
|
|
|
|
if( fd < 0 )
|
|
{
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: read - open %s failed %d\n",
|
|
procid, opno, f.path, e );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
return;
|
|
}
|
|
|
|
if( fstat64( fd, &stb ) < 0 )
|
|
{
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: read - fstat64 %s failed %d\n",
|
|
procid, opno, f.path, errno );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
close( fd );
|
|
return;
|
|
}
|
|
|
|
if( stb.st_size == 0 )
|
|
{
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: read - %s zero size\n", procid, opno,
|
|
f.path );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
close( fd );
|
|
return;
|
|
}
|
|
|
|
lr = ( ( __int64_t ) random() << 32 ) + random();
|
|
off = ( off64_t ) ( lr % stb.st_size );
|
|
lseek64( fd, off, SEEK_SET );
|
|
len = ( random() % ( getpagesize() * 4 ) ) + 1;
|
|
buf = malloc( len );
|
|
e = read( fd, buf, len ) < 0 ? errno : 0;
|
|
free( buf );
|
|
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: read %s [%lld,%ld] %d\n",
|
|
procid, opno, f.path, ( long long ) off, ( long int ) len, e );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
close( fd );
|
|
}
|
|
|
|
static void rename_f( int opno,
|
|
long r )
|
|
{
|
|
fent_t * dfep;
|
|
int e;
|
|
pathname_t f;
|
|
fent_t * fep;
|
|
flist_t * flp;
|
|
int id;
|
|
pathname_t newf;
|
|
int oldid;
|
|
int parid;
|
|
int v;
|
|
int v1;
|
|
|
|
init_pathname( &f );
|
|
|
|
if( !get_fname( FT_ANYm, r, &f, &flp, &fep, &v1 ) )
|
|
{
|
|
if( v1 )
|
|
{
|
|
RedPrintf( "%d/%d: rename - no filename\n", procid, opno );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
return;
|
|
}
|
|
|
|
if( !get_fname( FT_DIRm, random(), NULL, NULL, &dfep, &v ) )
|
|
{
|
|
parid = -1;
|
|
}
|
|
else
|
|
{
|
|
parid = dfep->id;
|
|
}
|
|
|
|
v |= v1;
|
|
init_pathname( &newf );
|
|
e = generate_fname( dfep, ( int ) ( flp - flist ), &newf, &id, &v1 );
|
|
v |= v1;
|
|
|
|
if( !e )
|
|
{
|
|
if( v )
|
|
{
|
|
fent_to_name( &f, &flist[ FT_DIR ], dfep );
|
|
RedPrintf( "%d/%d: rename - no filename from %s\n",
|
|
procid, opno, f.path );
|
|
}
|
|
|
|
free_pathname( &newf );
|
|
free_pathname( &f );
|
|
return;
|
|
}
|
|
|
|
e = rename_path( &f, &newf ) < 0 ? errno : 0;
|
|
check_cwd();
|
|
|
|
if( e == 0 )
|
|
{
|
|
if( flp - flist == FT_DIR )
|
|
{
|
|
oldid = fep->id;
|
|
fix_parent( oldid, id );
|
|
}
|
|
|
|
del_from_flist( ( int ) ( flp - flist ), ( int ) ( fep - flp->fents ) );
|
|
add_to_flist( ( int ) ( flp - flist ), id, parid );
|
|
}
|
|
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: rename %s to %s %d\n", procid, opno, f.path,
|
|
newf.path, e );
|
|
}
|
|
|
|
free_pathname( &newf );
|
|
free_pathname( &f );
|
|
}
|
|
|
|
static void rmdir_f( int opno,
|
|
long r )
|
|
{
|
|
int e;
|
|
pathname_t f;
|
|
fent_t * fep;
|
|
int v;
|
|
|
|
init_pathname( &f );
|
|
|
|
if( !get_fname( FT_DIRm, r, &f, NULL, &fep, &v ) )
|
|
{
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: rmdir - no directory\n", procid, opno );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
return;
|
|
}
|
|
|
|
e = rmdir_path( &f ) < 0 ? errno : 0;
|
|
check_cwd();
|
|
|
|
if( e == 0 )
|
|
{
|
|
del_from_flist( FT_DIR, ( int ) ( fep - flist[ FT_DIR ].fents ) );
|
|
}
|
|
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: rmdir %s %d\n", procid, opno, f.path, e );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
}
|
|
|
|
static void stat_f( int opno,
|
|
long r )
|
|
{
|
|
int e;
|
|
pathname_t f;
|
|
REDSTAT stb;
|
|
int v;
|
|
|
|
init_pathname( &f );
|
|
|
|
if( !get_fname( FT_ANYm, r, &f, NULL, NULL, &v ) )
|
|
{
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: stat - no entries\n", procid, opno );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
return;
|
|
}
|
|
|
|
e = lstat64_path( &f, &stb ) < 0 ? errno : 0;
|
|
check_cwd();
|
|
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: stat %s %d\n", procid, opno, f.path, e );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
}
|
|
|
|
static void truncate_f( int opno,
|
|
long r )
|
|
{
|
|
int e;
|
|
pathname_t f;
|
|
__int64_t lr;
|
|
off64_t off;
|
|
REDSTAT stb;
|
|
int v;
|
|
|
|
init_pathname( &f );
|
|
|
|
if( !get_fname( FT_REGFILE, r, &f, NULL, NULL, &v ) )
|
|
{
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: truncate - no filename\n", procid, opno );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
return;
|
|
}
|
|
|
|
e = stat64_path( &f, &stb ) < 0 ? errno : 0;
|
|
check_cwd();
|
|
|
|
if( e > 0 )
|
|
{
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: truncate - stat64 %s failed %d\n",
|
|
procid, opno, f.path, e );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
return;
|
|
}
|
|
|
|
lr = ( ( __int64_t ) random() << 32 ) + random();
|
|
off = lr % MIN( stb.st_size + ( 1024 * 1024 ), MAXFSIZE );
|
|
off %= maxfsize;
|
|
e = truncate64_path( &f, off ) < 0 ? errno : 0;
|
|
check_cwd();
|
|
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: truncate %s %lld %d\n", procid, opno, f.path,
|
|
( long long ) off, e );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
}
|
|
|
|
static void unlink_f( int opno,
|
|
long r )
|
|
{
|
|
int e;
|
|
pathname_t f;
|
|
fent_t * fep;
|
|
flist_t * flp;
|
|
int v;
|
|
|
|
init_pathname( &f );
|
|
|
|
if( !get_fname( FT_NOTDIR, r, &f, &flp, &fep, &v ) )
|
|
{
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: unlink - no file\n", procid, opno );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
return;
|
|
}
|
|
|
|
e = unlink_path( &f ) < 0 ? errno : 0;
|
|
check_cwd();
|
|
|
|
if( e == 0 )
|
|
{
|
|
del_from_flist( ( int ) ( flp - flist ), ( int ) ( fep - flp->fents ) );
|
|
}
|
|
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: unlink %s %d\n", procid, opno, f.path, e );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
}
|
|
|
|
static void write_f( int opno,
|
|
long r )
|
|
{
|
|
char * buf;
|
|
int e;
|
|
pathname_t f;
|
|
int fd;
|
|
uint32_t len;
|
|
__int64_t lr;
|
|
off64_t off;
|
|
REDSTAT stb;
|
|
int v;
|
|
|
|
init_pathname( &f );
|
|
|
|
if( !get_fname( FT_REGm, r, &f, NULL, NULL, &v ) )
|
|
{
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: write - no filename\n", procid, opno );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
return;
|
|
}
|
|
|
|
fd = open_path( &f, O_WRONLY );
|
|
e = fd < 0 ? errno : 0;
|
|
check_cwd();
|
|
|
|
if( fd < 0 )
|
|
{
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: write - open %s failed %d\n",
|
|
procid, opno, f.path, e );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
return;
|
|
}
|
|
|
|
if( fstat64( fd, &stb ) < 0 )
|
|
{
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: write - fstat64 %s failed %d\n",
|
|
procid, opno, f.path, errno );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
close( fd );
|
|
return;
|
|
}
|
|
|
|
lr = ( ( __int64_t ) random() << 32 ) + random();
|
|
off = ( off64_t ) ( lr % MIN( stb.st_size + ( 1024 * 1024 ), MAXFSIZE ) );
|
|
off %= maxfsize;
|
|
lseek64( fd, off, SEEK_SET );
|
|
len = ( random() % ( getpagesize() * 4 ) ) + 1;
|
|
buf = malloc( len );
|
|
memset( buf, nameseq & 0xff, len );
|
|
e = write( fd, buf, len ) < 0 ? errno : 0;
|
|
free( buf );
|
|
|
|
if( v )
|
|
{
|
|
RedPrintf( "%d/%d: write %s [%lld,%ld] %d\n",
|
|
procid, opno, f.path, ( long long ) off, ( long int ) len, e );
|
|
}
|
|
|
|
free_pathname( &f );
|
|
close( fd );
|
|
}
|
|
|
|
|
|
#if REDCONF_CHECKER == 1
|
|
static void check_f( int opno,
|
|
long r )
|
|
{
|
|
int32_t ret;
|
|
const char * pszVolume = gpRedVolConf->pszPathPrefix;
|
|
|
|
( void ) r;
|
|
|
|
errno = 0;
|
|
|
|
ret = red_transact( pszVolume );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
ret = red_umount( pszVolume );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
int32_t ret2;
|
|
|
|
errno = -RedCoreVolCheck();
|
|
|
|
if( errno != 0 )
|
|
{
|
|
ret = -1;
|
|
}
|
|
|
|
ret2 = red_mount( pszVolume );
|
|
|
|
if( ret == 0 )
|
|
{
|
|
ret = ret2;
|
|
}
|
|
|
|
if( ret2 != 0 )
|
|
{
|
|
exit( 1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
if( verbose )
|
|
{
|
|
RedPrintf( "%d/%d: check %s %d\n", procid, opno, pszVolume, errno );
|
|
}
|
|
}
|
|
#endif /* if REDCONF_CHECKER == 1 */
|
|
|
|
|
|
#endif /* FSSTRESS_SUPPORTED */
|