From 3ddab891e1a02259ae3c99dc0f72d2ca3fc4f69c Mon Sep 17 00:00:00 2001 From: Max Satula Date: Fri, 1 Jan 2016 17:10:14 -0500 Subject: [PATCH] Add ability to calculate MD5 or SHA1 digests in --ls Relates to #4 --- ChangeLog | 4 +++ NEWS | 1 + src/install.c | 34 +++++++++++++++++---- src/ls.c | 82 +++++++++++++++++++++++++++++++++++++++++---------- src/main.c | 18 ++++++++++- src/ocp.h | 4 ++- 6 files changed, 119 insertions(+), 24 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4e21c54..ca6ff61 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2016-01-01 Max Satula + +* Add ability to calculate MD5 or SHA1 digests in --ls + 2015-12-31 Max Satula * Improve and optimize wildcard filtering for --ls diff --git a/NEWS b/NEWS index 940c41e..7ee3a8c 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,7 @@ ocp * added --sysdba and --sysoper options to connect to Oracle accordingly +* added --md5 and --sha1 options to calculate file hashes ---------------------------------------------------------------------------- diff --git a/src/install.c b/src/install.c index bb24d8e..3e219ed 100644 --- a/src/install.c +++ b/src/install.c @@ -35,7 +35,8 @@ CREATE OR REPLACE\n\ TYPE t_ocp_file AS OBJECT (\n\ file_name VARCHAR2(200),\n\ bytes NUMBER,\n\ - last_modified DATE);" }, + last_modified DATE,\n\ + digest RAW(20));" }, { "\ CREATE OR REPLACE\n\ TYPE t_ocp_file_list IS TABLE OF t_ocp_file" }, @@ -46,6 +47,11 @@ CREATE OR REPLACE\n\ AS\n\ import java.io.File;\n\ import java.io.FilenameFilter;\n\ +import java.io.FileInputStream;\n\ +import java.io.FileNotFoundException;\n\ +import java.io.IOException;\n\ +import java.security.MessageDigest;\n\ +import java.security.NoSuchAlgorithmException;\n\ import java.sql.Connection;\n\ import java.sql.SQLException;\n\ import java.sql.Timestamp;\n\ @@ -276,8 +282,8 @@ public class Globs {\n\ \n\ public class j_ocp_DirList\n\ {\n\ - public static ARRAY getList(String directory, String pattern)\n\ - throws SQLException\n\ + public static ARRAY getList(String directory, String pattern, String hashAlgorithm)\n\ + throws SQLException, NoSuchAlgorithmException\n\ {\n\ Connection conn = new OracleDriver().defaultConnection();\n\ ArrayDescriptor arrayDescriptor = new ArrayDescriptor(\"T_OCP_FILE_LIST\", conn);\n\ @@ -299,7 +305,7 @@ public class j_ocp_DirList\n\ } else {\n\ files = path.listFiles();\n\ }\n\ - Object[][] result = new Object[files.length][3];\n\ + Object[][] result = new Object[files.length][4];\n\ for (int i = 0; i < files.length; i++)\n\ {\n\ result[i][0] = files[i].getName();\n\ @@ -307,6 +313,22 @@ public class j_ocp_DirList\n\ {\n\ result[i][1] = new Long(files[i].length());\n\ result[i][2] = new Timestamp(files[i].lastModified());\n\ + if (hashAlgorithm != null) {\n\ + try {\n\ + FileInputStream inputStream = new FileInputStream(files[i]);\n\ + MessageDigest digest = MessageDigest.getInstance(hashAlgorithm);\n\ + byte[] bytesBuffer = new byte[65536];\n\ + int bytesRead = -1;\n\ +\n\ + while ((bytesRead = inputStream.read(bytesBuffer)) != -1) {\n\ + digest.update(bytesBuffer, 0, bytesRead);\n\ + }\n\ +\n\ + result[i][3] = digest.digest();\n\ + }\n\ + catch (FileNotFoundException e) {}\n\ + catch (IOException e) {}\n\ + }\n\ }\n\ catch ( java.security.AccessControlException e ) {}\n\ }\n\ @@ -315,10 +337,10 @@ public class j_ocp_DirList\n\ }" }, { "\ CREATE OR REPLACE\n\ - FUNCTION f_ocp_dir_list(p_directory IN VARCHAR2, p_pattern IN VARCHAR2)\n\ + FUNCTION f_ocp_dir_list(p_directory IN VARCHAR2, p_pattern IN VARCHAR2, p_hashalgorithm IN VARCHAR2)\n\ RETURN t_ocp_file_list\n\ AS LANGUAGE JAVA\n\ -NAME 'j_ocp_DirList.getList( java.lang.String, java.lang.String ) return oracle.sql.ARRAY';" }, +NAME 'j_ocp_DirList.getList( java.lang.String, java.lang.String, java.lang.String ) return oracle.sql.ARRAY';" }, { 0 } }; diff --git a/src/ls.c b/src/ls.c index 006e66b..d5170c2 100644 --- a/src/ls.c +++ b/src/ls.c @@ -22,24 +22,29 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #endif #include +#include #include #include "oracle.h" #include "ocp.h" -void Ls(struct ORACLEALLINONE *oraAllInOne, char* pDirectory, const char* patterns, int patternLength) +void Ls(struct ORACLEALLINONE *oraAllInOne, char* pDirectory, const char* patterns, int patternLength, enum HASH_ALGORITHM hashAlgorithm) { sword ociResult; char vFileName[MAX_FMT_SIZE]; + char vHashAlgorithm[8]; ub8 vBytes; char vLastModified[7]; - int i; + unsigned char vDigest[20]; + int hashLength; + int i, j; long long totalBytes; int foundKnownSize, foundUnknownSize; struct BINDVARIABLE oraBindsLs[] = { - { 0, SQLT_CHR, ":patterns", patterns, patternLength }, - { 0, SQLT_STR, ":directory", pDirectory, ORA_IDENTIFIER_SIZE + 1 }, + { 0, SQLT_CHR, ":patterns", patterns, patternLength }, + { 0, SQLT_STR, ":hash_algorithm", vHashAlgorithm, sizeof(vHashAlgorithm) }, + { 0, SQLT_STR, ":directory", pDirectory, ORA_IDENTIFIER_SIZE + 1 }, { 0 } }; @@ -48,6 +53,7 @@ void Ls(struct ORACLEALLINONE *oraAllInOne, char* pDirectory, const char* patter { 0, SQLT_STR, vFileName, sizeof(vFileName)-1, 0 }, { 0, SQLT_INT, &vBytes, sizeof(vBytes), 0 }, { 0, SQLT_DAT, vLastModified, sizeof(vLastModified), 0 }, + { 0, SQLT_BIN, vDigest, sizeof(vDigest), 0 }, { 0 } }; @@ -55,19 +61,47 @@ void Ls(struct ORACLEALLINONE *oraAllInOne, char* pDirectory, const char* patter "\ SELECT t.file_name,\ t.bytes,\ - t.last_modified\ + t.last_modified,\ + t.digest\ FROM all_directories d,\ - TABLE(f_ocp_dir_list(d.directory_path, :patterns)) t\ + TABLE(f_ocp_dir_list(d.directory_path, :patterns, :hash_algorithm)) t\ WHERE d.directory_name = :directory", 0, oraBindsLs, oraDefinesLs }; + switch (hashAlgorithm) + { + case HASH_MD5: + strcpy(vHashAlgorithm, "MD5"); + hashLength = 16; + break; + case HASH_SHA1: + strcpy(vHashAlgorithm, "SHA1"); + hashLength = 20; + break; + case HASH_NONE: + vHashAlgorithm[0] = '\0'; + break; + } SetSessionAction(oraAllInOne, "LS"); PrepareStmtAndBind(oraAllInOne, &oraStmtLs); - printf("Contents of %s directory\n\ + if (hashAlgorithm != HASH_NONE) + { + printf("Contents of %s directory\n\ +%-40s %s hash\n\ +---------------------------------------- ", + pDirectory, "File Name", vHashAlgorithm); + for (j = 0; j < hashLength*2; j++) + printf("-"); + printf("\n"); + } + else + { + printf("Contents of %s directory\n\ %-40s %-12s %s\n\ ---------------------------------------- ------------ -------------------\n", - pDirectory, "File Name", " Size", "Last Modified"); + pDirectory, "File Name", " Size", "Last Modified"); + } i = 0; totalBytes = 0; @@ -75,7 +109,20 @@ SELECT t.file_name,\ ociResult = ExecuteStmt(oraAllInOne); while (ociResult == OCI_SUCCESS) { - if (oraStmtLs.oraDefines[1].indp != -1 && + if (hashAlgorithm != HASH_NONE) + { + printf("%-40s ", + vFileName); + if (oraStmtLs.oraDefines[3].indp != -1) + { + for (j = 0; j < hashLength; j++) + { + printf("%02x", vDigest[j]); + } + } + printf("\n"); + } + else if (oraStmtLs.oraDefines[1].indp != -1 && oraStmtLs.oraDefines[2].indp != -1) { printf("%-40s %12lld %02d/%02d/%d %02d:%02d:%02d\n", @@ -104,13 +151,16 @@ SELECT t.file_name,\ if (ociResult != OCI_NO_DATA) ExitWithError(oraAllInOne, 4, ERROR_OCI, "Failed to list files in oracle directory\n"); - if (i) - printf("---------------------------------------- ------------ -------------------\n"); - printf("%5d File(s)", i); - if (!foundKnownSize && foundUnknownSize) - printf("\n"); - else - printf(" %39lld%s\n", totalBytes, foundUnknownSize ? "+" : ""); + if (hashAlgorithm == HASH_NONE) + { + if (i) + printf("---------------------------------------- ------------ -------------------\n"); + printf("%5d File(s)", i); + if (!foundKnownSize && foundUnknownSize) + printf("\n"); + else + printf(" %39lld%s\n", totalBytes, foundUnknownSize ? "+" : ""); + } ReleaseStmt(oraAllInOne); SetSessionAction(oraAllInOne, 0); diff --git a/src/main.c b/src/main.c index 5ae728d..7404270 100644 --- a/src/main.c +++ b/src/main.c @@ -52,6 +52,7 @@ struct PROGRAM_OPTIONS enum TRANSFER_MODE transferMode; const char* connectionString; ub4 adminMode; + enum HASH_ALGORITHM hashAlgorithm; int isStdUsed; int numberOfOracleSessions; @@ -165,6 +166,13 @@ int main(int argc, const char *argv[]) POPT_TABLEEND }; + struct poptOption lsOptions[] = + { + { "md5", '\0', POPT_ARG_VAL, &programOptions.hashAlgorithm, HASH_MD5, "Calculate MD5 on listed files" }, + { "sha1", '\0', POPT_ARG_VAL, &programOptions.hashAlgorithm, HASH_SHA1, "Calculate SHA1 on listed files" }, + POPT_TABLEEND + }; + struct poptOption objOptions[] = { { "install", '\0', POPT_ARG_NONE, 0, ACTION_INSTALL, "Install objects" }, @@ -181,6 +189,7 @@ int main(int argc, const char *argv[]) { "sysoper", '\0', POPT_ARG_VAL, &programOptions.adminMode, OCI_SYSOPER, "Connect as SYSOPER" }, { NULL, '\0', POPT_ARG_INCLUDE_TABLE, transferModeOptions, 0, "Transfer options:" }, { NULL, '\0', POPT_ARG_INCLUDE_TABLE, compressionOptions, 0, "Compression options:" }, + { NULL, '\0', POPT_ARG_INCLUDE_TABLE, lsOptions, 0, "File list options:" }, { NULL, '\0', POPT_ARG_INCLUDE_TABLE, objOptions, 0, "Database objects for --ls support:" }, POPT_AUTOHELP POPT_TABLEEND @@ -197,6 +206,7 @@ int main(int argc, const char *argv[]) programOptions.isStdUsed = 0; programOptions.numberOfOracleSessions = 1; programOptions.adminMode = OCI_DEFAULT; + programOptions.hashAlgorithm = 0; poptcon = poptGetContext(NULL, argc, argv, options, 0); while ((rc = poptGetNextOpt(poptcon)) >= 0) @@ -257,6 +267,12 @@ int main(int argc, const char *argv[]) ExitWithUsage(&poptcon); } + if (programOptions.hashAlgorithm && programOptions.programAction != ACTION_LS) + { + fprintf(stderr, "Hashing option can only be specified for list mode\n"); + ExitWithUsage(&poptcon); + } + programOptions.connectionString = poptGetArg(poptcon); if (!programOptions.connectionString) { @@ -480,7 +496,7 @@ int main(int argc, const char *argv[]) break; case ACTION_LS: TryDirectory(&oraAllInOne, vDirectory); - Ls(&oraAllInOne, vDirectory, filePatterns, filePatternPtr - filePatterns); + Ls(&oraAllInOne, vDirectory, filePatterns, filePatternPtr - filePatterns, programOptions.hashAlgorithm); break; case ACTION_RM: TryDirectory(&oraAllInOne, vDirectory); diff --git a/src/ocp.h b/src/ocp.h index cfc245a..01bfb50 100644 --- a/src/ocp.h +++ b/src/ocp.h @@ -29,6 +29,8 @@ struct ORACLEFILEATTR ub8 length; }; +enum HASH_ALGORITHM { HASH_NONE, HASH_MD5, HASH_SHA1 }; + void GetOracleFileAttr(struct ORACLEALLINONE *oraAllInOne, char* pDirectory, char* pFileName, struct ORACLEFILEATTR *oraFileAttr); void TryDirectory(struct ORACLEALLINONE *oraAllInOne, char* pDirectory); @@ -53,7 +55,7 @@ void UploadFileWithCompression(struct ORACLEALLINONE *oraAllInOne, char* pDirect int isKeepPartial, int isResume); void LsDir(struct ORACLEALLINONE *oraAllInOne); -void Ls(struct ORACLEALLINONE *oraAllInOne, char* pDirectory, const char* patterns, int patternLength); +void Ls(struct ORACLEALLINONE *oraAllInOne, char* pDirectory, const char* patterns, int patternLength, enum HASH_ALGORITHM hashAlgorithm); void Rm(struct ORACLEALLINONE *oraAllInOne, char* pDirectory, char* pFileName); void InstallObjects(struct ORACLEALLINONE* oraAllInOne);