543 lines
14 KiB
C
543 lines
14 KiB
C
/*
|
|
* This function controls our communication with TSM
|
|
*/
|
|
|
|
#define BUFLEN (64*1024-4)
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include "dsmrc.h"
|
|
#include "dsmapitd.h"
|
|
#include "dsmapifp.h"
|
|
|
|
#include "../tsmpipe.h"
|
|
|
|
int tsm_matchone_cb(dsmQueryType qType, DataBlk *qResp, void *userdata) {
|
|
struct matchone_cb_data *cbdata = userdata;
|
|
|
|
cbdata->numfound++;
|
|
|
|
if (qType == qtArchive) {
|
|
qryRespArchiveData *qr = (void *) qResp->bufferPtr;
|
|
|
|
cbdata->objId = qr->objId;
|
|
|
|
} else if(qType == qtBackup) {
|
|
qryRespBackupData *qr = (void *) qResp->bufferPtr;
|
|
|
|
cbdata->objId = qr->objId;
|
|
cbdata->copyGroup = qr->copyGroup;
|
|
|
|
} else {
|
|
fprintf(stderr,"%s: UNKNOWN Type %d\n",__func__,qType);
|
|
return -1;
|
|
}
|
|
|
|
if (cbdata->numfound > 1) {
|
|
debugLog(0,__func__,"FAILED: The file specification matched multiple files.",0);
|
|
return -1;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Read/Write a buffer full of data from file descripter */
|
|
ssize_t rw_full(int fd, void *buf, size_t count) {
|
|
ssize_t done=0;
|
|
ssize_t len=0;
|
|
|
|
while (count) {
|
|
if (fd == STDIN_FILENO) {
|
|
len = read(fd,buf+done,count);
|
|
|
|
} else if (fd == STDOUT_FILENO) {
|
|
len = write(fd,buf+done,count);
|
|
|
|
} else {
|
|
debugLog(0,__func__,"ERROR: Unknown FD",4);
|
|
}
|
|
|
|
if (len == 0) {
|
|
break;
|
|
|
|
} else if (len < 0) {
|
|
if (errno == EINTR) {
|
|
continue;
|
|
|
|
} else {
|
|
if (done == 0)
|
|
done = -1;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
count -= len;
|
|
done += len;
|
|
}
|
|
|
|
return done;
|
|
}
|
|
|
|
/* Register a filespace in TSM */
|
|
int tsm_regfs(dsUint32_t dsmHandle, char *fsname) {
|
|
dsInt16_t rc=0;
|
|
regFSData regFS;
|
|
|
|
memset(®FS, 0, sizeof(regFS));
|
|
|
|
regFS.fsName = fsname;
|
|
regFS.fsType = "TSMPIPE";
|
|
|
|
rc = dsmRegisterFS(dsmHandle,®FS);
|
|
|
|
if (rc != DSM_RC_OK && rc != DSM_RC_FS_ALREADY_REGED) {
|
|
printf("%s: dsmRegisterFS() failed %s\n",__func__,tsm_printerr(dsmHandle,rc));
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Send data to TSM for storage */
|
|
#ifdef USE_DIGEST
|
|
double tsm_sendfile(dsUint32_t dsmHandle, char *fsname, char *filename, long long length, char *description, dsmSendType sendtype, dsBool_t startstop, dsBool_t summary, char *digest) {
|
|
#else
|
|
double tsm_sendfile(dsUint32_t dsmHandle, char *fsname, char *filename, long long length, char *description, dsmSendType sendtype, dsBool_t startstop, dsBool_t summary) {
|
|
#endif
|
|
extern int verbose;
|
|
char *buffer;
|
|
dsInt16_t rc=0;
|
|
dsmObjName objName;
|
|
mcBindKey mcBindKey;
|
|
ObjAttr objAttr;
|
|
DataBlk dataBlk;
|
|
sndArchiveData archData, *archDataP=NULL;
|
|
ssize_t nbytes;
|
|
dsmEndSendObjExIn_t dsmEndSendObjExIn;
|
|
dsmEndSendObjExOut_t dsmEndSendObjExOut;
|
|
dsUint16_t reason=0;
|
|
|
|
buffer = malloc(BUFLEN);
|
|
if (buffer==NULL) {
|
|
perror("tsm_sendfile: Argh, out of memory?");
|
|
exit(255);
|
|
}
|
|
|
|
#ifdef USE_DIGEST
|
|
char digest_str[EVP_MAX_MD_SIZE*2];
|
|
EVP_MD_CTX mdctx;
|
|
const EVP_MD *md=NULL;
|
|
unsigned char md_value[EVP_MAX_MD_SIZE];
|
|
unsigned int md_len, i;
|
|
|
|
if (digest) {
|
|
OpenSSL_add_all_digests();
|
|
|
|
md = EVP_get_digestbyname(digest);
|
|
|
|
if (! md) {
|
|
printf("%s: Unknown message digest %s\n",__func__,digest);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
// Register our filespace
|
|
if (! tsm_regfs(dsmHandle,fsname))
|
|
exit(3);
|
|
|
|
// Warn if we wont do compression
|
|
if (verbose && compressEnabled && length <= DSM_MIN_COMPRESS_SIZE)
|
|
fprintf(stderr,"%s: WARNING: Size (%i) too small for compression/de-duplication, even though it is enabled",__func__,(int)length);
|
|
|
|
// Log start backup
|
|
if (startstop)
|
|
tsm_datefs(dsmHandle,fsname,DSM_FSUPD_BACKSTARTDATE);
|
|
|
|
// Start a Transaction
|
|
rc = dsmBeginTxn(dsmHandle);
|
|
if (rc != DSM_RC_OK) {
|
|
printf("%s: dsmBeginTxn() failed %s\n",__func__,tsm_printerr(dsmHandle,rc));
|
|
return 0;
|
|
}
|
|
|
|
memset(&objName,0x00,sizeof(objName));
|
|
objName = dsmNameToObjname(fsname,filename);
|
|
|
|
if (verbose)
|
|
fprintf(stderr,"%s: Starting to send stdin as %s\n",__func__,dsmObjnameToStr(objName));
|
|
|
|
memset(&mcBindKey,0x00,sizeof(mcBindKey));
|
|
mcBindKey.stVersion = mcBindKeyVersion;
|
|
rc = dsmBindMC(dsmHandle,&objName,sendtype,&mcBindKey);
|
|
if (rc != DSM_RC_OK) {
|
|
printf("%s: dsmBindMC() failed %s\n",__func__,tsm_printerr(dsmHandle,rc));
|
|
return 0;
|
|
}
|
|
|
|
if (verbose > 1) {
|
|
char *cgdest=NULL;
|
|
|
|
fprintf(stderr,"%s: Bound to Management Class: %s\n",__func__,mcBindKey.mcName);
|
|
|
|
if ((sendtype == stArchiveMountWait || sendtype == stArchive) && mcBindKey.archive_cg_exists) {
|
|
cgdest = mcBindKey.archive_copy_dest;
|
|
|
|
} else if ((sendtype == stBackupMountWait || sendtype == stBackup) && mcBindKey.backup_cg_exists) {
|
|
cgdest = mcBindKey.backup_copy_dest;
|
|
|
|
} else {
|
|
fprintf(stderr,"%s: UNKNOWN Type %d\n",__func__,sendtype);
|
|
return -1;
|
|
}
|
|
|
|
if (cgdest)
|
|
fprintf(stderr,"%s: Destination Copy Group: %s\n",__func__,cgdest);
|
|
}
|
|
|
|
memset(&objAttr,0x00,sizeof(ObjAttr));
|
|
objAttr.stVersion = ObjAttrVersion;
|
|
*objAttr.owner = '\0';
|
|
objAttr.sizeEstimate.hi = length >> 32;
|
|
objAttr.sizeEstimate.lo = length & ~0U;
|
|
objAttr.objCompressed = bFalse;
|
|
// @todo Could we use the objinfo field?
|
|
|
|
if (sendtype == stArchiveMountWait || sendtype == stArchive) {
|
|
memset(&archData,0x00,sizeof(sndArchiveData));
|
|
|
|
archData.stVersion = sndArchiveDataVersion;
|
|
archData.descr = description;
|
|
|
|
archDataP = &archData;
|
|
}
|
|
|
|
rc = dsmSendObj(dsmHandle,sendtype,archDataP,&objName,&objAttr,NULL);
|
|
if (rc != DSM_RC_OK) {
|
|
printf("%s: dsmSendObj() failed %s\n",__func__,tsm_printerr(dsmHandle,rc));
|
|
return(0);
|
|
}
|
|
|
|
#ifdef USE_DIGEST
|
|
if (digest) {
|
|
EVP_MD_CTX_init(&mdctx);
|
|
EVP_DigestInit_ex(&mdctx, md, NULL);
|
|
}
|
|
#endif
|
|
|
|
memset(&dataBlk,0x00,sizeof(DataBlk));
|
|
dataBlk.stVersion = DataBlkVersion;
|
|
|
|
while(1) {
|
|
nbytes = rw_full(STDIN_FILENO,buffer,BUFLEN);
|
|
|
|
if (nbytes < 0) {
|
|
perror("tsm_sendmail: read");
|
|
return 0;
|
|
|
|
} else if (nbytes == 0) {
|
|
break;
|
|
}
|
|
|
|
#ifdef USE_DIGEST
|
|
if (digest)
|
|
EVP_DigestUpdate(&mdctx, buffer, nbytes);
|
|
#endif
|
|
|
|
dataBlk.bufferLen = nbytes;
|
|
dataBlk.numBytes = 0;
|
|
dataBlk.bufferPtr = buffer;
|
|
|
|
rc = dsmSendData(dsmHandle,&dataBlk);
|
|
|
|
if (rc != DSM_RC_OK && rc != DSM_RC_COMPRESS_GREW) {
|
|
printf("%s: dsmSendData() failed %s\n",__func__,tsm_printerr(dsmHandle,rc));
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
memset(&dsmEndSendObjExIn,0x00,sizeof(dsmEndSendObjExIn_t));
|
|
memset(&dsmEndSendObjExOut,0x00,sizeof(dsmEndSendObjExOut_t));
|
|
|
|
dsmEndSendObjExIn.stVersion = dsmEndSendObjExInVersion;
|
|
dsmEndSendObjExIn.dsmHandle = dsmHandle;
|
|
dsmEndSendObjExOut.stVersion = dsmEndSendObjExOutVersion;
|
|
|
|
if ((rc = dsmEndSendObjEx(&dsmEndSendObjExIn,&dsmEndSendObjExOut)) != DSM_RC_OK) {
|
|
printf("%s: dsmEndSendObjEx() failed %s\n",__func__,tsm_printerr(dsmHandle,rc));
|
|
return 0;
|
|
}
|
|
|
|
rc = dsmEndTxn(dsmHandle,DSM_VOTE_COMMIT,&reason);
|
|
if (rc == DSM_RC_CHECK_REASON_CODE || (rc == DSM_RC_OK && reason != DSM_RC_OK)) {
|
|
printf("%s: dsmEndTxn() failed %s\n",__func__,tsm_printerr(dsmHandle,reason));
|
|
return 0;
|
|
|
|
} else if(rc != DSM_RC_OK) {
|
|
printf("%s: dsmEndTxn() failed %s\n",__func__,tsm_printerr(dsmHandle,rc));
|
|
return 0;
|
|
}
|
|
|
|
#ifdef USE_DIGEST
|
|
if (digest) {
|
|
EVP_DigestFinal_ex(&mdctx, md_value, &md_len);
|
|
EVP_MD_CTX_cleanup(&mdctx);
|
|
|
|
for(i=0; i<md_len; i++)
|
|
sprintf(digest_str+(i*2), "%02x", md_value[i]);
|
|
|
|
digest_str[strlen(digest_str)+1] = '\0';
|
|
|
|
printf("%s %s (%s)",digest_str,dsmObjnameToStr(objName),digest);
|
|
}
|
|
|
|
objAttr.objInfo=digest_str;
|
|
objAttr.objInfoLength = md_len*2;
|
|
#endif
|
|
|
|
// Update our object with its correct size
|
|
objAttr.sizeEstimate.hi = dsmEndSendObjExOut.totalBytesSent.hi;
|
|
objAttr.sizeEstimate.lo = dsmEndSendObjExOut.totalBytesSent.lo;
|
|
rc = dsmUpdateObj(dsmHandle,sendtype,archDataP,&objName,&objAttr,DSM_BACKUPD_OBJINFO | DSM_ARCHUPD_OBJINFO);
|
|
|
|
// Log start backup
|
|
if (startstop) {
|
|
tsm_datefs(dsmHandle,fsname,DSM_FSUPD_BACKCOMPLETEDATE);
|
|
tsm_updatefs(dsmHandle,fsname,dsmSizeToNum(dsmEndSendObjExOut.totalBytesSent,bFalse));
|
|
}
|
|
|
|
if (summary) {
|
|
if (! digest)
|
|
printf("%s",dsmObjnameToStr(objName));
|
|
|
|
printf(" [%5.3f/%5.3f/%5.3f/%5.3f] (%s/%s)",
|
|
dsmSizeToNum(dsmEndSendObjExOut.totalBytesSent,bTrue),
|
|
dsmEndSendObjExOut.objDeduplicated ? dsmSizeToNum(dsmEndSendObjExOut.totalDedupSize,bTrue) : 0.0,
|
|
dsmEndSendObjExOut.objCompressed ? dsmSizeToNum(dsmEndSendObjExOut.totalCompressSize,bTrue) : 0.0,
|
|
dsmSizeToNum(dsmEndSendObjExOut.totalLFBytesSent,bTrue) ? dsmSizeToNum(dsmEndSendObjExOut.totalLFBytesSent,bTrue) : 0.0,
|
|
dsmEndSendObjExOut.encryptionType ? ( DSM_ENCRYPT_CLIENTENCRKEY ? "CLIENTENCRKEY" : DSM_ENCRYPT_USER ? "USER" : "NO" ) : "-",
|
|
dsmEndSendObjExOut.encryptionType ? ( DSM_ENCRYPT_AES_128BIT ? "AES_128BIT" : DSM_ENCRYPT_DES_56BIT ? "DES_56BIT" : "NONE" ) : "-");
|
|
}
|
|
|
|
if (digest || summary)
|
|
printf("\n");
|
|
|
|
return dsmSizeToNum(dsmEndSendObjExOut.totalBytesSent,bFalse);
|
|
}
|
|
|
|
/* Get data from TSM for restore */
|
|
int tsm_restorefile(dsUint32_t dsmHandle, dsmQueryType qType, qryArchiveData qaData, qryBackupData qbData) {
|
|
extern int verbose;
|
|
dsInt16_t rc=0;
|
|
struct matchone_cb_data cbdata;
|
|
dsmGetList getList;
|
|
DataBlk dataBlk;
|
|
dsmGetType gType;
|
|
|
|
memset(&cbdata,0x00,sizeof(cbdata));
|
|
cbdata.numfound = 0;
|
|
|
|
if (qType == qtArchive) {
|
|
if (verbose)
|
|
fprintf(stderr,"%s: Starting to receive %s via stdin\n",__func__,dsmObjnameToStr(*qaData.objName));
|
|
|
|
gType = gtArchive;
|
|
rc = tsm_queryfile(dsmHandle,qType,tsm_matchone_cb,&cbdata,qaData,qbData);
|
|
|
|
} else if (qType == qtBackup) {
|
|
if (verbose)
|
|
fprintf(stderr,"%s: Starting to receive %s via stdin\n",__func__,dsmObjnameToStr(*qbData.objName));
|
|
|
|
gType = gtBackup;
|
|
rc = tsm_queryfile(dsmHandle,qType,tsm_matchone_cb,&cbdata,qaData,qbData);
|
|
|
|
} else {
|
|
fprintf(stderr,"%s: UNKNOWN Type %d\n",__func__,qType);
|
|
return -1;
|
|
}
|
|
|
|
if (rc != DSM_RC_OK)
|
|
return 0;
|
|
|
|
if (cbdata.numfound == 0) {
|
|
debugLog(0,__func__,"FAILED: The file specification did not match any file.",0);
|
|
return 0;
|
|
}
|
|
|
|
memset(&getList,0x00,sizeof(dsmGetList));
|
|
getList.stVersion = dsmGetListVersion;
|
|
getList.numObjId = 1;
|
|
getList.objId = &cbdata.objId;
|
|
getList.partialObjData = NULL;
|
|
|
|
rc = dsmBeginGetData(dsmHandle,bTrue,gType,&getList);
|
|
if (rc != DSM_RC_OK) {
|
|
printf("%s: dsmBeginGetData() failed %s\n",__func__,tsm_printerr(dsmHandle,rc));
|
|
return 0;
|
|
}
|
|
|
|
memset(&dataBlk,0x00,sizeof(DataBlk));
|
|
dataBlk.stVersion = DataBlkVersion;
|
|
dataBlk.bufferPtr = malloc(BUFLEN);
|
|
if (!dataBlk.bufferPtr) {
|
|
perror("tsm_restorefile: malloc");
|
|
return 0;
|
|
}
|
|
|
|
dataBlk.bufferLen = BUFLEN;
|
|
dataBlk.numBytes = 0;
|
|
|
|
rc = dsmGetObj(dsmHandle,&cbdata.objId,&dataBlk);
|
|
|
|
while (rc == DSM_RC_MORE_DATA) {
|
|
if (rw_full(STDOUT_FILENO,dataBlk.bufferPtr,dataBlk.numBytes) < 0) {
|
|
perror("tsm_restorefile: write");
|
|
return 0;
|
|
}
|
|
|
|
dataBlk.numBytes = 0;
|
|
rc = dsmGetData(dsmHandle,&dataBlk);
|
|
}
|
|
|
|
if (rc != DSM_RC_FINISHED) {
|
|
printf("%s: dsmGetObj()/dsmGetData() failed %s\n",__func__,tsm_printerr(dsmHandle,rc));
|
|
return 0;
|
|
}
|
|
|
|
if (rw_full(STDOUT_FILENO,dataBlk.bufferPtr,dataBlk.numBytes) < 0) {
|
|
perror("tsm_restorefile: write");
|
|
return 0;
|
|
}
|
|
|
|
rc = dsmEndGetObj(dsmHandle);
|
|
if (rc != DSM_RC_OK) {
|
|
printf("%s: dsmEndGetObj() failed %s\n",__func__,tsm_printerr(dsmHandle,rc));
|
|
return 0;
|
|
}
|
|
|
|
rc = dsmEndGetData(dsmHandle);
|
|
if (rc != DSM_RC_OK) {
|
|
printf("%s: dsmEndGetData() failed %s\n",__func__,tsm_printerr(dsmHandle,rc));
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Delete data in TSM */
|
|
int tsm_deletefile(dsUint32_t dsmHandle, dsmQueryType qType, qryArchiveData qaData, qryBackupData qbData) {
|
|
extern int verbose;
|
|
dsInt16_t rc=0;
|
|
dsUint16_t reason=0;
|
|
dsmDelInfo *dInfoP;
|
|
dsmDelType dType;
|
|
struct matchone_cb_data cbdata;
|
|
|
|
cbdata.numfound = 0;
|
|
|
|
rc = tsm_queryfile(dsmHandle,qType,tsm_matchone_cb,&cbdata,qaData,qbData);
|
|
if (rc != DSM_RC_OK) {
|
|
return 0;
|
|
}
|
|
|
|
if (cbdata.numfound == 0) {
|
|
debugLog(0,__func__,"FAILED: The file specification did not match any file.",0);
|
|
return 0;
|
|
}
|
|
|
|
if (qType == qtArchive) {
|
|
if (verbose)
|
|
fprintf(stderr,"%s: Deleting archive file %s\n",__func__,dsmObjnameToStr(*qaData.objName));
|
|
|
|
dType = dtArchive;
|
|
|
|
delArch daInfo;
|
|
daInfo.stVersion = delArchVersion;
|
|
daInfo.objId = cbdata.objId;
|
|
|
|
dInfoP = (dsmDelInfo *) &daInfo;
|
|
|
|
} else if (qType == qtBackup) {
|
|
if (verbose)
|
|
fprintf(stderr,"%s: Deleting backup file %s\n",__func__,dsmObjnameToStr(*qbData.objName));
|
|
|
|
dType = dtBackup;
|
|
|
|
delBack dbInfo;
|
|
dbInfo.stVersion = delBackVersion;
|
|
dbInfo.objNameP = qbData.objName;
|
|
dbInfo.copyGroup = cbdata.copyGroup;
|
|
|
|
dInfoP = (dsmDelInfo *) &dbInfo;
|
|
|
|
} else {
|
|
fprintf(stderr,"%s: UNKNOWN Type %d\n",__func__,qType);
|
|
return -1;
|
|
}
|
|
|
|
rc = dsmBeginTxn(dsmHandle);
|
|
if (rc != DSM_RC_OK) {
|
|
printf("%s: dsmBeginTxn() failed %s\n",__func__,tsm_printerr(dsmHandle,rc));
|
|
return 0;
|
|
}
|
|
|
|
rc = dsmDeleteObj(dsmHandle,dType,*dInfoP);
|
|
if (rc != DSM_RC_OK) {
|
|
printf("%s: dsmDeleteObj() failed %s\n",__func__,tsm_printerr(dsmHandle,rc));
|
|
return 0;
|
|
}
|
|
|
|
rc = dsmEndTxn(dsmHandle,DSM_VOTE_COMMIT,&reason);
|
|
if (rc == DSM_RC_CHECK_REASON_CODE || (rc == DSM_RC_OK && reason != DSM_RC_OK)) {
|
|
printf("%s: dsmEndTxn() failed %s\n",__func__,tsm_printerr(dsmHandle,reason));
|
|
return 0;
|
|
|
|
} else if (rc != DSM_RC_OK) {
|
|
printf("%s: dsmEndTxn() failed %s\n",__func__,tsm_printerr(dsmHandle,rc));
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Update the FS capacity and utilsation */
|
|
int tsm_updatefs(dsUint32_t dsmHandle, char *fsname, long long size) {
|
|
dsInt16_t rc=0;
|
|
dsmFSUpd fsUpdateP;
|
|
|
|
memset(&fsUpdateP,0x00,sizeof(dsmFSUpd));
|
|
fsUpdateP.occupancy.hi = size >> 32;
|
|
fsUpdateP.occupancy.lo = size & ~0U;
|
|
fsUpdateP.capacity = fsUpdateP.occupancy;
|
|
|
|
rc = dsmUpdateFS(dsmHandle,fsname,&fsUpdateP,DSM_FSUPD_OCCUPANCY | DSM_FSUPD_CAPACITY);
|
|
|
|
if (rc != DSM_RC_OK) {
|
|
printf("%s: dsmUpdateFS() failed %s\n",__func__,tsm_printerr(dsmHandle,rc));
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int tsm_datefs(dsUint32_t dsmHandle, char *fsname, int startstop) {
|
|
dsInt16_t rc=0;
|
|
dsmFSUpd fsUpdateP;
|
|
|
|
memset(&fsUpdateP,0x00,sizeof(dsmFSUpd));
|
|
rc = dsmUpdateFS(dsmHandle,fsname,&fsUpdateP,startstop);
|
|
|
|
if (rc != DSM_RC_OK) {
|
|
printf("%s: dsmUpdateFS() failed %s\n",__func__,tsm_printerr(dsmHandle,rc));
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|