зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1736331 - Add nsILocalFileMac methods for interacting with extended attributes r=nika
This patch adds support for setting, getting, and reading extended filesystem attributes on macOS. Differential Revision: https://phabricator.services.mozilla.com/D133836
This commit is contained in:
Родитель
f53fe4b9a2
Коммит
f93fd9cc79
|
@ -168,6 +168,39 @@ interface nsILocalFileMac : nsIFile
|
|||
* this is the same as lastModifiedTime.
|
||||
*/
|
||||
readonly attribute int64_t bundleContentsLastModifiedTime;
|
||||
|
||||
/**
|
||||
* Return whether or not the file has an extended attribute.
|
||||
*
|
||||
* @param aAttrName The attribute name to check for.
|
||||
*
|
||||
* @return Whether or not the extended attribute is present.
|
||||
*/
|
||||
bool hasXAttr(in ACString aAttrName);
|
||||
|
||||
/**
|
||||
* Get the value of the extended attribute.
|
||||
*
|
||||
* @param aAttrName The attribute name to read.
|
||||
*
|
||||
* @return The extended attribute value.
|
||||
*/
|
||||
Array<uint8_t> getXAttr(in ACString aAttrName);
|
||||
|
||||
/**
|
||||
* Set an extended attribute.
|
||||
*
|
||||
* @param aAttrName The attribute name to set a value for.
|
||||
* @param aAttrValue The value to set for the attribute.
|
||||
*/
|
||||
void setXAttr(in ACString aAttrName, in Array<uint8_t> aAttrValue);
|
||||
|
||||
/**
|
||||
* Delete an extended attribute.
|
||||
*
|
||||
* @param aAttrName The extended attribute to delete.
|
||||
*/
|
||||
void delXAttr(in ACString aAttrName);
|
||||
};
|
||||
|
||||
%{C++
|
||||
|
|
|
@ -109,6 +109,11 @@ inline nsresult nsresultForErrno(int aErr) {
|
|||
case EFBIG: /* File too large. */
|
||||
return NS_ERROR_FILE_TOO_BIG;
|
||||
|
||||
#ifdef ENOATTR
|
||||
case ENOATTR:
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
#endif // ENOATTR
|
||||
|
||||
default:
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
|
|
@ -917,11 +917,7 @@ nsLocalFile::CopyToNative(nsIFile* aNewParent, const nsACString& aNewName) {
|
|||
|
||||
#if defined(XP_MACOSX)
|
||||
bool quarantined = true;
|
||||
if (getxattr(mPath.get(), "com.apple.quarantine", nullptr, 0, 0, 0) == -1) {
|
||||
if (errno == ENOATTR) {
|
||||
quarantined = false;
|
||||
}
|
||||
}
|
||||
(void)HasXAttr("com.apple.quarantine"_ns, &quarantined);
|
||||
#endif
|
||||
|
||||
PRFileDesc* oldFD;
|
||||
|
@ -1007,7 +1003,7 @@ nsLocalFile::CopyToNative(nsIFile* aNewParent, const nsACString& aNewName) {
|
|||
else if (!quarantined) {
|
||||
// If the original file was not in quarantine, lift the quarantine that
|
||||
// file creation added because of LSFileQuarantineEnabled.
|
||||
removexattr(newPathName.get(), "com.apple.quarantine", 0);
|
||||
(void)newFile->DelXAttr("com.apple.quarantine"_ns);
|
||||
}
|
||||
#endif // defined(XP_MACOSX)
|
||||
|
||||
|
@ -2281,6 +2277,86 @@ nsresult NS_NewLocalFile(const nsAString& aPath, bool aFollowLinks,
|
|||
|
||||
#ifdef MOZ_WIDGET_COCOA
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLocalFile::HasXAttr(const nsACString& aAttrName, bool* aHasAttr) {
|
||||
NS_ENSURE_ARG_POINTER(aHasAttr);
|
||||
|
||||
nsAutoCString attrName{aAttrName};
|
||||
|
||||
ssize_t size = getxattr(mPath.get(), attrName.get(), nullptr, 0, 0, 0);
|
||||
if (size == -1) {
|
||||
if (errno == ENOATTR) {
|
||||
*aHasAttr = false;
|
||||
} else {
|
||||
return NSRESULT_FOR_ERRNO();
|
||||
}
|
||||
}
|
||||
|
||||
*aHasAttr = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLocalFile::GetXAttr(const nsACString& aAttrName,
|
||||
nsTArray<uint8_t>& aAttrValue) {
|
||||
aAttrValue.Clear();
|
||||
|
||||
nsAutoCString attrName{aAttrName};
|
||||
|
||||
ssize_t size = getxattr(mPath.get(), attrName.get(), nullptr, 0, 0, 0);
|
||||
|
||||
if (size == -1) {
|
||||
return NSRESULT_FOR_ERRNO();
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
aAttrValue.SetCapacity(size);
|
||||
|
||||
// The attribute can change between our first call and this call, so we need
|
||||
// to re-check the size and possibly call with a larger buffer.
|
||||
ssize_t newSize = getxattr(mPath.get(), attrName.get(),
|
||||
aAttrValue.Elements(), size, 0, 0);
|
||||
if (newSize == -1) {
|
||||
return NSRESULT_FOR_ERRNO();
|
||||
}
|
||||
|
||||
if (newSize <= size) {
|
||||
aAttrValue.SetLength(newSize);
|
||||
break;
|
||||
} else {
|
||||
size = newSize;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLocalFile::SetXAttr(const nsACString& aAttrName,
|
||||
const nsTArray<uint8_t>& aAttrValue) {
|
||||
nsAutoCString attrName{aAttrName};
|
||||
|
||||
if (setxattr(mPath.get(), attrName.get(), aAttrValue.Elements(),
|
||||
aAttrValue.Length(), 0, 0) == -1) {
|
||||
return NSRESULT_FOR_ERRNO();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLocalFile::DelXAttr(const nsACString& aAttrName) {
|
||||
nsAutoCString attrName{aAttrName};
|
||||
|
||||
// Ignore removing an attribute that does not exist.
|
||||
if (removexattr(mPath.get(), attrName.get(), 0) == -1) {
|
||||
return NSRESULT_FOR_ERRNO();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static nsresult MacErrorMapper(OSErr inErr) {
|
||||
nsresult outErr;
|
||||
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const { Assert } = ChromeUtils.import("resource://testing-common/Assert.jsm");
|
||||
const { FileUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/FileUtils.jsm"
|
||||
);
|
||||
|
||||
async function run_test() {
|
||||
const path = PathUtils.join(await IOUtils.getTempDir(), "macos-xattrs.tmp.d");
|
||||
await IOUtils.makeDirectory(path);
|
||||
|
||||
try {
|
||||
test_macos_xattr(path);
|
||||
} finally {
|
||||
await IOUtils.remove(path, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
async function test_macos_xattr(tmpDir) {
|
||||
const encoder = new TextEncoder();
|
||||
const path = PathUtils.join(tmpDir, "file.tmp");
|
||||
|
||||
await IOUtils.writeUTF8(path, "");
|
||||
|
||||
const file = new FileUtils.File(path);
|
||||
file.queryInterface(Cc.nsILocalFileMac);
|
||||
|
||||
info("Testing reading an attribute that does not exist");
|
||||
Assert.ok(
|
||||
!file.hasXAttr("bogus.attr"),
|
||||
"File should not have attribute before being set."
|
||||
);
|
||||
Assert.throws(
|
||||
() => file.getXAttr("bogus.attr"),
|
||||
/NS_ERROR_NOT_AVAILABLE/,
|
||||
"Attempting to get an attribute that does not exist throws"
|
||||
);
|
||||
|
||||
{
|
||||
info("Testing setting and reading an attribute");
|
||||
file.setXAttr("bogus.attr", encoder.encode("bogus"));
|
||||
Assert.ok(
|
||||
file.hasXAttr("bogus.attr"),
|
||||
"File should have attribute after being set"
|
||||
);
|
||||
const result = file.getXAttr("bogus.attr");
|
||||
|
||||
Assert.equal(
|
||||
result,
|
||||
encoder.encode("bogus"),
|
||||
"File should have attribute value matching what was set"
|
||||
);
|
||||
}
|
||||
|
||||
info("Testing removing an attribute");
|
||||
file.delXAttr("bogus.attr");
|
||||
Assert.ok(
|
||||
!file.hasXAttr("bogus.attr"),
|
||||
"File should no longer have the attribute after removal"
|
||||
);
|
||||
Assert.throws(
|
||||
() => file.getXAttr("bogus.attr"),
|
||||
/NS_ERROR_NOT_AVAILABLE/,
|
||||
"Attempting to get an attribute after removal results in an error"
|
||||
);
|
||||
|
||||
info("Testing removing an attribute that does not exist");
|
||||
Assert.throws(
|
||||
() => file.delXAttr("bogus.attr"),
|
||||
/NS_ERROR_NOT_AVAILABLE/,
|
||||
"Attempting to remove an attribute that does not exist throws"
|
||||
);
|
||||
}
|
|
@ -31,6 +31,8 @@ fail-if = os == "android"
|
|||
[test_ioutil.js]
|
||||
[test_localfile.js]
|
||||
[test_mac_bundle.js]
|
||||
[test_mac_xattrs.js]
|
||||
skip-if = os != "mac"
|
||||
[test_nsIMutableArray.js]
|
||||
[test_nsIProcess.js]
|
||||
skip-if = os == "win" || os == "linux" || os == "android" # bug 582821, bug 1325609, Bug 676998, Bug 1631671
|
||||
|
|
Загрузка…
Ссылка в новой задаче