/* * 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" 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,"tsm_matchone_cb: 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) { 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); } // 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); 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 ((sendtype == stBackupMountWait || sendtype == stBackup) && mcBindKey.backup_cg_exists) { cgdest = mcBindKey.backup_copy_dest; } else { fprintf(stderr,"tsm_sendfile: Internal error: Unknown qType %d\n",sendtype); return -1; } 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("tsm_sendmail: 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_sendfile: 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, 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,"tsm_restorefile: Starting to receive %s via stdin\n",dsmObjnameToStr(*qaData.objName)); gType = gtArchive; rc = tsm_queryfile(dsmHandle,qType,tsm_matchone_cb,&cbdata,qaData,qbData); } else if (qType == qtBackup) { if (verbose) fprintf(stderr,"tsm_restorefile: Starting to receive %s via stdin\n",dsmObjnameToStr(*qbData.objName)); gType = gtBackup; rc = tsm_queryfile(dsmHandle,qType,tsm_matchone_cb,&cbdata,qaData,qbData); } else { fprintf(stderr,"tsm_restorefile: Internal error: Unknown qType %d\n",qType); return -1; } 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; rc = dsmBeginGetData(dsmHandle,bTrue,gType,&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, 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) { fprintf(stderr,"tsm_deletefile: FAILED: The file specification did not match any file.\n"); return 0; } if (qType == qtArchive) { if (verbose) fprintf(stderr,"tsm_deletefile: Deleting archive file %s\n",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,"tsm_deletefile: Deleting backup file %s\n",dsmObjnameToStr(*qbData.objName)); dType = dtBackup; delBack dbInfo; dbInfo.stVersion = delBackVersion; dbInfo.objNameP = qbData.objName; dbInfo.copyGroup = cbdata.copyGroup; dInfoP = (dsmDelInfo *) &dbInfo; } else { fprintf(stderr,"tsm_matchone_cb: Internal error: Unknown qType %d\n",qType); return -1; } 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; }