/* * The recommended buffer size is n*TCPBUFFLEN - 4 bytes. * To get your buffer size, do: dsmc query options|grep TCPBUF * 32kB seems to be the new default, 31kB was the old. * * An additional factor is the pipe buffer size. Since we don't do threading * (yet), we hide a little bit of latency if we don't read more than the pipe * buffer can hold at a time. On Linux 2.6 this is 64kB. * * So, I would recommend a BUFLEN of 64kB if your TCPBUFLEN is above the * 64kB + 4 bytes limit or if your TCPBUFLEN is lower, the * n*TCPBUFFLEN - 4 bytes that gets you closest to 64kB. * * For a default tuned TSM client on Linux, BUFLEN should thus be 64*1024-4. */ #define BUFLEN (64*1024-4) #include #include #include #include #include #include "dsmrc.h" #include "dsmapitd.h" #include "dsmapifp.h" #include "../tsmpipe.h" extern dsBool_t compressEnabled; extern void tsm_printerr(dsUint32_t dsmHandle, dsInt16_t rc, char *str); extern char *dsmObjnameToStr(dsmObjName objName); extern double dsmSizeToNum(dsStruct64_t dsStruct64); extern dsmObjName dsmNameToObjname(char *fsname, char *filename, int verbose); extern dsInt16_t tsm_queryfile(dsUint32_t dsmHandle, dsmObjName *objName, char *description, dsmSendType sendtype, char verbose, tsm_query_callback usercb, void * userdata, char *pitdate, int querytype); 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,"tsm_matchone_cb: Internal error: Unknown qType %d\n",qType); return -1; } if (cbdata->numfound > 1) { fprintf(stderr,"tsmpipe: FAILED: The file specification matched multiple files.\n"); 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; while (count) { ssize_t len; if (fd == STDIN_FILENO) { len = read(fd,buf+done,count); } else if (fd == STDOUT_FILENO) { len = write(fd,buf+done,count); } else { fprintf(stderr,"rw_full: rw_full: Unknown FD\n"); exit(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) { tsm_printerr(dsmHandle,rc,"tsm_regfs: dsmRegisterFS failed"); return 0; } return 1; } /* Send data to TSM for storage */ int tsm_sendfile(dsUint32_t dsmHandle, char *fsname, char *filename, long long length, char *description, dsmSendType sendtype, char 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); } // 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,"tsm_sendfile: WARNING: Size (%i) too small for compression/de-duplication, even though it is enabled",(int)length); // Start a Transaction rc = dsmBeginTxn(dsmHandle); if (rc != DSM_RC_OK) { tsm_printerr(dsmHandle,rc,"tsm_sendfile: dsmBeginTxn failed"); return 0; } memset(&objName,0x00,sizeof(objName)); objName = dsmNameToObjname(fsname,filename,verbose); if (verbose) fprintf(stderr,"tsm_sendfile: Starting to send stdin as %s\n",dsmObjnameToStr(objName)); memset(&mcBindKey,0x00,sizeof(mcBindKey)); mcBindKey.stVersion = mcBindKeyVersion; rc = dsmBindMC(dsmHandle,&objName,sendtype,&mcBindKey); if (rc != DSM_RC_OK) { tsm_printerr(dsmHandle,rc,"tsm_sendfile: dsmBindMC failed"); return 0; } if (verbose > 1) { char *cgdest=NULL; fprintf(stderr,"tsm_sendfile: Bound to Management Class: %s\n",mcBindKey.mcName); if ((sendtype == stArchiveMountWait || sendtype == stArchive) && mcBindKey.archive_cg_exists) { cgdest = mcBindKey.archive_copy_dest; } else if(mcBindKey.backup_cg_exists) { cgdest = mcBindKey.backup_copy_dest; } if (cgdest) fprintf(stderr,"tsm_sendfile: Destination Copy Group: %s\n",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) { tsm_printerr(dsmHandle,rc,"tsm_sendfile: dsmSendObj failed"); return(0); } memset(&dataBlk,0x00,sizeof(DataBlk)); dataBlk.stVersion = DataBlkVersion; while(1) { nbytes = rw_full(STDIN_FILENO,buffer,BUFLEN); if (nbytes < 0) { perror("tsmpipe: read"); return 0; } else if (nbytes == 0) { break; } dataBlk.bufferLen = nbytes; dataBlk.numBytes = 0; dataBlk.bufferPtr = buffer; rc = dsmSendData(dsmHandle,&dataBlk); if (rc != DSM_RC_OK && rc != DSM_RC_COMPRESS_GREW) { tsm_printerr(dsmHandle,rc,"tsm_senfile: dsmSendData failed"); 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) { tsm_printerr(dsmHandle,rc,"tsm_sendfile: dsmEndSendObjEx failed"); return 0; } rc = dsmEndTxn(dsmHandle,DSM_VOTE_COMMIT,&reason); if (rc == DSM_RC_CHECK_REASON_CODE || (rc == DSM_RC_OK && reason != DSM_RC_OK)) { tsm_printerr(dsmHandle,reason,"tsm_sendfile: dsmEndTxn failed, reason"); return 0; } else if(rc != DSM_RC_OK) { tsm_printerr(dsmHandle,rc,"tsm_sendfile: dsmEndTxn failed"); return 0; } if (verbose) { printf("Total bytes sent: %5.3f\n",dsmSizeToNum(dsmEndSendObjExOut.totalBytesSent)); if (dsmEndSendObjExOut.objDeduplicated) printf("Deduplicated size: %5.3f\n",dsmSizeToNum(dsmEndSendObjExOut.totalDedupSize)); if (dsmEndSendObjExOut.objCompressed) printf("Compressed size: %5.3f\n",dsmSizeToNum(dsmEndSendObjExOut.totalCompressSize)); if (dsmSizeToNum(dsmEndSendObjExOut.totalLFBytesSent)) printf("LAN-free bytes sentsize: %5.3f\n",dsmSizeToNum(dsmEndSendObjExOut.totalLFBytesSent)); if (dsmEndSendObjExOut.encryptionType) { printf("Encryption: %s\n",dsmEndSendObjExOut.encryptionType & DSM_ENCRYPT_CLIENTENCRKEY ? "CLIENTENCRKEY" : dsmEndSendObjExOut.encryptionType & DSM_ENCRYPT_USER ? "USER" : "NO"); printf("Encryption Strength: %s\n",dsmEndSendObjExOut.encryptionType & DSM_ENCRYPT_AES_128BIT ? "AES_128BIT" : dsmEndSendObjExOut.encryptionType & DSM_ENCRYPT_DES_56BIT ? "DES_56BIT" : "NONE"); } } return 1; } /* Get data from TSM for restore */ int tsm_restorefile(dsUint32_t dsmHandle, char *fsname, char *filename, char *description, dsmSendType sendtype, char verbose, char *pitdate) { dsInt16_t rc=0; struct matchone_cb_data cbdata; dsmObjName objName; dsmGetList getList; dsmGetType getType; DataBlk dataBlk; objName = dsmNameToObjname(fsname,filename,verbose); if (verbose) fprintf(stderr,"tsm_restorefile: Starting to receive %s via stdin\n",dsmObjnameToStr(objName)); memset(&cbdata,0x00,sizeof(cbdata)); cbdata.numfound = 0; // Retrieve a list based on the PITDATE or if no PITDATE, the last ACTIVE if (sendtype == stBackupMountWait) rc = tsm_queryfile(dsmHandle,&objName,description,sendtype,verbose,tsm_matchone_cb,&cbdata,pitdate,pitdate ? DSM_ANY_MATCH : DSM_ACTIVE); else rc = tsm_queryfile(dsmHandle,&objName,description,sendtype,verbose,tsm_matchone_cb,&cbdata,NULL,0); if (rc != DSM_RC_OK) return 0; if (cbdata.numfound == 0) { fprintf(stderr,"tsm_restorefile: FAILED: The file specification did not match any file.\n"); return 0; } memset(&getList,0x00,sizeof(dsmGetList)); getList.stVersion = dsmGetListVersion; getList.numObjId = 1; getList.objId = &cbdata.objId; getList.partialObjData = NULL; if (sendtype == stArchiveMountWait || sendtype == stArchive) { getType = gtArchive; } else { getType = gtBackup; } rc = dsmBeginGetData(dsmHandle,bTrue,getType,&getList); if (rc != DSM_RC_OK) { tsm_printerr(dsmHandle,rc,"tsm_restorefile: dsmBeginGetData failed"); 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) { tsm_printerr(dsmHandle,rc,"tsm_restorefile: dsmGetObj/dsmGetData failed"); 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) { tsm_printerr(dsmHandle,rc,"tsm_restorefile: dsmEndGetObj failed"); return 0; } rc = dsmEndGetData(dsmHandle); if (rc != DSM_RC_OK) { tsm_printerr(dsmHandle,rc,"tsm_restorefile: dsmEndGetData failed"); return 0; } return 1; } /* Delete data in TSM */ int tsm_deletefile(dsUint32_t dsmHandle, char *fsname, char *filename, char *description, dsmSendType sendtype, char verbose) { dsInt16_t rc=0; dsUint16_t reason=0; dsmDelInfo *dInfoP; dsmDelType dType; struct matchone_cb_data cbdata; delArch daInfo; delBack dbInfo; dsmObjName objName; objName = dsmNameToObjname(fsname,filename,verbose); if (verbose) fprintf(stderr,"tsm_deletefile: Deleting file %s\n",dsmObjnameToStr(objName)); cbdata.numfound = 0; rc = tsm_queryfile(dsmHandle,&objName,description,sendtype,verbose,tsm_matchone_cb,&cbdata,NULL,DSM_ACTIVE); if (rc != DSM_RC_OK) { return 0; } if (cbdata.numfound == 0) { fprintf(stderr,"tsm_deletefile: FAILED: The file specification did not match any file.\n"); return 0; } if (sendtype == stArchiveMountWait || sendtype == stArchive) { dType = dtArchive; daInfo.stVersion = delArchVersion; daInfo.objId = cbdata.objId; dInfoP = (dsmDelInfo *) &daInfo; } else { dType = dtBackup; dbInfo.stVersion = delBackVersion; dbInfo.objNameP = &objName; dbInfo.copyGroup = cbdata.copyGroup; dInfoP = (dsmDelInfo *) &dbInfo; } rc = dsmBeginTxn(dsmHandle); if (rc != DSM_RC_OK) { tsm_printerr(dsmHandle,rc,"tsm_deletefile: dsmBeginTxn failed"); return 0; } rc = dsmDeleteObj(dsmHandle,dType,*dInfoP); if (rc != DSM_RC_OK) { tsm_printerr(dsmHandle,rc,"tsm_deletefile: dsmDeleteObj failed"); return 0; } rc = dsmEndTxn(dsmHandle,DSM_VOTE_COMMIT,&reason); if (rc == DSM_RC_CHECK_REASON_CODE || (rc == DSM_RC_OK && reason != DSM_RC_OK)) { tsm_printerr(dsmHandle,reason,"tsm_deletefile: dsmEndTxn failed, reason"); return 0; } else if (rc != DSM_RC_OK) { tsm_printerr(dsmHandle,rc,"tsm_deletefile: dsmEndTxn failed"); return 0; } return 1; }