diff --git a/lib/dsmsendrecv.c b/lib/dsmsendrecv.c index 4b22620..45c6702 100644 --- a/lib/dsmsendrecv.c +++ b/lib/dsmsendrecv.c @@ -439,14 +439,15 @@ int tsm_deletefile(dsUint32_t dsmHandle, dsmQueryType qType, qryArchiveData qaDa extern int verbose; dsInt16_t rc=0; dsUint16_t reason=0; - dsmDelInfo *dInfoP; dsmDelType dType; + dsmDelInfo delInfo; struct matchone_cb_data cbdata; dsmBool_t friendly=bFalse; cbdata.numfound = 0; - rc = tsm_queryfile(dsmHandle,qType,tsm_matchone_cb,&cbdata,qaData,qbData,friendly); + /** Our purge function uses qtReserved8 - this will break in a future release of the API **/ + rc = tsm_queryfile(dsmHandle,((qType == qtBackup || qType == qtReserved8) ? qtBackup : qtArchive),tsm_matchone_cb,&cbdata,qaData,qbData,friendly); if (rc != DSM_RC_OK) { return 0; } @@ -462,24 +463,35 @@ int tsm_deletefile(dsUint32_t dsmHandle, dsmQueryType qType, qryArchiveData qaDa dType = dtArchive; - delArch daInfo; - daInfo.stVersion = delArchVersion; - daInfo.objId = cbdata.objId; - - dInfoP = (dsmDelInfo *) &daInfo; + delInfo.archInfo.stVersion = delArchVersion; + delInfo.archInfo.objId = cbdata.objId; } else if (qType == qtBackup) { if (verbose) - fprintf(stderr,"%s: Deleting backup file %s\n",__func__,dsmObjnameToStr(*qbData.objName)); + fprintf(stderr,"%s: Deleting backup file %s with id [%u-%u]\n",__func__,dsmObjnameToStr(*qbData.objName),cbdata.objId.hi,cbdata.objId.lo); dType = dtBackup; - delBack dbInfo; - dbInfo.stVersion = delBackVersion; - dbInfo.objNameP = qbData.objName; - dbInfo.copyGroup = cbdata.copyGroup; + dsmObjName objName; + memset(&objName,0x00,sizeof(objName)); + strcpy(objName.fs,qbData.objName->fs); + strcpy(objName.hl,qbData.objName->hl); + strcpy(objName.ll,qbData.objName->ll); + objName.objType = DSM_OBJ_FILE; - dInfoP = (dsmDelInfo *) &dbInfo; + delInfo.backInfo.stVersion = delBackVersion; + delInfo.backInfo.objNameP = &objName; + delInfo.backInfo.copyGroup = cbdata.copyGroup; + + /** Our purge function uses qtReserved8 - this will break in a future release of the API **/ + } else if (qType == qtReserved8) { + if (verbose) + fprintf(stderr,"%s: Purging backup file %s with id [%u-%u]\n",__func__,dsmObjnameToStr(*qbData.objName),cbdata.objId.hi,cbdata.objId.lo); + + dType = dtBackupID; + + delInfo.backIDInfo.stVersion = delBackIDVersion; + delInfo.backIDInfo.objId = cbdata.objId; } else { fprintf(stderr,"%s: UNKNOWN Type %d\n",__func__,qType); @@ -492,7 +504,7 @@ int tsm_deletefile(dsUint32_t dsmHandle, dsmQueryType qType, qryArchiveData qaDa return 0; } - rc = dsmDeleteObj(dsmHandle,dType,*dInfoP); + rc = dsmDeleteObj(dsmHandle,dType,delInfo); if (rc != DSM_RC_OK) { printf("%s: dsmDeleteObj() failed %s\n",__func__,tsm_printerr(dsmHandle,rc)); return 0; diff --git a/test/test.sh b/test/test.sh index bba22c3..8cfc528 100755 --- a/test/test.sh +++ b/test/test.sh @@ -1,43 +1,94 @@ #!/bin/sh +cd $(dirname $0) + set -e FILE=random.1m +VERBOSE=${VERBOSE:-""} # Create TEST File -MD5=$(dd if=/dev/urandom bs=1024 count=1024 2>/dev/null |tee ${FILE} |md5sum |awk '{print $1}') +MD5=$(dd if=/dev/urandom bs=10240 count=1024 2>/dev/null |tee ${FILE} |md5sum |awk '{print $1}') SIZE=$(ls -l ${FILE}|awk '{print $5}') +TSMOPT="-VIRTUALNODENAME=TEST -PASSWORD=TEST -COMPRESSALWAYS=YES" TSMPIPE=../tsmpipe echo "MD5 of [${FILE}] is [${MD5}] (${SIZE})" -ln -s /opt/tivoli/tsm/client/api/bin64/EN_US +ln -fs /opt/tivoli/tsm/client/api/bin64/EN_US export DSMI_DIR=./ export DSMI_CONFIG=dsm.opt # Send file to TSM -echo "+ TEST SEND FILE!" -cat ${FILE} | ${TSMPIPE} -Bcm MD5 -s /test -f ${FILE} -vvv -l ${SIZE} -O"-VIRTUALNODENAME=TEST -PASSWORD=TEST" +echo "+++ TEST BACKUP ++++!" +echo "+ BACKUP: SEND FILE!" +cat ${FILE} | ${TSMPIPE} -Bcm MD5 -s /test -f ${FILE} ${VERBOSE} -l ${SIZE} -O"${TSMOPT}" # Send a second time -echo "+ TEST SEND FILE AGAIN!" -cat ${FILE} | ${TSMPIPE} -Bcm MD5 -s /test -f ${FILE} -vvv -l ${SIZE} -O"-VIRTUALNODENAME=TEST -PASSWORD=TEST" +echo "+ BACKUP: SEND FILE AGAIN!" +cat ${FILE} | ${TSMPIPE} -Bcm MD5 -s /test -f ${FILE} ${VERBOSE} -l ${SIZE} -O"${TSMOPT}" # List it -echo "+ TEST LIST FILE!" -${TSMPIPE} -Bts /test -f ${FILE} -vvv -O"-VIRTUALNODENAME=TEST -PASSWORD=TEST" +echo "+ BACKUP: LIST FILE!" +${TSMPIPE} -Bts /test -f ${FILE} -O"${TSMOPT}" # Get it back -echo "+ TEST RETRIEVE FILE!" -${TSMPIPE} -Bxs /test -f ${FILE} -vvv -O"-VIRTUALNODENAME=TEST -PASSWORD=TEST" > ${FILE}.back +echo "+ BACKUP: RETRIEVE FILE!" +${TSMPIPE} -Bxs /test -f ${FILE} ${VERBOSE} -O"${TSMOPT}" > ${FILE}.back RETRIEVE=$(md5sum ${FILE}.back | awk '{print $1}') if [ "${MD5}" != "${RETRIEVE}" ]; then exit 1 else - echo "+ RETRIEVE VALID (${RETRIEVE})!" + echo "+ BACKUP: RETRIEVE VALID (${RETRIEVE})!" fi +# Purge the last one +echo "+ BACKUP: PURGE!" +${TSMPIPE} -Bgs /test -f ${FILE} ${VERBOSE} -O"${TSMOPT}" + +# List it +echo "+ BACKUP: LIST FILE IS PURGED!" +${TSMPIPE} -Bts /test -f ${FILE} -O"${TSMOPT}" + # Delete it -echo "+ TEST DELETE!" -${TSMPIPE} -Bds /test -f ${FILE} -vvv -O"-VIRTUALNODENAME=TEST -PASSWORD=TEST" +echo "+ BACKUP: DELETE!" +${TSMPIPE} -Bds /test -f ${FILE} ${VERBOSE} -O"${TSMOPT}" + +# List it +echo "+ BACKUP: LIST FILE IS DELETED!" +${TSMPIPE} -Bts /test -f ${FILE} -O"${TSMOPT}" +echo "+++ END BACKUP ++++!" + +echo "+++ TEST ARCHIVE ++++!" +echo "+ ARCHIVE: SEND FILE!" +cat ${FILE} | ${TSMPIPE} -Acm MD5 -s /test -f ${FILE} ${VERBOSE} -l ${SIZE} -D"Test Archive File" -O"${TSMOPT}" + +# Send a second time +echo "+ ARCHIVE: SEND FILE AGAIN! (delay)" +sleep 2 +cat ${FILE} | ${TSMPIPE} -Acm MD5 -s /test -f ${FILE} ${VERBOSE} -l ${SIZE} -D"Test Archive File" -O"${TSMOPT}" +DATE=$(date +%m%d%Y:%H%M%S) + +# List it +echo "+ ARCIHVE: LIST FILE!" +${TSMPIPE} -Ats /test -f ${FILE} -O"${TSMOPT}" + +# Get it back +echo "+ ARCIHVE: RETRIEVE FILE! (${DATE})" +${TSMPIPE} -Axs /test -f ${FILE} -n ${DATE} ${VERBOSE} -O"${TSMOPT}" > ${FILE}.back +RETRIEVE=$(md5sum ${FILE}.back | awk '{print $1}') +if [ "${MD5}" != "${RETRIEVE}" ]; then + exit 1 +else + echo "+ ARCHIVE: RETRIEVE VALID (${RETRIEVE})!" +fi + +# Purge the last one +echo "+ ARCHIVE: DELETE!" +${TSMPIPE} -Ads /test -f ${FILE} -n ${DATE} ${VERBOSE} -O"${TSMOPT}" + +# List it +echo "+ ARCHIVE: LIST FILE AFTER DELETE!" +${TSMPIPE} -Ats /test -f ${FILE} -O"${TSMOPT}" +echo "+++ END ARCHIVE ++++!" rm -f EN_US ${FILE} ${FILE}.back diff --git a/tsmpipe.c b/tsmpipe.c index c2d7455..a81bbde 100644 --- a/tsmpipe.c +++ b/tsmpipe.c @@ -69,7 +69,7 @@ void usage(void) { fprintf(stderr, "tsmpipe %s, usage:\n" "\n" - "tsmpipe [-i|-p]|[[-A|-B|-U] [-c|-x|-d|-t] -s fsname -f filepath [-l len] ...]\n" + "tsmpipe [-i|-p]|[[-A|-B|-U] [-c|-x|-d|-g|-t] -s fsname -f filepath [-l len] ...]\n" " -i Show session information\n" " -p Set Password\n" " -A, -B and -U are mutually exclusive:\n" @@ -80,6 +80,7 @@ void usage(void) { " -c Create: Read from stdin and store in TSM\n" " -x eXtract: Recall from TSM and write to stdout\n" " -d Delete: Delete object from TSM\n" + " -g purGe: Active Delete object from TSM\n" " -t lisT: Print filelist to stdout\n" " -s and -f are required arguments for (-A/ -B operations):\n" " -s fsname Name of filespace in TSM\n" @@ -130,7 +131,7 @@ int main(int argc, char *argv[]) { char *digest=NULL; #endif - while ((c = getopt(argc, argv, "hiABcxdtUvVe:E:f:Fl:L:m:n:N:s:D:O:pP:")) != -1) { + while ((c = getopt(argc, argv, "hiABcxdtUvVe:E:f:Fgl:L:m:n:N:s:D:O:pP:")) != -1) { switch(c) { case 'h': usage(); break; case 'A': qType = qtArchive; break; @@ -141,6 +142,7 @@ int main(int argc, char *argv[]) { case 'c': if (action != 0) usage_action(); action = ACTION_CREATE; break; case 'x': if (action != 0) usage_action(); action = ACTION_EXTRACT; break; case 'd': if (action != 0) usage_action(); action = ACTION_DELETE; break; + case 'g': if (action != 0) usage_action(); action = ACTION_PURGE; break; case 't': if (action != 0) usage_action(); action = ACTION_LIST; break; case 'U': if (action != 0) usage_action(); action = ACTION_UPDATE; break; @@ -223,6 +225,10 @@ int main(int argc, char *argv[]) { } } + if (action == ACTION_PURGE && qType != qtBackup) { + debugLog(0,__func__,"ERROR: -g can only be used with -B",1); + } + /* Let the TSM api get the signals */ signal(SIGPIPE, SIG_IGN); signal(SIGINT, SIG_IGN); @@ -272,6 +278,7 @@ int main(int argc, char *argv[]) { break; case ACTION_DELETE: + case ACTION_PURGE: case ACTION_LIST: case ACTION_EXTRACT: memset(&qaData,0x00,sizeof(qryArchiveData)); @@ -286,6 +293,7 @@ int main(int argc, char *argv[]) { qbData.owner = ""; switch (action) { + case ACTION_PURGE: case ACTION_DELETE: qbData.objState = DSM_ACTIVE; break; @@ -299,7 +307,7 @@ int main(int argc, char *argv[]) { break; } - if (action == ACTION_DELETE || ! pitdate) { + if (action == ACTION_DELETE || action == ACTION_PURGE || ! pitdate) { qbData.pitDate.year = DATE_MINUS_INFINITE; } else { qbData.pitDate = dsmStrToDate(pitdate); @@ -342,6 +350,12 @@ int main(int argc, char *argv[]) { rc = tsm_deletefile(dsmHandle,qType,qaData,qbData); break; + case ACTION_PURGE: + debugLog(2,__func__,"PURGE Operation",0); + /** We use qtReserved8 to let tsm_deletefile() know that we want to purge - this will break a future API **/ + rc = tsm_deletefile(dsmHandle,qtReserved8,qaData,qbData); + break; + case ACTION_EXTRACT: debugLog(2,__func__,"EXTRACT Operation",0); rc = tsm_restorefile(dsmHandle,qType,qaData,qbData); diff --git a/tsmpipe.h b/tsmpipe.h index 6945d40..b9c60dd 100644 --- a/tsmpipe.h +++ b/tsmpipe.h @@ -1,4 +1,4 @@ -#define _TSMPIPE_VERSION "1.6.5" +#define _TSMPIPE_VERSION "1.6.6" #define INPUTLEN 1025 #define ACTION_INFO 1 @@ -9,6 +9,7 @@ #define ACTION_UPDATE 6 #define ACTION_CREATE_UPDATE 7 #define ACTION_PASSWORD 8 +#define ACTION_PURGE 9 #define DSM_COMM_TCPIP6 6 // There is no DSM_COMM_ const for TCPIPv6 // If you want to use MD5/SHA1 calculations as the data goes in here, ensure this is defined.