From c801ea2744dcc1aacfc544b874440f6413e94568 Mon Sep 17 00:00:00 2001 From: Dave Townsend Date: Fri, 31 Oct 2008 20:10:46 +0000 Subject: [PATCH] Bug 446464: File permissions are not preserved when zipping with zipwriter. r=biesi --- modules/libjar/zipwriter/src/nsZipHeader.h | 15 ++- modules/libjar/zipwriter/src/nsZipWriter.cpp | 53 +++++++-- modules/libjar/zipwriter/src/nsZipWriter.h | 6 +- .../zipwriter/test/unit/test_bug446708.js | 17 +-- .../test/unit/test_zippermissions.js | 106 ++++++++++++++++++ 5 files changed, 165 insertions(+), 32 deletions(-) create mode 100644 modules/libjar/zipwriter/test/unit/test_zippermissions.js diff --git a/modules/libjar/zipwriter/src/nsZipHeader.h b/modules/libjar/zipwriter/src/nsZipHeader.h index 78b5c34cb6e..b0714645043 100644 --- a/modules/libjar/zipwriter/src/nsZipHeader.h +++ b/modules/libjar/zipwriter/src/nsZipHeader.h @@ -45,8 +45,15 @@ #include "nsIZipReader.h" #include "nsAutoPtr.h" -#define ZIP_ATTRS_FILE 0 -#define ZIP_ATTRS_DIRECTORY 16 +// High word is S_IFREG, low word is DOS file attribute +#define ZIP_ATTRS_FILE 0x80000000 +// High word is S_IFDIR, low word is DOS dir attribute +#define ZIP_ATTRS_DIRECTORY 0x40000010 +#define PERMISSIONS_FILE 0644 +#define PERMISSIONS_DIR 0755 + +// Combine file type attributes with unix style permissions +#define ZIP_ATTRS(p, a) ((p & 0xfff) << 16) | a class nsZipHeader : public nsIZipEntry { @@ -61,8 +68,8 @@ public: mEAttr(0), mOffset(0), mFieldLength(0), - mVersionMade(20), - mVersionNeeded(20), + mVersionMade(0x0300 + 23), // Generated on Unix by v2.3 (matches infozip) + mVersionNeeded(20), // Requires v2.0 to extract mFlags(0), mMethod(0), mTime(0), diff --git a/modules/libjar/zipwriter/src/nsZipWriter.cpp b/modules/libjar/zipwriter/src/nsZipWriter.cpp index 64604d34a72..aec22fa8790 100644 --- a/modules/libjar/zipwriter/src/nsZipWriter.cpp +++ b/modules/libjar/zipwriter/src/nsZipWriter.cpp @@ -357,6 +357,7 @@ NS_IMETHODIMP nsZipWriter::AddEntryDirectory(const nsACString & aZipEntry, item.mOperation = OPERATION_ADD; item.mZipEntry = aZipEntry; item.mModTime = aModTime; + item.mPermissions = PERMISSIONS_DIR; if (!mQueue.AppendElement(item)) return NS_ERROR_OUT_OF_MEMORY; return NS_OK; @@ -364,7 +365,7 @@ NS_IMETHODIMP nsZipWriter::AddEntryDirectory(const nsACString & aZipEntry, if (mInQueue) return NS_ERROR_IN_PROGRESS; - return InternalAddEntryDirectory(aZipEntry, aModTime); + return InternalAddEntryDirectory(aZipEntry, aModTime, PERMISSIONS_DIR); } /* void addEntryFile (in AUTF8String aZipEntry, in PRInt32 aCompression, @@ -408,8 +409,12 @@ NS_IMETHODIMP nsZipWriter::AddEntryFile(const nsACString & aZipEntry, NS_ENSURE_SUCCESS(rv, rv); modtime *= PR_USEC_PER_MSEC; + PRUint32 permissions; + rv = aFile->GetPermissions(&permissions); + NS_ENSURE_SUCCESS(rv, rv); + if (isdir) - return InternalAddEntryDirectory(aZipEntry, modtime); + return InternalAddEntryDirectory(aZipEntry, modtime, permissions); if (mEntryHash.Get(aZipEntry, nsnull)) return NS_ERROR_FILE_ALREADY_EXISTS; @@ -420,7 +425,7 @@ NS_IMETHODIMP nsZipWriter::AddEntryFile(const nsACString & aZipEntry, NS_ENSURE_SUCCESS(rv, rv); rv = AddEntryStream(aZipEntry, modtime, aCompression, inputStream, - PR_FALSE); + PR_FALSE, permissions); NS_ENSURE_SUCCESS(rv, rv); return inputStream->Close(); @@ -445,6 +450,7 @@ NS_IMETHODIMP nsZipWriter::AddEntryChannel(const nsACString & aZipEntry, item.mZipEntry = aZipEntry; item.mModTime = aModTime; item.mCompression = aCompression; + item.mPermissions = PERMISSIONS_FILE; item.mChannel = aChannel; if (!mQueue.AppendElement(item)) return NS_ERROR_OUT_OF_MEMORY; @@ -461,7 +467,7 @@ NS_IMETHODIMP nsZipWriter::AddEntryChannel(const nsACString & aZipEntry, NS_ENSURE_SUCCESS(rv, rv); rv = AddEntryStream(aZipEntry, aModTime, aCompression, inputStream, - PR_FALSE); + PR_FALSE, PERMISSIONS_FILE); NS_ENSURE_SUCCESS(rv, rv); return inputStream->Close(); @@ -475,6 +481,20 @@ NS_IMETHODIMP nsZipWriter::AddEntryStream(const nsACString & aZipEntry, PRInt32 aCompression, nsIInputStream *aStream, PRBool aQueue) +{ + return AddEntryStream(aZipEntry, aModTime, aCompression, aStream, aQueue, + PERMISSIONS_FILE); +} + +/* void addEntryStream (in AUTF8String aZipEntry, in PRTime aModTime, + * in PRInt32 aCompression, in nsIInputStream aStream, + * in boolean aQueue, in unsigned long aPermissions); */ +nsresult nsZipWriter::AddEntryStream(const nsACString & aZipEntry, + PRTime aModTime, + PRInt32 aCompression, + nsIInputStream *aStream, + PRBool aQueue, + PRUint32 aPermissions) { NS_ENSURE_ARG_POINTER(aStream); if (!mStream) @@ -486,6 +506,7 @@ NS_IMETHODIMP nsZipWriter::AddEntryStream(const nsACString & aZipEntry, item.mZipEntry = aZipEntry; item.mModTime = aModTime; item.mCompression = aCompression; + item.mPermissions = aPermissions; item.mStream = aStream; if (!mQueue.AppendElement(item)) return NS_ERROR_OUT_OF_MEMORY; @@ -499,7 +520,8 @@ NS_IMETHODIMP nsZipWriter::AddEntryStream(const nsACString & aZipEntry, nsRefPtr header = new nsZipHeader(); NS_ENSURE_TRUE(header, NS_ERROR_OUT_OF_MEMORY); - header->Init(aZipEntry, aModTime, ZIP_ATTRS_FILE, mCDSOffset); + header->Init(aZipEntry, aModTime, ZIP_ATTRS(aPermissions, ZIP_ATTRS_FILE), + mCDSOffset); nsresult rv = header->WriteFileHeader(mStream); if (NS_FAILED(rv)) { SeekCDS(); @@ -742,18 +764,21 @@ NS_IMETHODIMP nsZipWriter::OnStopRequest(nsIRequest *aRequest, } nsresult nsZipWriter::InternalAddEntryDirectory(const nsACString & aZipEntry, - PRTime aModTime) + PRTime aModTime, + PRUint32 aPermissions) { nsRefPtr header = new nsZipHeader(); NS_ENSURE_TRUE(header, NS_ERROR_OUT_OF_MEMORY); + PRUint32 zipAttributes = ZIP_ATTRS(aPermissions, ZIP_ATTRS_DIRECTORY); + if (aZipEntry.Last() != '/') { nsCString dirPath; dirPath.Assign(aZipEntry + NS_LITERAL_CSTRING("/")); - header->Init(dirPath, aModTime, ZIP_ATTRS_DIRECTORY, mCDSOffset); + header->Init(dirPath, aModTime, zipAttributes, mCDSOffset); } else - header->Init(aZipEntry, aModTime, ZIP_ATTRS_DIRECTORY, mCDSOffset); + header->Init(aZipEntry, aModTime, zipAttributes, mCDSOffset); if (mEntryHash.Get(header->mName, nsnull)) return NS_ERROR_FILE_ALREADY_EXISTS; @@ -860,6 +885,9 @@ inline nsresult nsZipWriter::BeginProcessingAddition(nsZipQueueItem* aItem, NS_ENSURE_SUCCESS(rv, rv); aItem->mModTime *= PR_USEC_PER_MSEC; + rv = aItem->mFile->GetPermissions(&aItem->mPermissions); + NS_ENSURE_SUCCESS(rv, rv); + if (!isdir) { // Set up for fall through to stream reader rv = NS_NewLocalFileInputStream(getter_AddRefs(aItem->mStream), @@ -869,11 +897,13 @@ inline nsresult nsZipWriter::BeginProcessingAddition(nsZipQueueItem* aItem, // If a dir then this will fall through to the plain dir addition } + PRUint32 zipAttributes = ZIP_ATTRS(aItem->mPermissions, ZIP_ATTRS_FILE); + if (aItem->mStream) { nsRefPtr header = new nsZipHeader(); NS_ENSURE_TRUE(header, NS_ERROR_OUT_OF_MEMORY); - header->Init(aItem->mZipEntry, aItem->mModTime, ZIP_ATTRS_FILE, + header->Init(aItem->mZipEntry, aItem->mModTime, zipAttributes, mCDSOffset); nsresult rv = header->WriteFileHeader(mStream); NS_ENSURE_SUCCESS(rv, rv); @@ -897,7 +927,7 @@ inline nsresult nsZipWriter::BeginProcessingAddition(nsZipQueueItem* aItem, nsRefPtr header = new nsZipHeader(); NS_ENSURE_TRUE(header, NS_ERROR_OUT_OF_MEMORY); - header->Init(aItem->mZipEntry, aItem->mModTime, ZIP_ATTRS_FILE, + header->Init(aItem->mZipEntry, aItem->mModTime, zipAttributes, mCDSOffset); nsRefPtr stream = new nsZipDataStream(); @@ -912,7 +942,8 @@ inline nsresult nsZipWriter::BeginProcessingAddition(nsZipQueueItem* aItem, // Must be plain directory addition *complete = PR_TRUE; - return InternalAddEntryDirectory(aItem->mZipEntry, aItem->mModTime); + return InternalAddEntryDirectory(aItem->mZipEntry, aItem->mModTime, + aItem->mPermissions); } inline nsresult nsZipWriter::BeginProcessingRemoval(PRInt32 aPos) diff --git a/modules/libjar/zipwriter/src/nsZipWriter.h b/modules/libjar/zipwriter/src/nsZipWriter.h index dc60fad810d..48d4e118fb3 100644 --- a/modules/libjar/zipwriter/src/nsZipWriter.h +++ b/modules/libjar/zipwriter/src/nsZipWriter.h @@ -67,6 +67,7 @@ public: nsCOMPtr mStream; PRTime mModTime; PRInt32 mCompression; + PRUint32 mPermissions; }; class nsZipWriter : public nsIZipWriter, @@ -100,9 +101,12 @@ private: void Cleanup(); nsresult ReadFile(nsIFile *aFile); nsresult InternalAddEntryDirectory(const nsACString & aZipEntry, - PRTime aModTime); + PRTime aModTime, PRUint32 aPermissions); nsresult BeginProcessingAddition(nsZipQueueItem* aItem, PRBool* complete); nsresult BeginProcessingRemoval(PRInt32 aPos); + nsresult AddEntryStream(const nsACString & aZipEntry, PRTime aModTime, + PRInt32 aCompression, nsIInputStream *aStream, + PRBool aQueue, PRUint32 aPermissions); void BeginProcessingNextItem(); void FinishQueue(nsresult aStatus); }; diff --git a/modules/libjar/zipwriter/test/unit/test_bug446708.js b/modules/libjar/zipwriter/test/unit/test_bug446708.js index fb1ec59fc04..2cd47924df4 100644 --- a/modules/libjar/zipwriter/test/unit/test_bug446708.js +++ b/modules/libjar/zipwriter/test/unit/test_bug446708.js @@ -30,23 +30,8 @@ function AddToZip(zipWriter, path, file) function RecursivelyZipDirectory(bundle) { - // create directory service - var dirUtils = Components.classes["@mozilla.org/file/directory_service;1"] - .createInstance(Components.interfaces.nsIProperties); - - // get the temp dir, where our temporary zip attachments can be stored - var tempFile = dirUtils.get("TmpD", Components.interfaces.nsIFile).clone(); - - // create unique file there - tempFile.append(bundle.leafName + ".zip"); - tempFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, - 0600); - - zipW.open(tempFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE); - + zipW.open(tmpFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE); AddToZip(zipW, "", bundle); - - // we're done. zipW.close(); } diff --git a/modules/libjar/zipwriter/test/unit/test_zippermissions.js b/modules/libjar/zipwriter/test/unit/test_zippermissions.js new file mode 100644 index 00000000000..8c38284495b --- /dev/null +++ b/modules/libjar/zipwriter/test/unit/test_zippermissions.js @@ -0,0 +1,106 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Zip Writer Component. + * + * The Initial Developer of the Original Code is + * Dave Townsend . + * + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** + */ + +const DATA = "ZIP WRITER TEST DATA"; + +var TESTS = []; + +function build_tests() { + var id = 0; + + // Minimum mode is 0400 + for (let u = 4; u <= 7; u++) { + for (let g = 0; g <= 7; g++) { + for (let o = 0; o <= 7; o++) { + TESTS[id] = { + name: "test" + u + g + o, + permission: (u << 6) + (g << 3) + o + }; + id++; + } + } + } +} + +function run_test() { + build_tests(); + + var foStream = Cc["@mozilla.org/network/file-output-stream;1"]. + createInstance(Ci.nsIFileOutputStream); + + var tmp = tmpDir.clone(); + tmp.append("temp-permissions"); + tmp.createUnique(Ci.nsILocalFile.DIRECTORY_TYPE, 0755); + + var file = tmp.clone(); + file.append("tempfile"); + + zipW.open(tmpFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE); + for (let i = 0; i < TESTS.length; i++) { + // Open the file with the permissions to match how the zipreader extracts + // This obeys the umask + foStream.init(file, 0x02 | 0x08 | 0x20, TESTS[i].permission, 0); + foStream.close(); + + // umask may have altered the permissions so test against what they really were. + // This reduces the coverage of the test but there isn't much we can do + var perm = file.permissions & 0xfff; + if (TESTS[i].permission != perm) { + dump("File permissions for " + TESTS[i].name + " were " + perm.toString(8) + "\n"); + TESTS[i].permission = perm; + } + + zipW.addEntryFile(TESTS[i].name, Ci.nsIZipWriter.COMPRESSION_NONE, file, false); + file.permissions = 0600; + file.remove(true); + } + zipW.close(); + + zipR = new ZipReader(tmpFile); + for (let i = 0; i < TESTS.length; i++) { + zipR.extract(TESTS[i].name, file); + dump("Testing file permissions for " + TESTS[i].name + "\n"); + do_check_eq(file.permissions & 0xfff, TESTS[i].permission); + do_check_false(file.isDirectory()); + file.permissions = 0600; + file.remove(true); + } + zipR.close(); + + tmp.remove(true); +}