Bug 446464: File permissions are not preserved when zipping with zipwriter. r=biesi

This commit is contained in:
Dave Townsend 2008-10-31 20:10:46 +00:00
parent 4998d614a8
commit c801ea2744
5 changed files with 165 additions and 32 deletions

View File

@ -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),

View File

@ -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<nsZipHeader> 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<nsZipHeader> 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<nsZipHeader> 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<nsZipHeader> 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<nsZipDataStream> 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)

View File

@ -67,6 +67,7 @@ public:
nsCOMPtr<nsIInputStream> 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);
};

View File

@ -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();
}

View File

@ -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 <dtownsend@oxymoronical.com>.
*
* 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);
}