Browse Source

Merge pull request #2 from rfhb/master

support for linux and symbolic links
master
boB Rudis 6 years ago
committed by GitHub
parent
commit
9ac63efe1f
No known key found for this signature in database GPG Key ID: 1D7529BE14E2BBA9
  1. 24
      R/RcppExports.R
  2. 1
      R/df.R
  3. 41
      R/get.R
  4. 2
      R/rm.R
  5. 6
      man/get_xattr_raw.Rd
  6. 6
      man/get_xattr_size.Rd
  7. 20
      src/RcppExports.cpp
  8. 44
      src/extattr.h
  9. 38
      src/xattrs-main.cpp
  10. 109
      tests/testthat/test-xattrs.R

24
R/RcppExports.R

@ -28,28 +28,12 @@ rcpp_get_xattr <- function(path, name, follow_symlinks = TRUE) {
.Call('_xattrs_rcpp_get_xattr', PACKAGE = 'xattrs', path, name, follow_symlinks)
}
#' Retrieve the (raw) contents of the named xattr
#'
#' @md
#' @param path target path (file or dir); this is auto-expanded
#' @param name xattr name to retrieve
#' @param follow_symlinks if `FALSE` get xattr of the symlink vs the target it references
#' @export
#' @example inst/examples/ex1.R
get_xattr_raw <- function(path, name, follow_symlinks = TRUE) {
.Call('_xattrs_get_xattr_raw', PACKAGE = 'xattrs', path, name, follow_symlinks)
rcpp_get_xattr_raw <- function(path, name, follow_symlinks = TRUE) {
.Call('_xattrs_rcpp_get_xattr_raw', PACKAGE = 'xattrs', path, name, follow_symlinks)
}
#' Retrieve the size (bytes) of the named xattr
#'
#' @md
#' @param path target path (file or dir); this is auto-expanded
#' @param name xattr name to retrieve
#' @param follow_symlinks if `FALSE` get xattr of the symlink vs the target it references
#' @export
#' @example inst/examples/ex1.R
get_xattr_size <- function(path, name, follow_symlinks = TRUE) {
.Call('_xattrs_get_xattr_size', PACKAGE = 'xattrs', path, name, follow_symlinks)
rcpp_get_xattr_size <- function(path, name, follow_symlinks = TRUE) {
.Call('_xattrs_rcpp_get_xattr_size', PACKAGE = 'xattrs', path, name, follow_symlinks)
}
rcpp_get_xattr_df <- function(path, follow_symlinks = TRUE) {

1
R/df.R

@ -11,6 +11,7 @@ get_xattr_df <- function(path, follow_symlinks = TRUE) {
class(xattr_list$contents) <- c("AsIs", "list")
xdf <- as.data.frame(xattr_list, stringsAsFactors=FALSE)
attributes(xdf$contents) <- NULL
xdf$name <- handle_user_prefix_return(xdf$name)
class(xdf) <- c("tbl_df", "tbl", "data.frame")
xdf

41
R/get.R

@ -17,4 +17,43 @@ get_xattr <- function(path, name, follow_symlinks=TRUE) {
ret
}
}
#' Get raw contents of an extended attribute
#'
#' @md
#' @inheritParams get_xattr
#' @export
#' @example inst/examples/ex1.R
get_xattr_raw <- function(path, name, follow_symlinks=TRUE) {
path <- path.expand(path)
if (!file.exists(path)) stop("File not found.", call.=FALSE)
name <- handle_user_prefix_param(name)
ret <- rcpp_get_xattr_raw(path, name, follow_symlinks)
ret
}
#' Get size of an extended attribute
#'
#' @md
#' @inheritParams get_xattr
#' @export
#' @example inst/examples/ex1.R
get_xattr_size <- function(path, name, follow_symlinks=TRUE) {
path <- path.expand(path)
if (!file.exists(path)) stop("File not found.", call.=FALSE)
name <- handle_user_prefix_param(name)
ret <- rcpp_get_xattr_size(path, name, follow_symlinks)
ret
}

2
R/rm.R

@ -15,7 +15,7 @@ rm_xattr <- function(path, name, follow_symlinks=TRUE) {
ret <- rcpp_rm_xattr(path, name, follow_symlinks)
if (ret != 0L) warning(sprintf("Error %s while removing attribute.", ret))
if (ret != 0L) warning(sprintf("Error %s while removing attribute.", ret), call. = FALSE, immediate. = TRUE)
return(invisible(ret == 0L))

6
man/get_xattr_raw.Rd

@ -1,8 +1,8 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/RcppExports.R
% Please edit documentation in R/get.R
\name{get_xattr_raw}
\alias{get_xattr_raw}
\title{Retrieve the (raw) contents of the named xattr}
\title{Get raw contents of an extended attribute}
\usage{
get_xattr_raw(path, name, follow_symlinks = TRUE)
}
@ -14,7 +14,7 @@ get_xattr_raw(path, name, follow_symlinks = TRUE)
\item{follow_symlinks}{if \code{FALSE} get xattr of the symlink vs the target it references}
}
\description{
Retrieve the (raw) contents of the named xattr
Get raw contents of an extended attribute
}
\examples{
# Create a temp file for the example

6
man/get_xattr_size.Rd

@ -1,8 +1,8 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/RcppExports.R
% Please edit documentation in R/get.R
\name{get_xattr_size}
\alias{get_xattr_size}
\title{Retrieve the size (bytes) of the named xattr}
\title{Get size of an extended attribute}
\usage{
get_xattr_size(path, name, follow_symlinks = TRUE)
}
@ -14,7 +14,7 @@ get_xattr_size(path, name, follow_symlinks = TRUE)
\item{follow_symlinks}{if \code{FALSE} get xattr of the symlink vs the target it references}
}
\description{
Retrieve the size (bytes) of the named xattr
Get size of an extended attribute
}
\examples{
# Create a temp file for the example

20
src/RcppExports.cpp

@ -69,29 +69,29 @@ BEGIN_RCPP
return rcpp_result_gen;
END_RCPP
}
// get_xattr_raw
RawVector get_xattr_raw(const std::string path, std::string name, bool follow_symlinks);
RcppExport SEXP _xattrs_get_xattr_raw(SEXP pathSEXP, SEXP nameSEXP, SEXP follow_symlinksSEXP) {
// rcpp_get_xattr_raw
RawVector rcpp_get_xattr_raw(const std::string path, std::string name, bool follow_symlinks);
RcppExport SEXP _xattrs_rcpp_get_xattr_raw(SEXP pathSEXP, SEXP nameSEXP, SEXP follow_symlinksSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< const std::string >::type path(pathSEXP);
Rcpp::traits::input_parameter< std::string >::type name(nameSEXP);
Rcpp::traits::input_parameter< bool >::type follow_symlinks(follow_symlinksSEXP);
rcpp_result_gen = Rcpp::wrap(get_xattr_raw(path, name, follow_symlinks));
rcpp_result_gen = Rcpp::wrap(rcpp_get_xattr_raw(path, name, follow_symlinks));
return rcpp_result_gen;
END_RCPP
}
// get_xattr_size
ssize_t get_xattr_size(const std::string path, std::string name, bool follow_symlinks);
RcppExport SEXP _xattrs_get_xattr_size(SEXP pathSEXP, SEXP nameSEXP, SEXP follow_symlinksSEXP) {
// rcpp_get_xattr_size
ssize_t rcpp_get_xattr_size(const std::string path, std::string name, bool follow_symlinks);
RcppExport SEXP _xattrs_rcpp_get_xattr_size(SEXP pathSEXP, SEXP nameSEXP, SEXP follow_symlinksSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< const std::string >::type path(pathSEXP);
Rcpp::traits::input_parameter< std::string >::type name(nameSEXP);
Rcpp::traits::input_parameter< bool >::type follow_symlinks(follow_symlinksSEXP);
rcpp_result_gen = Rcpp::wrap(get_xattr_size(path, name, follow_symlinks));
rcpp_result_gen = Rcpp::wrap(rcpp_get_xattr_size(path, name, follow_symlinks));
return rcpp_result_gen;
END_RCPP
}
@ -114,8 +114,8 @@ static const R_CallMethodDef CallEntries[] = {
{"_xattrs_rcpp_rm_xattr", (DL_FUNC) &_xattrs_rcpp_rm_xattr, 3},
{"_xattrs_rcpp_list_xattrs", (DL_FUNC) &_xattrs_rcpp_list_xattrs, 2},
{"_xattrs_rcpp_get_xattr", (DL_FUNC) &_xattrs_rcpp_get_xattr, 3},
{"_xattrs_get_xattr_raw", (DL_FUNC) &_xattrs_get_xattr_raw, 3},
{"_xattrs_get_xattr_size", (DL_FUNC) &_xattrs_get_xattr_size, 3},
{"_xattrs_rcpp_get_xattr_raw", (DL_FUNC) &_xattrs_rcpp_get_xattr_raw, 3},
{"_xattrs_rcpp_get_xattr_size", (DL_FUNC) &_xattrs_rcpp_get_xattr_size, 3},
{"_xattrs_rcpp_get_xattr_df", (DL_FUNC) &_xattrs_rcpp_get_xattr_df, 2},
{NULL, NULL, 0}
};

44
src/extattr.h

@ -46,27 +46,38 @@ inline int setxattr(int fd, const std::string &name, const std::string &value, i
}
*/
inline int setxattr(const std::string path, const std::string &name, const std::string &value, int options=0)
{
#if defined(__APPLE__) && defined(__MACH__)
return setxattr(path.c_str(), name.c_str(), value.data(), value.length(), 0, options);
#endif
#if defined(__linux__)
return setxattr(path.c_str(), name.c_str(), value.data(), value.length(), 0);
/* By default (i.e., options flags is zero), the extended attribute will be
* created if it does not exist, or the value will be replaced if the attribute exists.*/
if (options == 0){
return setxattr(path.c_str(), name.c_str(), value.data(), value.length(), 0);
} else {
return lsetxattr(path.c_str(), name.c_str(), value.data(), value.length(), 0);
}
#endif
#if defined(__FreeBSD__)
return extattr_set_file(path.c_str(), EXTATTR_NAMESPACE_USER, name.c_str(), value.data(), value.length());
#endif
}
inline ssize_t getxattrsize(const std::string path, const std::string &name, int options=0)
{
#if defined(__APPLE__) && defined(__MACH__)
return getxattr(path.c_str(), name.c_str(), NULL, 0, 0, options);
#endif
#if defined(__linux__)
/* getxattr(const char *path, const char *name, void *value, size_t size); */
return getxattr(path.c_str(), name.c_str(), NULL, 0);
if (options == 0){
return getxattr(path.c_str(), name.c_str(), NULL, 0);
} else{
return lgetxattr(path.c_str(), name.c_str(), NULL, 0);
}
#endif
#if defined(__FreeBSD__)
return extattr_get_file(path.c_str(), EXTATTR_NAMESPACE_USER, name.c_str(), NULL, 0);
@ -87,8 +98,11 @@ inline std::string getxattr(const std::string path, const std::string &name, int
getxattr(path.c_str(), name.c_str(), buf, size, 0, options);
#endif
#if defined(__linux__)
/* getxattr(const char *path, const char *name, void *value, size_t size); */
getxattr(path.c_str(), name.c_str(), buf, size);
if (options == 0){
getxattr(path.c_str(), name.c_str(), buf, size);
} else{
lgetxattr(path.c_str(), name.c_str(), buf, size);
}
#endif
#if defined(__FreeBSD__)
extattr_get_file(path.c_str(), EXTATTR_NAMESPACE_USER, name.c_str(), buf, size);
@ -106,8 +120,11 @@ inline ssize_t listxattrsize(const std::string path, int options=0)
return listxattr(path.c_str(), NULL, 0, options);
#endif
#if defined(__linux__)
/* ssize_t listxattr(const char *path, char *list, size_t size); */
return listxattr(path.c_str(), NULL, 0);
if (options == 0){
return listxattr(path.c_str(), NULL, 0);
} else{
return llistxattr(path.c_str(), NULL, 0);
}
#endif
#if defined(__FreeBSD__)
return extattr_list_file(path.c_str(), EXTATTR_NAMESPACE_USER, NULL, 0);
@ -123,8 +140,11 @@ inline size_t _listxattr(const std::string path, char *buf, const size_t size, i
return listxattr(path.c_str(), buf, size, options);
#endif
#if defined(__linux__)
/* ssize_t listxattr(const char *path, char *list, size_t size); */
return listxattr(path.c_str(), buf, size);
if (options == 0){
return listxattr(path.c_str(), buf, size);
} else{
return llistxattr(path.c_str(), buf, size);
}
#endif
#if defined(__FreeBSD__)
return extattr_list_file(path.c_str(), EXTATTR_NAMESPACE_USER, buf, size);
@ -359,7 +379,11 @@ inline int removexattr(const std::string path, const std::string name, int optio
return removexattr(path.c_str(), name.c_str(), options);
#endif
#if defined(__linux__)
return removexattr(path.c_str(), name.c_str(), options);
if (options == 0){
return removexattr(path.c_str(), name.c_str());
} else{
return lremovexattr(path.c_str(), name.c_str());
}
#endif
#if defined(__FreeBSD__)
return extattr_delete_file(path.c_str(), EXTATTR_NAMESPACE_USER, name.c_str());

38
src/xattrs-main.cpp

@ -4,7 +4,7 @@
#if defined(__linux__)
#include <attr/xattr.h>
#define XATTR_NOFOLLOW 0
#define XATTR_NOFOLLOW 0x0001 /* Don't follow symbolic links */
#endif
#include "extattr.h"
@ -57,13 +57,13 @@ inline RawVector getxattr_raw(const std::string path, const std::string &name, i
inline int setxattr_raw(const std::string path, const std::string &name, RawVector value, int options=0) {
#if defined(__APPLE__) && defined(__MACH__)
return setxattr(path.c_str(), name.c_str(), value.begin(), value.size(), 0, options);
return setxattr(path.c_str(), name.c_str(), value.begin(), value.size(), 0, options);
#endif
#if defined(__linux__)
return setxattr(path.c_str(), name.c_str(), value.begin(), value.size(), options);
return setxattr(path.c_str(), name.c_str(), value.begin(), value.size(), options);
#endif
#if defined(__FreeBSD__)
return extattr_set_file(path.c_str(), EXTATTR_NAMESPACE_USER, name.c_str(), value.begin(), value.size());
return extattr_set_file(path.c_str(), EXTATTR_NAMESPACE_USER, name.c_str(), value.begin(), value.size());
#endif
}
@ -79,8 +79,6 @@ int rcpp_set_xattr(std::string path, std::string name, RawVector value, bool fol
int rcpp_rm_xattr(std::string path, std::string name, bool follow_symlinks=true) {
int options = 0;
if (!follow_symlinks) options = XATTR_NOFOLLOW;
/* return(removexattr(path, name, options)); */
#if defined(__APPLE__) && defined(__MACH__)
return(removexattr(path, name, options));
#endif
@ -90,8 +88,8 @@ int rcpp_rm_xattr(std::string path, std::string name, bool follow_symlinks=true)
#if defined(__FreeBSD__)
return(removexattr(path, name, options));
#endif
}
}
// [[Rcpp::export]]
@ -115,19 +113,11 @@ CharacterVector rcpp_get_xattr(const std::string path, std::string name, bool fo
std::string out = getxattr(full_path, name, options);
if (out.length()>0) return(Rcpp::wrap(out));
}
return(CharacterVector::create());
return(CharacterVector::create());
}
//' Retrieve the (raw) contents of the named xattr
//'
//' @md
//' @param path target path (file or dir); this is auto-expanded
//' @param name xattr name to retrieve
//' @param follow_symlinks if `FALSE` get xattr of the symlink vs the target it references
//' @export
//' @example inst/examples/ex1.R
// [[Rcpp::export]]
RawVector get_xattr_raw(const std::string path, std::string name, bool follow_symlinks=true) {
RawVector rcpp_get_xattr_raw(const std::string path, std::string name, bool follow_symlinks=true) {
std::string full_path = std::string(R_ExpandFileName(path.c_str()));
int options = 0;
if (!follow_symlinks) options = XATTR_NOFOLLOW;
@ -138,16 +128,9 @@ RawVector get_xattr_raw(const std::string path, std::string name, bool follow_sy
return(RawVector::create());
}
//' Retrieve the size (bytes) of the named xattr
//'
//' @md
//' @param path target path (file or dir); this is auto-expanded
//' @param name xattr name to retrieve
//' @param follow_symlinks if `FALSE` get xattr of the symlink vs the target it references
//' @export
//' @example inst/examples/ex1.R
// [[Rcpp::export]]
ssize_t get_xattr_size(const std::string path, std::string name, bool follow_symlinks=true) {
ssize_t rcpp_get_xattr_size(const std::string path, std::string name, bool follow_symlinks=true) {
std::string full_path = std::string(R_ExpandFileName(path.c_str()));
int options = 0;
if (!follow_symlinks) options = XATTR_NOFOLLOW;
@ -186,4 +169,3 @@ List rcpp_get_xattr_df(const std::string path, bool follow_symlinks=true) {
}

109
tests/testthat/test-xattrs.R

@ -1,72 +1,107 @@
context("Get / set / check / read / list / size xattrs ops work")
test_that("we can do something", {
# a target for a temporary file for testing
tf <- tempfile(fileext = ".csv")
write.csv(mtcars, tf)
# a target for a link to the temporary file
tl <- tempfile()
# no attribute set so far
expect_false(has_xattrs(tf))
expect_identical(get_xattr(tf, "is.rud.setting"), character(0))
# setting attribute using respective OS tool and reading
#
# setting attribute using respective OS tool
if(grepl("darwin", utils::sessionInfo()$platform)) {
system(paste0("xattr -w 'is.rud.setting' 'another attribute' ", tf), ignore.stdout = TRUE)
sys::exec_internal("xattr", arg = c("-w", "is.rud.setting", "another attribute", tf), error = FALSE)
}
if(grepl("linux", utils::sessionInfo()$platform)) {
sys::exec_internal("attr", arg = c("-s", "is.rud.setting", "-V", "another attribute", tf), error = FALSE)
}
# reading and deleting with internal functions
if(grepl("darwin|linux", utils::sessionInfo()$platform)) {
# check attributes set with OS tool
expect_true(has_xattrs(tf))
expect_identical(list_xattrs(tf), "is.rud.setting")
expect_identical(get_xattr(tf, "is.rud.setting"), "another attribute")
# TO BE implemented with handle_user_prefix_{param,return}
expect_equal(get_xattr_size(tf, "is.rud.setting"), 17L)
expect_identical(class(get_xattr_df(tf)), c("tbl_df", "tbl", "data.frame"))
expect_true(rm_xattr(tf, "is.rud.setting"))
# continue testing
# set and check attribute on tf
expect_true(set_xattr(tf, "is.rud.setting", "attribute value"))
expect_true(has_xattrs(tf))
# remove
expect_true(rm_xattr(tf, "is.rud.setting"))
}
#
if(grepl("linux", utils::sessionInfo()$platform)) {
system(paste0("attr -s is.rud.setting -V 'another attribute' ", tf), ignore.stdout = TRUE)
expect_true(has_xattrs(tf))
expect_identical(list_xattrs(tf), "is.rud.setting")
expect_identical(get_xattr(tf, "is.rud.setting"), "another attribute")
# TO BE implemented with handle_user_prefix_{param,return}
expect_equal(get_xattr_size(tf, "user.is.rud.setting"), 17L)
expect_identical(class(get_xattr_df(tf)), c("tbl_df", "tbl", "data.frame"))
# continue testing
expect_true(rm_xattr(tf, "is.rud.setting"))
# setting attribute using respective OS tool on symbolic link
# record in lnok if successful to inform further testing below
if(grepl("darwin", utils::sessionInfo()$platform)) {
sys::exec_internal("ln", arg = c("-s", tf, tl))
lnok <- sys::exec_internal("xattr", arg = c("-s", "-w", "is.rud.setting.ln", "another attribute", tl), error = FALSE)
}
# setting and reading attribute with internal functions
expect_true(set_xattr(tf, "is.rud.setting.a", "first attribut"))
# TO BE implemented with handle_user_prefix_{param,return}
# expect_equal(get_xattr_size(tf, "user.is.rud.setting.a"), 14L)
if(grepl("linux", utils::sessionInfo()$platform)) {
sys::exec_internal("ln", arg = c("-s", tf, tl))
lnok <- sys::exec_internal("attr", arg = c("-s", "is.rud.setting.ln", "-V", "another attribute", tl), error = FALSE)
}
# check attribute on symbolic link only
if(exists("lnok") && !length(lnok$stderr)){
# check attributes set with OS tool
expect_false(has_xattrs(tf))
expect_true(has_xattrs(tl, follow_symlinks = FALSE))
expect_false(has_xattrs(tl, follow_symlinks = TRUE))
expect_identical(list_xattrs(tl, follow_symlinks = FALSE), "is.rud.setting.ln")
expect_identical(get_xattr(tl, "is.rud.setting.ln", follow_symlinks = FALSE), "another attribute")
expect_equal(get_xattr_size(tl, "is.rud.setting.ln", follow_symlinks = FALSE), 17L)
expect_identical(class(get_xattr_df(tl, follow_symlinks = FALSE)), c("tbl_df", "tbl", "data.frame"))
expect_true(rm_xattr(tl, "is.rud.setting.ln", follow_symlinks = FALSE))
# set and check attribute on tl
expect_true(set_xattr(tl, "is.rud.setting.ln", "attribute value", follow_symlinks = FALSE))
expect_false(has_xattrs(tf))
expect_true(set_xattr(tl, "is.rud.setting.f", "attribute data", follow_symlinks = TRUE))
expect_true(has_xattrs(tl, follow_symlinks = FALSE))
expect_true(has_xattrs(tl, follow_symlinks = TRUE))
expect_identical(list_xattrs(tl, follow_symlinks = FALSE), "is.rud.setting.ln")
expect_identical(list_xattrs(tl, follow_symlinks = TRUE), "is.rud.setting.f")
expect_identical(get_xattr(tl, "is.rud.setting.ln", follow_symlinks = FALSE), "attribute value")
expect_equal(get_xattr_size(tl, "is.rud.setting.ln", follow_symlinks = FALSE), 15L)
expect_identical(class(get_xattr_df(tl, follow_symlinks = FALSE)), c("tbl_df", "tbl", "data.frame"))
#
expect_true(rm_xattr(tl, "is.rud.setting.ln", follow_symlinks = FALSE))
expect_true(rm_xattr(tl, "is.rud.setting.f", follow_symlinks = TRUE))
expect_false(has_xattrs(tf))
expect_false(has_xattrs(tl, follow_symlinks = FALSE))
# continue testing
}
# setting, reading, deleting attributes with internal functions
expect_true(set_xattr(tf, "is.rud.setting.a", "first attribut"))
expect_equal(get_xattr_size(tf, "is.rud.setting.a"), 14L)
expect_true(has_xattrs(tf))
#
expect_true(set_xattr(tf, "is.rud.setting.b", "second attribute"))
expect_equal(get_xattr(tf, "is.rud.setting.b"), "second attribute")
# TO BE implemented with handle_user_prefix_{param,return}
# expect_equal(get_xattr_size(tf, "user.is.rud.setting.b"), 16L)
# continue testing
expect_equal(get_xattr_size(tf, "is.rud.setting.b"), 16L)
#
expect_true(set_xattr(tf, "is.rud.setting.a", "first attribute"))
expect_equal(get_xattr(tf, "is.rud.setting.a"), "first attribute")
# TO BE implemented with handle_user_prefix_{param,return}
# expect_equal(get_xattr_size(tf, "user.is.rud.setting.a"), 15L)
# continue testing
expect_equal(get_xattr_size(tf, "is.rud.setting.a"), 15L)
#
expect_equal(length(list_xattrs(tf)), 2)
expect_equal(nrow(get_xattr_df(tf)), 2)
expect_true(rm_xattr(tf, "is.rud.setting.a"))
expect_false(suppressWarnings(rm_xattr(tf, "is.rud.setting.a")))
expect_equal(get_xattr(tf, "is.rud.setting.a"), character(0))
unlink(tf)
unlink(tl)
})

Loading…
Cancel
Save