sapling/eden/fs/utils/XAttr.cpp
Wez Furlong d6adcfc058 add fsattr utility for testing purposes
Summary:
This is the spiritual successor to D3302706 which originally
wanted to solve this by adding a python extension.  That would prove
to be too painful for the opensource build so it was shelved.

We now need to be able to run our tests in an environment that doesn't
have the `attr` rpm installed so this is a good time to fix this
in a more portable way.

This diff adds a little wrapper around the functions that we already
have for consuming extended attribute information and augments them
with another to list attributes.

The utility emits output in json format and is intended to be fed
directly into the helper functions we have in `fs.py`.

Reviewed By: chadaustin

Differential Revision: D6851182

fbshipit-source-id: 3d1d1a351f2e01405645d45658d1c8bc61a659a4
2018-01-30 21:50:39 -08:00

147 lines
3.3 KiB
C++

/*
* Copyright (c) 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "XAttr.h"
#include <folly/Exception.h>
#include <folly/File.h>
namespace facebook {
namespace eden {
std::string getxattr(folly::StringPiece path, folly::StringPiece name) {
folly::File file(path, O_RDONLY);
return fgetxattr(file.fd(), name);
}
std::string fgetxattr(int fd, folly::StringPiece name) {
std::string result;
// Reasonable ballpark for most attributes we might want; this saves
// us from an extra syscall to query the size in the common case.
result.resize(64, 0);
auto namestr = name.str();
// We loop until we either hit a hard error or succeed in extracting
// the requested information.
while (true) {
// First, try to read into the buffer at its existing size.
auto size = ::fgetxattr(
fd,
namestr.c_str(),
&result[0],
result.size()
#ifdef __APPLE__
,
0, // position
0 // options
#endif
);
if (size != -1) {
result.resize(size);
return result;
}
// ERANGE means that the buffer wasn't large enough. Any other
// error terminates our attempt to get the attribute.
if (errno != ERANGE) {
folly::throwSystemError("fgetxattr");
}
// Got the wrong size, query to find out what we should have used
size = ::fgetxattr(
fd,
namestr.c_str(),
nullptr,
0
#ifdef __APPLE__
,
0, // position
0 // options
#endif
);
if (size < 0) {
folly::throwSystemError("fgetxattr to query the size failed");
}
// Make sure we have room for a trailing NUL byte.
result.resize(size + 1, 0);
}
}
void fsetxattr(int fd, folly::StringPiece name, folly::StringPiece value) {
auto namestr = name.str();
folly::checkUnixError(::fsetxattr(
fd,
namestr.c_str(),
value.data(),
value.size()
#ifdef __APPLE__
,
0 // position
#endif
,
0 // allow create and replace
));
}
std::vector<std::string> listxattr(folly::StringPiece path) {
std::string buf;
auto pathStr = path.str();
buf.resize(128, 0);
while (true) {
auto size = ::listxattr(
pathStr.c_str(),
&buf[0],
buf.size()
#ifdef __APPLE__
,
XATTR_NOFOLLOW
#endif
);
if (size != -1) {
// Success; parse the result in a list of names separated by NUL
// bytes, terminated by a NUL byte.
std::vector<std::string> result;
// Don't include the final terminator in the size, as that just causes
// the split array to contain a final empty name.
folly::split('\0', folly::StringPiece(buf.data(), size - 1), result);
return result;
}
if (errno != ERANGE) {
folly::throwSystemError("listxattr");
}
// Query for the size
size = ::listxattr(
pathStr.c_str(),
nullptr,
0
#ifdef __APPLE__
,
XATTR_NOFOLLOW
#endif
);
if (size == -1) {
folly::throwSystemError("listxattr");
}
buf.resize(size, 0);
}
}
} // namespace eden
} // namespace facebook