mirror of
https://github.com/ksherlock/profuse.git
synced 2024-12-23 11:31:43 +00:00
415 lines
8.3 KiB
C++
415 lines
8.3 KiB
C++
#include <string>
|
|
#include <vector>
|
|
#include <algorithm>
|
|
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <cstdio>
|
|
#include <cctype>
|
|
#include <cerrno>
|
|
|
|
#include <stdint.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/xattr.h>
|
|
|
|
|
|
typedef std::vector<std::string>::iterator vsiter;
|
|
|
|
#ifdef __APPLE__
|
|
// apple has additional parameter for position and options.
|
|
|
|
inline ssize_t getxattr(const char *path, const char *name, void *value, size_t size)
|
|
{
|
|
return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
|
|
}
|
|
|
|
// apple has additional parameter for options.
|
|
inline ssize_t listxattr(const char *path, char *namebuf, size_t size)
|
|
{
|
|
return listxattr(path, namebuf, size, XATTR_NOFOLLOW);
|
|
}
|
|
|
|
// apple has additional parameter for offset .
|
|
inline int setxattr(const char *path, const char *name, void *value, size_t size, int flags)
|
|
{
|
|
return setxattr(path, name, value, size, 0, flags);
|
|
}
|
|
#endif
|
|
|
|
void hexdump(const uint8_t *data, ssize_t size)
|
|
{
|
|
const char *HexMap = "0123456789abcdef";
|
|
|
|
char buffer1[16 * 3 + 1 + 1];
|
|
char buffer2[16 + 1];
|
|
ssize_t offset = 0;
|
|
unsigned i, j;
|
|
|
|
|
|
while(size > 0)
|
|
{
|
|
std::memset(buffer1, ' ', sizeof(buffer1));
|
|
std::memset(buffer2, ' ', sizeof(buffer2));
|
|
|
|
unsigned linelen = (unsigned)std::min(size, (ssize_t)16);
|
|
|
|
|
|
for (i = 0, j = 0; i < linelen; i++)
|
|
{
|
|
unsigned x = data[i];
|
|
buffer1[j++] = HexMap[x >> 4];
|
|
buffer1[j++] = HexMap[x & 0x0f];
|
|
j++;
|
|
if (i == 7) j++;
|
|
|
|
// isascii not part of std:: and may be a macro.
|
|
buffer2[i] = isascii(x) && std::isprint(x) ? x : '.';
|
|
|
|
}
|
|
|
|
buffer1[sizeof(buffer1)-1] = 0;
|
|
buffer2[sizeof(buffer2)-1] = 0;
|
|
|
|
|
|
std::printf("%06x:\t%s\t%s\n", (unsigned)offset, buffer1, buffer2);
|
|
offset += 16;
|
|
data += 16;
|
|
size -= 16;
|
|
}
|
|
std::printf("\n");
|
|
}
|
|
|
|
/*
|
|
* build a list of attribute names for a file.
|
|
* returns -1 on failure
|
|
* otherwise, returns # of attributes.
|
|
*
|
|
*/
|
|
ssize_t get_attr_list(const char * fname, std::vector<std::string> &out)
|
|
{
|
|
ssize_t asize = listxattr(fname, NULL, 0);
|
|
if (asize < 0) return -1;
|
|
if (asize == 0) return 0;
|
|
|
|
char *buffer = new char[asize];
|
|
|
|
if ((asize = listxattr(fname, buffer, asize)) < 0)
|
|
{
|
|
delete []buffer;
|
|
return -1;
|
|
}
|
|
|
|
//hexdump((uint8_t *)buffer, asize);
|
|
|
|
char *cp = buffer;
|
|
// buffer is null-terminated utf-8 strings
|
|
// eg:
|
|
// c a t 0 d o g 0
|
|
// 0 1 2 3 4 5 6 7
|
|
while (asize > 0)
|
|
{
|
|
ssize_t len = std::strlen(cp);
|
|
|
|
out.push_back(std::string(cp, len));
|
|
++len;
|
|
asize -= len;
|
|
cp += len;
|
|
}
|
|
|
|
delete []buffer;
|
|
|
|
return out.size();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* hexdump an individual attribute.
|
|
*/
|
|
void dumpxattr(const char *file, const char *attr)
|
|
{
|
|
ssize_t asize;
|
|
char *buffer;
|
|
|
|
asize = getxattr(file, attr, NULL, 0);
|
|
if (asize < 0)
|
|
{
|
|
std::perror(attr);
|
|
return;
|
|
}
|
|
|
|
std::printf("%s: %u\n", attr, (unsigned)asize);
|
|
|
|
buffer = new char[asize];
|
|
//if (!buffer) return;
|
|
|
|
asize = getxattr(file, attr, buffer, asize);
|
|
if (asize >= 0)
|
|
{
|
|
hexdump((uint8_t *)buffer, asize);
|
|
}
|
|
else
|
|
{
|
|
std::perror(attr);
|
|
}
|
|
|
|
delete []buffer;
|
|
}
|
|
|
|
|
|
/*
|
|
* list a file's attributes (and size)
|
|
*
|
|
*/
|
|
int list(int argc, char **argv)
|
|
{
|
|
|
|
const char *fname = *argv;
|
|
|
|
std::vector<std::string>attr;
|
|
|
|
if (argc == 1)
|
|
{
|
|
get_attr_list(fname, attr);
|
|
std::sort(attr.begin(), attr.end());
|
|
}
|
|
else
|
|
{
|
|
for (int i = 1; i < argc; ++i)
|
|
{
|
|
attr.push_back(argv[i]);
|
|
}
|
|
}
|
|
|
|
|
|
// find the max name length (not utf-8 happy)
|
|
unsigned maxname = 0;
|
|
for (vsiter i = attr.begin(); i != attr.end() ; ++i)
|
|
{
|
|
maxname = std::max(maxname, (unsigned)i->length());
|
|
}
|
|
maxname += 2;
|
|
|
|
for (vsiter i = attr.begin(); i != attr.end() ; ++i)
|
|
{
|
|
const char *attr_name = i->c_str();
|
|
|
|
ssize_t asize = getxattr(fname, attr_name, NULL, 0);
|
|
if (asize >= 0)
|
|
{
|
|
std::printf("%-*s %8u\n", maxname, attr_name, (unsigned)asize);
|
|
}
|
|
else
|
|
{
|
|
std::perror(attr_name);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* hexdump a file's attributes.
|
|
*
|
|
*/
|
|
int dump(int argc, char **argv)
|
|
{
|
|
|
|
const char *fname = *argv;
|
|
|
|
std::vector<std::string>attr;
|
|
|
|
if (argc == 1)
|
|
{
|
|
get_attr_list(fname, attr);
|
|
std::sort(attr.begin(), attr.end());
|
|
}
|
|
else
|
|
{
|
|
for (int i = 1; i < argc; ++i)
|
|
{
|
|
attr.push_back(argv[i]);
|
|
}
|
|
}
|
|
|
|
|
|
for (vsiter i = attr.begin(); i != attr.end() ; ++i)
|
|
{
|
|
const char *attr_name = i->c_str();
|
|
|
|
dumpxattr(fname, attr_name);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
// must specify the xattr name.
|
|
int read(int argc, char **argv)
|
|
{
|
|
|
|
if (argc != 2)
|
|
{
|
|
std::fprintf(stderr, "Must specify attribute to be read.\n");
|
|
return -1;
|
|
}
|
|
|
|
const char *fname = argv[0];
|
|
const char *attr_name = argv[1];
|
|
|
|
// get the attribute size and read it in.
|
|
ssize_t asize = getxattr(fname, attr_name, NULL, 0);
|
|
if (asize < 0)
|
|
{
|
|
std::perror(attr_name);
|
|
return -1;
|
|
}
|
|
if (asize == 0) return 0;
|
|
|
|
uint8_t *buffer = new uint8_t[asize];
|
|
asize = getxattr(fname, attr_name, buffer, asize);
|
|
|
|
if (asize < 0)
|
|
{
|
|
std::perror(attr_name);
|
|
return -1;
|
|
}
|
|
|
|
if (::write(STDOUT_FILENO, buffer, asize) != asize)
|
|
{
|
|
perror(NULL);
|
|
delete []buffer;
|
|
return -1;
|
|
}
|
|
|
|
|
|
delete []buffer;
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
ssize_t read_all(int fd, std::vector<uint8_t> &out)
|
|
{
|
|
struct stat st;
|
|
ssize_t bsize;
|
|
|
|
// stdin could be a file, a pipe, or a tty.
|
|
if (fstat(fd, &st) < 0)
|
|
{
|
|
std::perror("stdin");
|
|
return -1;
|
|
}
|
|
|
|
|
|
bsize = std::max(st.st_blksize, (blksize_t)512);
|
|
out.reserve(std::max(st.st_size, (off_t)512));
|
|
|
|
uint8_t *buffer = new uint8_t[bsize];
|
|
|
|
for(;;)
|
|
{
|
|
ssize_t size = ::read(fd, buffer, bsize);
|
|
|
|
if (size == 0) break;
|
|
if (size == -1)
|
|
{
|
|
if (errno == EINTR) continue;
|
|
delete[] buffer;
|
|
std::perror(NULL);
|
|
return -1;
|
|
}
|
|
|
|
// force reserve?
|
|
out.insert(out.end(), buffer, buffer + size);
|
|
}
|
|
|
|
|
|
delete []buffer;
|
|
|
|
return out.size();
|
|
}
|
|
|
|
|
|
// xattr write filename attrname
|
|
// stdin -> filename:attrname
|
|
int write(int argc, char **argv)
|
|
{
|
|
|
|
std::vector<uint8_t> buffer;
|
|
int flags = 0;
|
|
|
|
if (argc != 2)
|
|
{
|
|
std::fprintf(stderr, "Must specify attribute to be read.\n");
|
|
return -1;
|
|
}
|
|
|
|
const char *fname = argv[0];
|
|
const char *attr_name = argv[1];
|
|
|
|
if (read_all(STDIN_FILENO, buffer) < 0)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// Flag options:
|
|
// XATTR_CREATE : fails if attr already exists.
|
|
// XATTR_REPLACE : fails if attr does not exist.
|
|
// if neither of the above is specified, will create and replace.
|
|
// XATTR_NOFOLLOW : do not follow symbolic links (Apple only)
|
|
|
|
#ifdef __APPLE__
|
|
flags = XATTR_NOFOLLOW;
|
|
#endif
|
|
|
|
ssize_t asize = setxattr(fname, attr_name, &buffer[0], buffer.size(), flags);
|
|
|
|
if (asize < 0)
|
|
{
|
|
std::perror(attr_name);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void usage(const char *name)
|
|
{
|
|
std::printf("usage:\n");
|
|
std::printf("%s list file [attr ...]\n", name);
|
|
std::printf("%s dump file [attr ...]\n", name);
|
|
std::printf("%s read file attr\n", name);
|
|
std::printf("%s write file attr\n", name);
|
|
|
|
std::exit(0);
|
|
}
|
|
|
|
/*
|
|
* xattr list file [xattr ...]
|
|
* xattr dump file [xattr ...]
|
|
*
|
|
*/
|
|
int main(int argc, char **argv)
|
|
{
|
|
if (argc < 3) usage(*argv);
|
|
|
|
if (std::strcmp(argv[1], "list") == 0) return list(argc - 2, argv + 2);
|
|
if (std::strcmp(argv[1], "dump") == 0) return dump(argc - 2, argv + 2);
|
|
|
|
if (std::strcmp(argv[1], "read") == 0) return read(argc - 2, argv + 2);
|
|
if (std::strcmp(argv[1], "write") == 0) return write(argc - 2, argv + 2);
|
|
|
|
|
|
usage(*argv);
|
|
|
|
return 1;
|
|
}
|