/******************************************************************************/
/* XrdFfsPosix.cc C wrapper to some of the Xrootd Posix library functions */
/* */
/* (c) 2010 by the Board of Trustees of the Leland Stanford, Jr., University */
/* All Rights Reserved */
/* Author: Wei Yang (SLAC National Accelerator Laboratory, 2009) */
/* Contract DE-AC02-76-SFO0515 with the Department of Energy */
/* */
/* This file is part of the XRootD software suite. */
/* */
/* XRootD is free software: you can redistribute it and/or modify it under */
/* the terms of the GNU Lesser General Public License as published by the */
/* Free Software Foundation, either version 3 of the License, or (at your */
/* option) any later version. */
/* */
/* XRootD is distributed in the hope that it will be useful, but WITHOUT */
/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
/* License for more details. */
/* */
/* You should have received a copy of the GNU Lesser General Public License */
/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
/* COPYING (GPL license). If not, see . */
/* */
/* The copyright holder's institutional names and contributor's names may not */
/* be used to endorse or promote products derived from this software without */
/* specific prior written permission of the institution or contributor. */
/******************************************************************************/
#define _FILE_OFFSET_BITS 64
#include
#include
#include
#include
#if !defined(__solaris__) && !defined(__FreeBSD__)
#include
#endif
#ifndef ENOATTR
#define ENOATTR ENODATA
#endif
#include
#include
#include
#include
#include
#include "XrdFfs/XrdFfsPosix.hh"
#include "XrdPosix/XrdPosixXrootd.hh"
#include "XrdFfs/XrdFfsMisc.hh"
#include "XrdFfs/XrdFfsDent.hh"
#include "XrdFfs/XrdFfsQueue.hh"
#ifdef __cplusplus
extern "C" {
#endif
#define MAXROOTURLLEN 1024 // this is also defined in other files
int XrdFfsPosix_stat(const char *path, struct stat *buf)
{
int rc;
errno = 0;
rc = XrdPosixXrootd::Stat(path, buf);
if (rc == 0 && S_ISBLK(buf->st_mode)) /* If 'buf' come from HPSS, xrootd will return it as a block device! */
{ /* So we re-mark it to a regular file */
buf->st_mode &= 0007777;
if ( buf->st_mode & S_IXUSR )
buf->st_mode |= 0040000; /* a directory */
else
buf->st_mode |= 0100000; /* a file */
}
return rc;
}
DIR *XrdFfsPosix_opendir(const char *path)
{
return XrdPosixXrootd::Opendir(path);
}
struct dirent *XrdFfsPosix_readdir(DIR *dirp)
{
return XrdPosixXrootd::Readdir(dirp);
}
int XrdFfsPosix_closedir(DIR *dirp)
{
return XrdPosixXrootd::Closedir(dirp);
}
int XrdFfsPosix_mkdir(const char *path, mode_t mode)
{
return XrdPosixXrootd::Mkdir(path, mode);
}
int XrdFfsPosix_rmdir(const char *path)
{
/* Note: Xrootd returns ENOSYS rather than ENOTEMPTY when a directory is not empty */
return XrdPosixXrootd::Rmdir(path);
}
int XrdFfsPosix_open(const char *path, int oflags, mode_t mode)
{
return XrdPosixXrootd::Open(path, oflags, mode);
}
int XrdFfsPosix_close(int fildes)
{
return XrdPosixXrootd::Close(fildes);
}
off_t XrdFfsPosix_lseek(int fildes, off_t offset, int whence)
{
return XrdPosixXrootd::Lseek(fildes, (long long)offset, whence);
}
ssize_t XrdFfsPosix_read(int fildes, void *buf, size_t nbyte)
{
return XrdPosixXrootd::Read(fildes, buf, nbyte);
}
ssize_t XrdFfsPosix_pread(int fildes, void *buf, size_t nbyte, off_t offset)
{
return XrdPosixXrootd::Pread(fildes, buf, nbyte, (long long)offset);
}
ssize_t XrdFfsPosix_write(int fildes, const void *buf, size_t nbyte)
{
return XrdPosixXrootd::Write(fildes, buf, nbyte);
}
ssize_t XrdFfsPosix_pwrite(int fildes, const void *buf, size_t nbyte, off_t offset)
{
return XrdPosixXrootd::Pwrite(fildes, buf, nbyte, (long long) offset);
}
int XrdFfsPosix_fsync(int fildes)
{
return XrdPosixXrootd::Fsync(fildes);
}
int XrdFfsPosix_unlink(const char *path)
{
return XrdPosixXrootd::Unlink(path);
}
int XrdFfsPosix_rename(const char *oldpath, const char *newpath)
{
return XrdPosixXrootd::Rename(oldpath, newpath);
}
int XrdFfsPosix_ftruncate(int fildes, off_t offset)
{
return XrdPosixXrootd::Ftruncate(fildes, offset);
}
int XrdFfsPosix_truncate(const char *path, off_t Size)
{
return XrdPosixXrootd::Truncate(path, Size);
}
long long XrdFfsPosix_getxattr(const char *path, const char *name, void *value, unsigned long long size)
{
int bufsize;
char xattrbuf[1024], nameclass[128], *namesubclass;
char *token, *key, *val;
char *lasts_xattr[256], *lasts_tokens[128];
/*
Xrootd only support two names: xroot.space and xroot.xattr. We add support of xroot.space.*
such as xroot.space.oss.cgroup etc.
*/
strncpy(nameclass, name, 11);
nameclass[11] = '\0';
if (strcmp(nameclass, "xroot.space") != 0 &&
strcmp(nameclass, "xroot.xattr") != 0 &&
strcmp(nameclass, "xroot.cksum") != 0)
{
errno = ENOATTR;
return -1;
}
bufsize = XrdPosixXrootd::Getxattr(path, nameclass, xattrbuf, size);
if (bufsize == -1) return -1;
if (strlen(name) > 11)
{
strcpy(nameclass, name);
namesubclass = &nameclass[12];
}
else /* xroot.space or xroot.xattr or xroot.cksum is provided. */
{
strcpy((char*)value, xattrbuf);
return bufsize;
}
token = strtok_r(xattrbuf, "&", lasts_xattr);
while ( token != NULL )
{
key = strtok_r(token, "=", lasts_tokens);
val = strtok_r(NULL, "=", lasts_tokens);
if (! strcmp(key, namesubclass))
{
strcpy((char*)value, val);
return strlen(val);
}
token = strtok_r(NULL, "&", lasts_xattr);
}
errno = ENOATTR;
return -1;
}
/* clean redirector cache */
void XrdFfsPosix_clear_from_rdr_cache(const char *rdrurl)
{
int fd;
fd = XrdFfsPosix_open(rdrurl, O_CREAT | O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
if ( fd != -1 )
{
XrdFfsPosix_close(fd);
XrdFfsPosix_unlink(rdrurl);
}
}
/* Posix IO functions to operation on all data servers */
struct XrdFfsPosixX_deleteall_args {
char *url;
int *res;
int *err;
mode_t st_mode;
};
void* XrdFfsPosix_x_deleteall(void *x)
{
struct XrdFfsPosixX_deleteall_args* args = (struct XrdFfsPosixX_deleteall_args*) x;
if (S_ISREG(args->st_mode))
*(args->res) = XrdFfsPosix_unlink(args->url);
else if (S_ISDIR(args->st_mode))
*(args->res) = XrdFfsPosix_rmdir(args->url);
*(args->err) = errno;
return NULL;
}
int XrdFfsPosix_deleteall(const char *rdrurl, const char *path, uid_t user_uid, mode_t st_mode)
{
int i, nurls, res;
char *newurls[XrdFfs_MAX_NUM_NODES];
int res_i[XrdFfs_MAX_NUM_NODES];
int errno_i[XrdFfs_MAX_NUM_NODES];
struct XrdFfsPosixX_deleteall_args args[XrdFfs_MAX_NUM_NODES];
struct XrdFfsQueueTasks *jobs[XrdFfs_MAX_NUM_NODES];
nurls = XrdFfsMisc_get_all_urls(rdrurl, newurls, XrdFfs_MAX_NUM_NODES);
for (i = 0; i < nurls; i++)
{
errno_i[i] = 0;
strncat(newurls[i],path, MAXROOTURLLEN - strlen(newurls[i]) -1);
XrdFfsMisc_xrd_secsss_editurl(newurls[i], user_uid, 0);
args[i].url = newurls[i];
args[i].err = &errno_i[i];
args[i].res = &res_i[i];
args[i].st_mode = st_mode;
#ifdef NOUSE_QUEUE
XrdFfsPosix_x_deleteall((void*) &args[i]);
}
#else
jobs[i] = XrdFfsQueue_create_task(XrdFfsPosix_x_deleteall, (void**)(&args[i]), 0);
}
for (i = 0; i < nurls; i++)
{
XrdFfsQueue_wait_task(jobs[i]);
XrdFfsQueue_free_task(jobs[i]);
}
#endif
res = -1;
errno = ENOENT;
for (i = 0; i < nurls; i++)
if (res_i[i] == 0)
{
res = 0;
errno = 0;
}
else if (res_i[i] != 0 && errno_i[i] == 125) // host is down
{
res = -1;
errno = ETIMEDOUT;
syslog(LOG_WARNING, "WARNING: unlink/rmdir(%s) failed (connection timeout)", newurls[i]);
break;
}
else if (res_i[i] != 0 && errno_i[i] != ENOENT)
{
res = -1;
errno = errno_i[i];
syslog(LOG_WARNING, "WARNING: unlink/rmdir(%s) failed (errno = %d)", newurls[i], errno);
break;
}
for (i = 0; i < nurls; i++)
free(newurls[i]);
return res;
}
int XrdFfsPosix_unlinkall(const char *rdrurl, const char *path, uid_t user_uid)
{
return XrdFfsPosix_deleteall(rdrurl, path, user_uid, S_IFREG);
}
int XrdFfsPosix_rmdirall(const char *rdrurl, const char *path, uid_t user_uid)
{
return XrdFfsPosix_deleteall(rdrurl, path, user_uid, S_IFDIR);
}
int XrdFfsPosix_renameall(const char *rdrurl, const char *from, const char *to, uid_t user_uid)
{
int i, nurls, res, rval = 0;
struct stat stbuf;
char fromurl[1024], tourl[1024], *newurls[XrdFfs_MAX_NUM_NODES];
nurls = XrdFfsMisc_get_all_urls(rdrurl, newurls, XrdFfs_MAX_NUM_NODES);
if (nurls < 0) rval = -1;
for (i = 0; i < nurls; i++)
{
errno = 0;
fromurl[0]='\0';
strcat(fromurl, newurls[i]);
strncat(fromurl, from, MAXROOTURLLEN - strlen(fromurl) -1);
tourl[0]='\0';
strcat(tourl, newurls[i]);
strncat(tourl, to, MAXROOTURLLEN - strlen(tourl) -1);
XrdFfsMisc_xrd_secsss_editurl(fromurl, user_uid, 0);
XrdFfsMisc_xrd_secsss_editurl(tourl, user_uid, 0);
res = (XrdFfsPosix_stat(fromurl, &stbuf));
if (res == 0)
{
/* XrdFfsPosix_rename doesn't need this protection
newdir = strdup(tourl);
newdir = dirname(newdir);
if (XrdFfsPosix_stat(newdir, &stbuf) == -1)
XrdFfsPosix_mkdir(newdir, 0777);
free(newdir);
*/
rval = XrdFfsPosix_rename(fromurl, tourl);
if (rval == -1)
{
syslog(LOG_WARNING, "WARNING: rename(%s, %s) failed (errno = %d)", fromurl, tourl, errno);
break;
}
/* if a successful rename is followed by a failed one, will return failure (and leave both old and new files) for
user to investigate. */
}
}
for (i = 0; i < nurls; i++)
free(newurls[i]);
if (rval != 0 && errno == 0) errno = EIO;
return rval;
}
int XrdFfsPosix_truncateall(const char *rdrurl, const char *path, off_t size, uid_t user_uid)
{
int i, nurls, res, rval = 0;
struct stat stbuf;
char *newurls[XrdFfs_MAX_NUM_NODES];
nurls = XrdFfsMisc_get_all_urls(rdrurl, newurls, XrdFfs_MAX_NUM_NODES);
if (nurls < 0) rval = -1;
for (i = 0; i < nurls; i++)
{
errno = 0;
strncat(newurls[i],path, MAXROOTURLLEN - strlen(newurls[i]) -1);
XrdFfsMisc_xrd_secsss_editurl(newurls[i], user_uid, 0);
res = (XrdFfsPosix_stat(newurls[i], &stbuf));
if (res == 0)
{
if (S_ISREG(stbuf.st_mode))
rval = XrdFfsPosix_truncate(newurls[i], size);
else
rval = -1;
if (rval == -1)
{
syslog(LOG_WARNING, "WARNING: (f)truncate(%s) failed (errno = %d)", newurls[i], errno);
break;
}
/* again, it will be messy if a successful truncate is followed by a failed one */
}
else if (errno != ENOENT)
rval = -1;
}
for (i = 0; i < nurls; i++)
free(newurls[i]);
if (rval != 0 && errno == 0) errno = EIO;
return rval;
}
struct XrdFfsPosixX_readdirall_args {
char *url;
int *res;
int *err;
struct XrdFfsDentnames **dents;
};
/*
It seems xrootd posix return dp[i] != NULL even if the dir
doesn't exist on a data server. XrdFfsPosix_readdir() returns
NULL in this case.
Do we need some protection here? We are not in trouble so far
because FUSE's _getattr will test the existance of the dir
so we know that at least one data server has the directory.
*/
void* XrdFfsPosix_x_readdirall(void* x)
{
struct XrdFfsPosixX_readdirall_args *args = (struct XrdFfsPosixX_readdirall_args*) x;
DIR *dp;
struct dirent *de;
/*
Xrootd's Opendir will not return NULL even under some error. For instance,
when it is supposed to return ENOENT or ENOTDIR, it actually returns
EINPROGRESS (115), and DIR *dp will not be NULL.
*/
dp = XrdFfsPosix_opendir(args->url);
if ( dp == NULL && errno != 0)
{
*(args->err) = errno;
*(args->res) = -1;
if (dp != NULL)
XrdFfsPosix_closedir(dp);
}
else
{
*(args->res) = 0;
while ((de = XrdFfsPosix_readdir(dp)) != NULL)
XrdFfsDent_names_add(args->dents, de->d_name);
XrdFfsPosix_closedir(dp);
}
return NULL;
}
int XrdFfsPosix_readdirall(const char *rdrurl, const char *path, char*** direntarray, uid_t user_uid)
{
int i, j, n, nents, nurls;
bool hasDirLock = false;
char *newurls[XrdFfs_MAX_NUM_NODES];
int res_i[XrdFfs_MAX_NUM_NODES];
int errno_i[XrdFfs_MAX_NUM_NODES];
struct XrdFfsDentnames *dir_i[XrdFfs_MAX_NUM_NODES] = {0};
struct XrdFfsPosixX_readdirall_args args[XrdFfs_MAX_NUM_NODES];
struct XrdFfsQueueTasks *jobs[XrdFfs_MAX_NUM_NODES];
// for (i = 0; i < XrdFfs_MAX_NUM_NODES; i++)
// dir_i[i] = NULL;
nurls = XrdFfsMisc_get_all_urls(rdrurl, newurls, XrdFfs_MAX_NUM_NODES);
/*
If a directory doesn't exist on any data server, it is better to return -1 with errno = ENOENT
than to return 0 with errno = 0. But this is difficult because it depends on correct returning
from XrdPosixXrootd::Opendir(). This has never been a problem for xrootdfs itself because FUSE
does stat() before readdir().
In the use case of XrdPssDir::Opendir(), it does expect this function to return -1/ENOENT. In
this use case the "rdrurl" contains the complete URL and "path" contains "" so "nurls" will be
zero if no data server has the directory. The following is a quick and dirty fix for this use
case. The orignal code was: if (nurls < 0) { errno = EACCES; return -1; }
*/
if (nurls <= 0)
{
errno = (nurls == 0? ENOENT : EACCES);
return -1;
}
for (i = 0; i < nurls; i++)
{
errno_i[i] = 0;
strncat(newurls[i], path, MAXROOTURLLEN - strlen(newurls[i]) -1);
XrdFfsMisc_xrd_secsss_editurl(newurls[i], user_uid, 0);
args[i].url = newurls[i];
args[i].err = &errno_i[i];
args[i].res = &res_i[i];
args[i].dents = &dir_i[i];
#ifdef NOUSE_QUEUE
XrdFfsPosix_x_readdirall((void*) &args[i]);
}
#else
jobs[i] = XrdFfsQueue_create_task(XrdFfsPosix_x_readdirall, (void**)(&args[i]), 0);
}
for (i = 0; i < nurls; i++)
{
XrdFfsQueue_wait_task(jobs[i]);
XrdFfsQueue_free_task(jobs[i]);
}
#endif
errno = 0;
for (i = 0; i < nurls; i++)
if (res_i[i] != 0 && errno_i[i] == 125) // when host i is down
{
errno = ETIMEDOUT;
syslog(LOG_WARNING, "WARNING: opendir(%s) failed (connection timeout)", newurls[i]);
break;
}
for (i = 0; i < nurls; i++)
free(newurls[i]);
for (i = 1; i < nurls; i++)
XrdFfsDent_names_join(&dir_i[i], &dir_i[i-1]);
char *last = NULL, **dnarraytmp;
n = XrdFfsDent_names_extract(&dir_i[nurls-1], &dnarraytmp);
*direntarray = (char **) malloc(sizeof(char*) * n);
// note that dnarraytmp[] may contain redundant entries
nents = 0;
for (i = 0; i < n; i++)
{
// put DIR_LOCK to the last one to allow rm -rf to work...
//
if (! strcmp(dnarraytmp[i], "DIR_LOCK"))
{
hasDirLock = true;
continue;
}
if (i != 0) // can be used to filter out .lock .fail, etc.
{
char *tmp, *tmp_dot;
tmp = strdup(dnarraytmp[i]);
tmp_dot = tmp + strlen(tmp) - 5;
if (! strcmp(tmp_dot, ".lock") || ! strcmp(tmp_dot, ".fail")) // filter out .lock/.fail files
{
for (j = nents - 1; j >= 0; j--)
{
tmp_dot[0] = '\0';
if (! strcmp(tmp, (*direntarray)[j]))
{
tmp_dot[0] = '.';
free(tmp);
break;
}
}
if (j >= 0) continue; // found the file cooresponding to the .lock/.fail
}
free(tmp);
}
if (last == NULL || strcmp(last, dnarraytmp[i]) != 0)
{
last = dnarraytmp[i];
(*direntarray)[nents++] = strdup(dnarraytmp[i]);
}
}
for (i = 0; i < n; i++) free(dnarraytmp[i]); // do not mergo with the above because the above loop has 'break'.
free(dnarraytmp);
/* inject this list into dent cache */
char *p;
p = strdup(path);
XrdFfsDent_cache_fill(p, direntarray, nents);
free(p);
if (hasDirLock) (*direntarray)[nents++] = strdup("DIR_LOCK");
return nents;
}
/*
struct XrdFfsPosixX_statvfsall_args, void XrdFfsPosix_x_statvfsall(), int XrdFfsPosiXrdFfsPosix_x_statvfsall() are
organized in such a way to allow using pthread if needed
*/
struct XrdFfsPosixX_statvfsall_args {
char *url;
int *res;
int *err;
struct statvfs *stbuf;
short osscgroup;
};
void* XrdFfsPosix_x_statvfsall(void *x)
{
struct XrdFfsPosixX_statvfsall_args *args = (struct XrdFfsPosixX_statvfsall_args *)x;
char xattr[256];
off_t oss_size;
long long llVal;
*(args->res) = XrdFfsPosix_getxattr(args->url, "xroot.space.oss.space", xattr, 256);
*(args->err) = errno;
sscanf((const char*)xattr, "%lld", &llVal);
oss_size = static_cast(llVal);
args->stbuf->f_blocks = (fsblkcnt_t) (oss_size / args->stbuf->f_bsize);
// sscanf((const char*)xattr, "%lld", &(args->stbuf->f_blocks));
if (*(args->res) == -1)
{
args->stbuf->f_blocks = 0;
args->stbuf->f_bavail = 0;
args->stbuf->f_bfree = 0;
return NULL;
}
*(args->res) = XrdFfsPosix_getxattr(args->url, "xroot.space.oss.free", xattr, 256);
*(args->err) = errno;
sscanf((const char*)xattr, "%lld", &llVal);
oss_size = static_cast(llVal);
args->stbuf->f_bavail = (fsblkcnt_t) (oss_size / args->stbuf->f_bsize);
// sscanf((const char*)xattr, "%lld", &(args->stbuf->f_bavail));
if (*(args->res) == -1)
{
args->stbuf->f_blocks = 0;
args->stbuf->f_bavail = 0;
args->stbuf->f_bfree = 0;
return NULL;
}
/*
The relation of the output of df and stbuf->f_blocks, f_bfree and f_bavail is
Filesystem Size Used Avail Use% Mounted on
f_blocks f_blocks - f_bfree f_bavail
In the case of querying without oss.cgroup, f_bfree = f_bavail (e.g. Used is used space by all oss.space)
In the case of querying with oss.cgroup, Used is used space by the specified oss.space (oss_size/f_bsize) and
therefore f_bfree = f_blocks - oss_size / f_bsize (e.g. Used is oss_size / f_bsize)
*/
if (args->osscgroup != 1)
args->stbuf->f_bfree = args->stbuf->f_bavail;
else
{
*(args->res) = XrdFfsPosix_getxattr(args->url, "xroot.space.oss.used", xattr, 256);
*(args->err) = errno;
sscanf((const char*)xattr, "%lld", &llVal);
oss_size = static_cast(llVal);
args->stbuf->f_bfree = args->stbuf->f_blocks - (fsblkcnt_t) (oss_size / args->stbuf->f_bsize);
// args->stbuf->f_bfree = args->stbuf->f_blocks - oss_size;
}
return NULL;
}
int XrdFfsPosix_statvfsall(const char *rdrurl, const char *path, struct statvfs *stbuf, uid_t user_uid)
{
int i, nurls;
short osscgroup;
char *newurls[XrdFfs_MAX_NUM_NODES];
int res_i[XrdFfs_MAX_NUM_NODES];
int errno_i[XrdFfs_MAX_NUM_NODES];
struct statvfs stbuf_i[XrdFfs_MAX_NUM_NODES];
struct XrdFfsPosixX_statvfsall_args args[XrdFfs_MAX_NUM_NODES];
struct XrdFfsQueueTasks *jobs[XrdFfs_MAX_NUM_NODES];
nurls = XrdFfsMisc_get_all_urls(rdrurl, newurls, XrdFfs_MAX_NUM_NODES);
if (nurls < 0)
{
errno = EACCES;
return -1;
}
if (strstr(path, "oss.cgroup") != NULL)
osscgroup = 1;
else
osscgroup = 0;
for (i = 0; i < nurls; i++)
{
strncat(newurls[i], path, MAXROOTURLLEN - strlen(newurls[i]) -1);
// XrdFfsMisc_xrd_secsss_editurl(newurls[i], user_uid);
args[i].url = newurls[i];
args[i].res = &res_i[i];
args[i].err = &errno_i[i];
stbuf_i[i].f_bsize = stbuf->f_bsize;
args[i].stbuf = &(stbuf_i[i]);
args[i].osscgroup = osscgroup;
#ifdef NOUSE_QUEUE
XrdFfsPosix_x_statvfsall((void*) &args[i]);
}
#else
jobs[i] = XrdFfsQueue_create_task(XrdFfsPosix_x_statvfsall, (void**)(&args[i]), 0);
}
for (i = 0; i < nurls; i++)
{
XrdFfsQueue_wait_task(jobs[i]);
XrdFfsQueue_free_task(jobs[i]);
}
#endif
/*
for statfs call, we don't care about return code and errno
*/
stbuf->f_blocks = 0;
stbuf->f_bfree = 0;
stbuf->f_bavail = 0;
for (i = 0; i < nurls; i++)
{
stbuf->f_blocks += args[i].stbuf->f_blocks;
stbuf->f_bavail += args[i].stbuf->f_bavail;
stbuf->f_bfree += args[i].stbuf->f_bfree;
}
for (i = 0; i < nurls; i++)
free(newurls[i]);
return 0;
}
/* XrdFfsPosiXrdFfsPosix_x_statall() */
struct XrdFfsPosixX_statall_args {
char *url;
int *res;
int *err;
struct stat *stbuf;
};
void* XrdFfsPosix_x_statall(void *x)
{
struct XrdFfsPosixX_statall_args *args = (struct XrdFfsPosixX_statall_args *)x;
*(args->res) = XrdFfsPosix_stat(args->url, args->stbuf);
*(args->err) = errno;
return (void *)0;
}
int XrdFfsPosix_statall(const char *rdrurl, const char *path, struct stat *stbuf, uid_t user_uid)
{
int i, res, nurls;
char *newurls[XrdFfs_MAX_NUM_NODES];
int res_i[XrdFfs_MAX_NUM_NODES];
int errno_i[XrdFfs_MAX_NUM_NODES];
struct stat stbuf_i[XrdFfs_MAX_NUM_NODES];
struct XrdFfsPosixX_statall_args args[XrdFfs_MAX_NUM_NODES];
struct XrdFfsQueueTasks *jobs[XrdFfs_MAX_NUM_NODES];
char *p1, *p2, *dir, *file, rootpath[MAXROOTURLLEN];
rootpath[0] = '\0';
strncat(rootpath,rdrurl, MAXROOTURLLEN - strlen(rootpath) -1);
strncat(rootpath,path, MAXROOTURLLEN - strlen(rootpath) -1);
p1 = strdup(path);
p2 = strdup(path);
dir = dirname(p1);
file = basename(p2);
// if task queue is too long, or if the stat() is from an ls -l command, the stat() against redirector
if ( XrdFfsQueue_count_tasks()/XrdFfsMisc_get_number_of_data_servers() > 20 || XrdFfsDent_cache_search(dir, file))
{
XrdFfsMisc_xrd_secsss_editurl(rootpath, user_uid, 0);
res = XrdFfsPosix_stat(rootpath, stbuf);
// maybe a data server is down since the last _readdir()? in that case, continue
// we also saw a case where redirectors report the file exist but meta redirector report
// that the file doesn't exist, and we need to continue at here
if (res == 0)
{
free(p1);
free(p2);
return 0;
}
}
free(p1);
free(p2);
nurls = XrdFfsMisc_get_all_urls(rdrurl, newurls, XrdFfs_MAX_NUM_NODES);
for (i = 0; i < nurls; i++)
{
strncat(newurls[i], path, MAXROOTURLLEN - strlen(path) -1);
XrdFfsMisc_xrd_secsss_editurl(newurls[i], user_uid, 0);
args[i].url = newurls[i];
args[i].res = &res_i[i];
args[i].err = &errno_i[i];
args[i].stbuf = &(stbuf_i[i]);
#ifdef NOUSE_QUEUE
XrdFfsPosix_x_statall((void*) &args[i]);
}
#else
jobs[i] = XrdFfsQueue_create_task(XrdFfsPosix_x_statall, (void**)(&args[i]), 0);
}
for (i = 0; i < nurls; i++)
{
XrdFfsQueue_wait_task(jobs[i]);
XrdFfsQueue_free_task(jobs[i]);
}
#endif
res = -1;
errno = ENOENT;
for (i = 0; i < nurls; i++)
if (res_i[i] == 0)
{
res = 0;
errno = 0;
memcpy((void*)stbuf, (void*)(&stbuf_i[i]), sizeof(struct stat));
break;
}
else if (res_i[i] != 0 && errno_i[i] == 125) // when host i is down
{
res = -1;
errno = ETIMEDOUT;
syslog(LOG_WARNING, "WARNING: stat(%s) failed (connection timeout)", newurls[i]);
}
for (i = 0; i < nurls; i++)
free(newurls[i]);
return res;
}
#ifdef __cplusplus
}
#endif