/******************************************************************************/
/*                                                                            */
/*                           X r d P o s i x . c c                            */
/*                                                                            */
/* (c) 2005 by the Board of Trustees of the Leland Stanford, Jr., University  */
/*                            All Rights Reserved                             */
/*   Produced by Andrew Hanushevsky for Stanford University under 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 <http://www.gnu.org/licenses/>.        */
/*                                                                            */
/* 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.       */
/******************************************************************************/

#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <limits.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/uio.h>

#include "XrdSys/XrdSysHeaders.hh"
#include "XrdPosix/XrdPosixLinkage.hh"
#include "XrdPosix/XrdPosixXrootd.hh"
#include "XrdPosix/XrdPosixXrootdPath.hh"
#include "XrdSys/XrdSysStatx.hh"

/******************************************************************************/
/*                        G l o b a l   O b j e c t s                         */
/******************************************************************************/

       XrdPosixXrootd    Xroot;

       XrdPosixXrootPath XrootPath;

extern XrdPosixLinkage   Xunix;

/******************************************************************************/
/*                      U t i l i t y  F u n c t i o n s                      */
/******************************************************************************/

#ifdef MUSL
#include <stdio_ext.h>
#endif

static inline void fseterr(FILE *fp)
{
  /* Most systems provide FILE as a struct and the necessary bitmask in
     <stdio.h>, because they need it for implementing getc() and putc() as
     fast macros. This function is based on gnulib's fseterr.c */
#if defined _IO_ERR_SEEN || defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1
  /* GNU libc, BeOS, Haiku, Linux libc5 */
  fp->_flags |= _IO_ERR_SEEN;
#elif defined __sferror || defined __APPLE__ || defined __DragonFly__ || defined __FreeBSD__ || defined __ANDROID__
  /* FreeBSD, NetBSD, OpenBSD, DragonFly, Mac OS X, Cygwin, Minix 3, Android */
  fp->_flags |= __SERR;
#elif defined _IOERR
  /* AIX, HP-UX, IRIX, OSF/1, Solaris, OpenServer, UnixWare, mingw, MSVC, NonStop Kernel, OpenVMS */
  fp->_flag |= _IOERR;
#elif defined __UCLIBC__            /* uClibc */
  fp->__modeflags |= __FLAG_ERROR;
#elif defined MUSL /* musl libc */
  __fseterr(fp);
#else
 #error "Unsupported platform! Please report it as a bug."
#endif
}

static inline void fseteof(FILE *fp)
{
  /* Most systems provide FILE as a struct and the necessary bitmask in
     <stdio.h>, because they need it for implementing getc() and putc() as
     fast macros.  */
#if defined _IO_EOF_SEEN || defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1
  /* GNU libc, BeOS, Haiku, Linux libc5 */
  fp->_flags |= _IO_EOF_SEEN;
#elif defined __sferror || defined __APPLE__ || defined __DragonFly__ || defined __ANDROID__
  /* FreeBSD, NetBSD, OpenBSD, DragonFly, Mac OS X, Cygwin, Minix 3, Android */
  fp->_flags |= __SEOF;
#elif defined _IOEOF
  /* AIX, HP-UX, IRIX, OSF/1, Solaris, OpenServer, UnixWare, mingw, MSVC, NonStop Kernel, OpenVMS */
  fp->_flag |= _IOEOF;
#elif defined __UCLIBC__            /* uClibc */
  fp->__modeflags |= __FLAG_EOF;
#else
  (void) fseek(fp, 0L, SEEK_END);
#endif
}

/******************************************************************************/
/*                       X r d R e s o l v e L i n k                          */
/******************************************************************************/
static ssize_t XrdResolveLink(const char *path, char *resolved, size_t rsize)
{
  // Make sure a path was passed
  //
  if (!path) {
    errno = EINVAL;
    return -1;
  }

  char unref[2049], filename[2049];
  char *result = realpath(path, nullptr);

  if (result) {
    strlcpy(filename, result, sizeof(filename));
    free(result);
  } else {
    bzero(filename, sizeof(filename));
    bzero(unref, sizeof(unref));

    strlcpy(filename, path, sizeof(filename));

    // if it is a link, follow it until the end
    int i = 0;
    errno = 0;
    while (readlink(filename, unref, sizeof(unref)) > 0 && ++i < 10) {
      strlcpy(filename, unref, sizeof(filename));
      bzero(unref, sizeof(unref)); // reset unref buffer
    }
    if (i == 10) {
      errno = ELOOP;
      return -1;
    }
    // succeed only if we found a file (EINVAL returned),
    // or a link to a remote file (ENOENT returned)
    if (errno != EINVAL && errno != ENOENT)
      return -1;
  }
  size_t len = strlen(filename);
  if (len > rsize) {
    errno = ENAMETOOLONG;
    return -1;
  }
  strlcpy(resolved, filename, rsize);
  return len;
}

/******************************************************************************/
/*                       X r d P o s i x _ A c c e s s                        */
/******************************************************************************/
extern "C"
{
int XrdPosix_Access(const char *path, int amode)
{
  char *myPath, buff[2048];
  char unref[2049];

   ssize_t res=XrdResolveLink(path, unref, sizeof(unref));
   if (res > 0) {
     // Return the results of a mkdir of a Unix file system
     //
     if (!(myPath = XrootPath.URL(unref, buff, sizeof(buff))))
       return Xunix.Access( path, amode);

     // Return the results of our version of access()
     //
     return Xroot.Access(myPath, amode);
   } else {
     return res;
   }
}
}

/******************************************************************************/
/*                          X r d P o s i x _ A c l                           */
/******************************************************************************/

// This is a required addition for Solaris 10+ systems

extern "C"
{
int XrdPosix_Acl(const char *path, int cmd, int nentries, void *aclbufp)
{
  char unref[2049];

  ssize_t res=XrdResolveLink(path, unref, sizeof(unref));
  if (res < 0) return res;
  return (XrootPath.URL(unref, 0, 0)
	  ? Xunix.Acl("/tmp", cmd,nentries,aclbufp)
	  : Xunix.Acl(path,   cmd,nentries,aclbufp));
}
}

/******************************************************************************/
/*                        X r d P o s i x _ C h d i r                         */
/******************************************************************************/

extern "C"
{
int XrdPosix_Chdir(const char *path)
{
  int rc;
  char unref[2049];

  ssize_t res=XrdResolveLink(path, unref, sizeof(unref));
  if (res < 0) return res;

  // Set the working directory if the actual chdir succeeded
  //
  if (!(rc = Xunix.Chdir(path))) XrootPath.CWD(unref);
  return rc;
}
}

/******************************************************************************/
/*                        X r d P o s i x _ C l o s e                         */
/******************************************************************************/

extern "C"
{
int XrdPosix_Close(int fildes)
{

// Return result of the close
//
   return (Xroot.myFD(fildes) ? Xroot.Close(fildes) : Xunix.Close(fildes));
}
}

/******************************************************************************/
/*                     X r d P o s i x _ C l o s e d i r                      */
/******************************************************************************/

extern "C"
{
int XrdPosix_Closedir(DIR *dirp)
{

   return (Xroot.isXrootdDir(dirp) ? Xroot.Closedir(dirp)
                                   : Xunix.Closedir(dirp));
}
}

/******************************************************************************/
/*                        X r d P o s i x _ C r e a t                         */
/******************************************************************************/

extern "C"
{
int     XrdPosix_Creat(const char *path, mode_t mode)
{
   extern int XrdPosix_Open(const char *path, int oflag, ...);

   return XrdPosix_Open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
}
}

/******************************************************************************/
/*                       X r d P o s i x _ F c l o s e                        */
/******************************************************************************/

extern "C"
{
int XrdPosix_Fclose(FILE *stream)
{
   int nullfd = fileno(stream);

// Close the associated file
//
   if (Xroot.myFD(nullfd)) Xroot.Close(nullfd);

// Now close the stream
//
   return Xunix.Fclose(stream);
}
}

/******************************************************************************/
/*                        X r d P o s i x _ F c n t l                         */
/******************************************************************************/

extern "C"
{
int XrdPosix_Fcntl(int fd, int cmd, ...)
{
   va_list ap;
   void *theArg;

   if (Xroot.myFD(fd)) return 0;
   va_start(ap, cmd);
   theArg = va_arg(ap, void *);
   va_end(ap);
   return Xunix.Fcntl64(fd, cmd, theArg);
}
}

/******************************************************************************/
/*                    X r d P o s i x _ F d a t a s y n c                     */
/******************************************************************************/

extern "C"
{
int XrdPosix_Fdatasync(int fildes)
{

// Return the result of the sync
//
   return (Xroot.myFD(fildes) ? Xroot.Fsync(fildes)
                              : Xunix.Fsync(fildes));
}
}

/******************************************************************************/
/*                    X r d P o s i x _ F g e t x a t t r                     */
/******************************************************************************/

#if defined(__linux__) || defined(__GNU__) || (defined(__FreeBSD_kernel__) && defined(__GLIBC__))
extern "C"
{
ssize_t XrdPosix_Fgetxattr (int fd, const char *name, void *value, size_t size)
{
   if (Xroot.myFD(fd)) {errno = ENOTSUP; return -1;}
   return Xunix.Fgetxattr(fd, name, value, size);
}
}
#endif

/******************************************************************************/
/*                       X r d P o s i x _ F f l u s h                        */
/******************************************************************************/

extern "C"
{
int XrdPosix_Fflush(FILE *stream)
{

// Return the result of the fseek
//
   if (!stream || !Xroot.myFD(fileno(stream)))
      return Xunix.Fflush(stream);

   return Xroot.Fsync(fileno(stream));
}
}

/******************************************************************************/
/*                        X r d P o s i x _ F o p e n                         */
/******************************************************************************/

#define ISMODE(x) !strcmp(mode, x)

extern "C"
{
FILE *XrdPosix_Fopen(const char *path, const char *mode)
{
   char *myPath, buff[2048];
   int erc, fd, omode;
   FILE *stream;

  char unref[2049];

  ssize_t res=XrdResolveLink(path, unref, sizeof(unref));
  if (res < 0) return 0;

// Transfer to unix if this is not our path
//
   if (!(myPath = XrootPath.URL(unref, buff, sizeof(buff))))
      return Xunix.Fopen64(path, mode);

// Translate the mode flags
//
        if (ISMODE("r")  || ISMODE("rb"))                   omode = O_RDONLY;
   else if (ISMODE("w")  || ISMODE("wb"))                   omode = O_WRONLY
                                                        | O_CREAT | O_TRUNC;
   else if (ISMODE("a")  || ISMODE("ab"))                   omode = O_WRONLY
                                                        | O_CREAT | O_APPEND;
   else if (ISMODE("r+") || ISMODE("rb+") || ISMODE("r+b")) omode = O_RDWR;
   else if (ISMODE("w+") || ISMODE("wb+") || ISMODE("w+b")) omode = O_RDWR
                                                        | O_CREAT | O_TRUNC;
   else if (ISMODE("a+") || ISMODE("ab+") || ISMODE("a+b")) omode = O_RDWR
                                                        | O_CREAT | O_APPEND;
   else {errno = EINVAL; return 0;}

// Now open the file
//
   if ((fd = Xroot.Open(myPath, omode | XrdPosixXrootd::isStream , 0)) < 0)
      return 0;

// First obtain a free stream
//
   if (!(stream = fdopen(fd, mode)))
      {erc = errno; Xroot.Close(fd); errno = erc;}

// All done
//
   return stream;
}
}

/******************************************************************************/
/*                        X r d P o s i x _ F r e a d                         */
/******************************************************************************/

extern "C"
{
size_t XrdPosix_Fread(void *ptr, size_t size, size_t nitems, FILE *stream)
{
   ssize_t bytes;
   size_t rc = 0;
   int fd = fileno(stream);

   if (!Xroot.myFD(fd)) return Xunix.Fread(ptr, size, nitems, stream);

   bytes = Xroot.Read(fd, ptr, size*nitems);

// Get the right return code. Note that we cannot emulate the flags in sunx86
//
   if (bytes > 0 && size) rc = bytes/size;
   else if (bytes < 0) fseterr(stream);
   else                fseteof(stream);

   return rc;
}
}

/******************************************************************************/
/*                        X r d P o s i x _ F s e e k                         */
/******************************************************************************/

extern "C"
{
int XrdPosix_Fseek(FILE *stream, long offset, int whence)
{

// Return the result of the fseek
//
   if (!Xroot.myFD(fileno(stream)))
      return Xunix.Fseek( stream, offset, whence);

   return (Xroot.Lseek(fileno(stream), offset, whence) < 0 ? -1 : 0);
}
}

/******************************************************************************/
/*                       X r d P o s i x _ F s e e k o                        */
/******************************************************************************/

extern "C"
{
int XrdPosix_Fseeko(FILE *stream, long long offset, int whence)
{

// Return the result of the fseek
//
   if (!Xroot.myFD(fileno(stream)))
      return Xunix.Fseeko64(stream, offset, whence);

   return (Xroot.Lseek(fileno(stream), offset, whence) < 0 ? -1 : 0);
}
}

/******************************************************************************/
/*                        X r d P o s i x _ F s t a t                         */
/******************************************************************************/

extern "C"
{
  int XrdPosix_Fstat(int fildes, struct stat *buf)
  {
    if (Xroot.myFD(fildes)){
      return(Xroot.Fstat(fildes, buf));
    } else {
#ifdef SYS_fstat
      return syscall(SYS_fstat, fildes, buf);
#else
      errno = ENOSYS;
      return -1;
#endif
    }
  }
  int XrdPosix_FstatV(int ver, int fildes, struct stat *buf)
  {
    if (Xroot.myFD(fildes)){
      return(Xroot.Fstat(fildes, buf));
    } else {
#ifdef SYS_fstat
      return syscall(SYS_fstat, fildes, buf);
#else
      errno = ENOSYS;
      return -1;
#endif
    }
  }
}

/******************************************************************************/
/*                        X r d P o s i x _ F s t a t a t                     */
/******************************************************************************/

extern "C"
{
  int XrdPosix_Fstatat(int dirfd, const char *path, struct stat *buf, int flags)
{
  if (path && *path) {
    char buff[2048];
    char unref[2049];

    if (!(flags & AT_SYMLINK_NOFOLLOW)) {
      // We need to follow until path is no longer a link
      ssize_t res = XrdResolveLink(path, unref, sizeof(unref));
      if (res < 0) return res;
      // links are pointing to file unref now which is not a link
    } else {
      strncpy(unref, path, 2048);
    }
    if (char *myPath = XrootPath.URL(unref, buff, sizeof(buff))) {
      int ret = Xroot.Stat(myPath,  (struct stat *)buf);
      return (ret);
    } else {
      // not a root file
#ifdef SYS_newfstatat
      return syscall(SYS_newfstatat, dirfd, path, buf, flags);
#else
#ifdef SYS_fstatat
      return syscall(SYS_fstatat, dirfd, path, buf, flags);
#else
      errno = ENOSYS;
      return -1;
#endif
#endif
    }
  } else {
    errno = EFAULT;
  }
  return -1;
}
}

/******************************************************************************/
/*                        X r d P o s i x _ F s y n c                         */
/******************************************************************************/

extern "C"
{
int XrdPosix_Fsync(int fildes)
{

// Return the result of the sync
//
   return (Xroot.myFD(fildes) ? Xroot.Fsync(fildes)
                              : Xunix.Fsync(fildes));
}
}

/******************************************************************************/
/*                        X r d P o s i x _ F t e l l                         */
/******************************************************************************/

extern "C"
{
long XrdPosix_Ftell(FILE *stream)
{

// Return the result of the tell
//
   if (!Xroot.myFD(fileno(stream))) return Xunix.Ftell(stream);

   return static_cast<long>(Xroot.Lseek(fileno(stream), 0, SEEK_CUR));
}
}

/******************************************************************************/
/*                       X r d P o s i x _ F t e l l o                        */
/******************************************************************************/

extern "C"
{
long long XrdPosix_Ftello(FILE *stream)
{

// Return the result of the tell
//
   if (!Xroot.myFD(fileno(stream))) return Xunix.Ftello64(stream);

   return Xroot.Lseek(fileno(stream), 0, SEEK_CUR);
}
}

/******************************************************************************/
/*                    X r d P o s i x _ F t r u n c a t e                     */
/******************************************************************************/

extern "C"
{
int XrdPosix_Ftruncate(int fildes, long long offset)
{

// Return the result of the ftruncate
//
   return (Xroot.myFD(fildes) ? Xroot.Ftruncate  (fildes, offset)
                              : Xunix.Ftruncate64(fildes, offset));
}
}

/******************************************************************************/
/*                       X r d P o s i x _ F w r i t e                        */
/******************************************************************************/

extern "C"
{
size_t XrdPosix_Fwrite(const void *ptr, size_t size, size_t nitems, FILE *stream)
{
   size_t bytes, rc = 0;
   int fd = fileno(stream);

   if (!Xroot.myFD(fd)) return Xunix.Fwrite(ptr, size, nitems, stream);

   bytes = Xroot.Write(fd, ptr, size*nitems);

// Get the right return code.
//
   if (bytes > 0 && size) rc = bytes/size;
   else fseterr(stream);

   return rc;
}
}

/******************************************************************************/
/*                     X r d P o s i x _ G e t x a t t r                      */
/******************************************************************************/

#if defined(__linux__) || defined(__GNU__) || (defined(__FreeBSD_kernel__) && defined(__GLIBC__))
extern "C"
{
ssize_t XrdPosix_Getxattr (const char *path, const char *name, void *value, size_t size)
{
   char *myPath, buff[2048];
   char unref[2049];

   ssize_t res=XrdResolveLink(path, unref, sizeof(unref));
   if (res < 0) return res;
   if (!(myPath = XrootPath.URL(unref, buff, sizeof(buff))))
      return Xunix.Getxattr(path, name, value, size);

   return Xroot.Getxattr(myPath, name, value, size);
}
}
#endif

/******************************************************************************/
/*                    X r d P o s i x _ L g e t x a t t r                     */
/******************************************************************************/

#if defined(__linux__) || defined(__GNU__) || (defined(__FreeBSD_kernel__) && defined(__GLIBC__))
extern "C"
{
ssize_t XrdPosix_Lgetxattr (const char *path, const char *name, void *value, size_t size)
{
  char unref[2049];

  ssize_t res=XrdResolveLink(path, unref, sizeof(unref));
  if (res < 0) return res;

  if (XrootPath.URL(unref, 0, 0)) {errno = ENOTSUP; return -1;}
  return Xunix.Lgetxattr(path, name, value, size);
}
}
#endif

/******************************************************************************/
/*                        X r d P o s i x _ L s e e k                         */
/******************************************************************************/

extern "C"
{
off_t XrdPosix_Lseek(int fildes, off_t offset, int whence)
{

// Return the operation of the seek
//
   return (Xroot.myFD(fildes) ? Xroot.Lseek  (fildes, offset, whence)
                              : Xunix.Lseek64(fildes, offset, whence));
}
}

/******************************************************************************/
/*                        X r d P o s i x _ L s t a t                         */
/******************************************************************************/

extern "C"
{
int XrdPosix_Lstat(const char *path, struct stat *buf)
{
  char *myPath, buff[2048];

// Make sure a path was passed
//
  if (!path) {errno = EFAULT; return -1;}

// Return the results of an open of a Unix file
//
  myPath = XrootPath.URL(path, buff, sizeof(buff));
  if (myPath){
    return Xroot.Stat(myPath, buf);
  } else {
#ifdef SYS_lstat
    return syscall(SYS_lstat, path, buf);
#else
    errno = ENOSYS;
    return -1;
#endif
  }
}
}

/******************************************************************************/
/*                        X r d P o s i x _ M k d i r                         */
/******************************************************************************/

extern "C"
{
int XrdPosix_Mkdir(const char *path, mode_t mode)
{
   char *myPath, buff[2048];
   char unref[2049];

   ssize_t res=XrdResolveLink(path, unref, sizeof(unref));
   if (res < 0) return res;

// Return the results of a mkdir of a Unix file system
//
   if (!(myPath = XrootPath.URL(unref, buff, sizeof(buff))))
      return Xunix.Mkdir(path, mode);

// Return the results of an mkdir of an xrootd file system
//
   return Xroot.Mkdir(myPath, mode);
}
}

/******************************************************************************/
/*                         X r d P o s i x _ O p e n                          */
/******************************************************************************/

extern "C"
{
int XrdPosix_Open(const char *path, int oflag, ...)
{
   char *myPath, buff[2048];
   char unref[2049];
   va_list ap;
   int mode;
   ssize_t res=XrdResolveLink(path, unref, sizeof(unref));
   if (res < 0) return res;
   // Return the results of an open of a Unix file
   //
   if (!(myPath = XrootPath.URL(unref, buff, sizeof(buff))))
     {if (!(oflag & O_CREAT)) return Xunix.Open64(unref, oflag);
       va_start(ap, oflag);
       mode = va_arg(ap, int);
       va_end(ap);
       return Xunix.Open64(unref, oflag, (mode_t)mode);
     }

   // Return the results of an open of an xrootd file
   //
   if (!(oflag & O_CREAT)) return Xroot.Open(myPath, oflag);
   va_start(ap, oflag);
   mode = va_arg(ap, int);
   va_end(ap);
   return Xroot.Open(myPath, oflag, (mode_t)mode);
}
}

/******************************************************************************/
/*                         X r d P o s i x _ O p e n a t                          */
/******************************************************************************/

extern "C"
{
int XrdPosix_Openat(int dirfd, const char *path, int flag, ...)
{
  char *myPath, buff[2048];
  char unref[2049];
  va_list ap;
  int mode;
  ssize_t res=XrdResolveLink(path, unref, sizeof(unref));
  if (res < 0) return res;

  if (!(myPath = XrootPath.URL(unref, buff, sizeof(buff)))){
    mode_t mode = 0;
    if (flag & (O_CREAT)) {
      va_start(ap, flag);
      mode = va_arg(ap, int);
      va_end(ap);
    }
#ifdef SYS_openat
    return (syscall(SYS_openat, dirfd, path, flag, (mode_t)mode));
#else
    errno = ENOSYS;
    return -1;
#endif
  } else {
    // Return the results of an open of an xrootd file
    //
    if (!(flag & O_CREAT)) return Xroot.Open(myPath, flag);
    va_start(ap, flag);
    mode = va_arg(ap, int);
    va_end(ap);
    return Xroot.Open(myPath, flag, (mode_t)mode);
  }
}
}

/******************************************************************************/
/*                       X r d P o s i x _ O p e n d i r                      */
/******************************************************************************/

extern "C"
{
DIR* XrdPosix_Opendir(const char *path)
{
   char *myPath, buff[2048];
   char unref[2049];

   ssize_t res=XrdResolveLink(path, unref, sizeof(unref));
   if (res < 0) return NULL;

// Unix opendir
//
   if (!(myPath = XrootPath.URL(unref, buff, sizeof(buff))))
      return Xunix.Opendir(path);

// Xrootd opendir
//
   return Xroot.Opendir(myPath);
}
}

/******************************************************************************/
/*                     X r d P o s i x _ P a t h c o n f                      */
/******************************************************************************/

// This is a required addition for Solaris 10+ systems

extern "C"
{
long XrdPosix_Pathconf(const char *path, int name)
{
  char unref[2049];

  ssize_t res=XrdResolveLink(path, unref, sizeof(unref));
  if (res < 0) return res;

  return (XrootPath.URL(unref, 0, 0) ? Xunix.Pathconf("/tmp", name)
	                              : Xunix.Pathconf(unref,   name));
}
}

/******************************************************************************/
/*                        X r d P o s i x _ P r e a d                         */
/******************************************************************************/

extern "C"
{
ssize_t XrdPosix_Pread(int fildes, void *buf, size_t nbyte, off_t offset)
{

// Return the results of the read
//
   return (Xroot.myFD(fildes) ? Xroot.Pread  (fildes, buf, nbyte, offset)
                              : Xunix.Pread64(fildes, buf, nbyte, offset));
}
}

/******************************************************************************/
/*                       X r d P o s i x _ P w r i t e                        */
/******************************************************************************/

extern "C"
{
ssize_t XrdPosix_Pwrite(int fildes, const void *buf, size_t nbyte, off_t offset)
{

// Return the results of the write
//
   return (Xroot.myFD(fildes) ? Xroot.Pwrite  (fildes, buf, nbyte, offset)
                              : Xunix.Pwrite64(fildes, buf, nbyte, offset));
}
}

/******************************************************************************/
/*                         X r d P o s i x _ R e a d                          */
/******************************************************************************/

extern "C"
{
ssize_t XrdPosix_Read(int fildes, void *buf, size_t nbyte)
{

// Return the results of the read
//
   return (Xroot.myFD(fildes) ? Xroot.Read(fildes, buf, nbyte)
                              : Xunix.Read(fildes, buf, nbyte));
}
}

/******************************************************************************/
/*                        X r d P o s i x _ R e a d v                         */
/******************************************************************************/

extern "C"
{
ssize_t XrdPosix_Readv(int fildes, const struct iovec *iov, int iovcnt)
{

// Return results of the readv
//
   return (Xroot.myFD(fildes) ? Xroot.Readv(fildes, iov, iovcnt)
                              : Xunix.Readv(fildes, iov, iovcnt));
}
}

/******************************************************************************/
/*                      X r d P o s i x _ R e a d d i r                       */
/******************************************************************************/

extern "C"
{
// On some platforms both 32- and 64-bit versions are callable. so do the same
//
struct dirent   * XrdPosix_Readdir  (DIR *dirp)
{

// Return result of readdir
//
   return (Xroot.isXrootdDir(dirp) ? Xroot.Readdir(dirp)
                                   : Xunix.Readdir(dirp));
}

struct dirent64 * XrdPosix_Readdir64(DIR *dirp)
{

// Return result of readdir
//
   return (Xroot.isXrootdDir(dirp) ? Xroot.Readdir64(dirp)
                                   : Xunix.Readdir64(dirp));
}
}

/******************************************************************************/
/*                    X r d P o s i x _ R e a d d i r _ r                     */
/******************************************************************************/

extern "C"
{
int XrdPosix_Readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
{

// Return result of readdir
//
   return (Xroot.isXrootdDir(dirp) ? Xroot.Readdir_r(dirp,entry,result)
                                   : Xunix.Readdir_r(dirp,entry,result));
}

int XrdPosix_Readdir64_r(DIR *dirp, struct dirent64 *entry, struct dirent64 **result)
{

// Return result of readdir
//
   return (Xroot.isXrootdDir(dirp) ? Xroot.Readdir64_r(dirp,entry,result)
                                   : Xunix.Readdir64_r(dirp,entry,result));
}
}

/******************************************************************************/
/*                       X r d P o s i x _ R e n a m e                        */
/******************************************************************************/

extern "C"
{
int XrdPosix_Rename(const char *oldpath, const char *newpath)
{
   char *oldPath, buffold[2048], *newPath, buffnew[2048];

// Make sure a path was passed
//
   if (!oldpath || !newpath) {errno = EFAULT; return -1;}

// Return the results of a mkdir of a Unix file system
//
   if (!(oldPath = XrootPath.URL(oldpath, buffold, sizeof(buffold)))
   ||  !(newPath = XrootPath.URL(newpath, buffnew, sizeof(buffnew))))
      return Xunix.Rename(oldpath, newpath);

// Return the results of an mkdir of an xrootd file system
//
   return Xroot.Rename(oldPath, newPath);
}
}

/******************************************************************************/
/*                    X r d P o s i x _ R e w i n d d i r                     */
/******************************************************************************/

extern "C"
{
void XrdPosix_Rewinddir(DIR *dirp)
{

// Return result of rewind
//
   return (Xroot.isXrootdDir(dirp) ? Xroot.Rewinddir(dirp)
                                   : Xunix.Rewinddir(dirp));
}
}

/******************************************************************************/
/*                        X r d P o s i x _ R m d i r                         */
/******************************************************************************/

extern "C"
{
int XrdPosix_Rmdir(const char *path)
{
   char *myPath, buff[2048];

// Make sure a path was passed
//
   if (!path) {errno = EFAULT; return -1;}

// Return the results of a mkdir of a Unix file system
//
   if (!(myPath = XrootPath.URL(path, buff, sizeof(buff))))
      return Xunix.Rmdir(path);

// Return the results of an mkdir of an xrootd file system
//
   return Xroot.Rmdir(myPath);
}
}

/******************************************************************************/
/*                      X r d P o s i x _ S e e k d i r                       */
/******************************************************************************/

extern "C"
{
void XrdPosix_Seekdir(DIR *dirp, long loc)
{

// Call seekdir
//
   (Xroot.isXrootdDir(dirp) ? Xroot.Seekdir(dirp, loc)
                            : Xunix.Seekdir(dirp, loc));
}
}

/******************************************************************************/
/*                         X r d P o s i x _ S t a t                          */
/******************************************************************************/

extern "C"
{
int XrdPosix_Stat(const char *path, struct stat *buf)
{
   char buff[2048];
   char unref[2049];

   ssize_t res=XrdResolveLink(path, unref, sizeof(unref));
   if (res < 0) return res;

   // links are pointing to file unref now which is not a link
   if (char *myPath = XrootPath.URL(unref, buff, sizeof(buff))) {
     int ret = Xroot.Stat(myPath, buf);
     return (ret);
   } else {
     // not a root file
#ifdef SYS_stat
     return syscall(SYS_stat, path, buf);
#else
     errno = ENOSYS;
     return -1;
#endif
   }
}
}

extern "C"
{
int XrdPosix_Statx(int dirfd, const char *path, int flags,
                   unsigned int mask, XrdSysStatx *stx)
{
  if (path && *path) {
    char buff[2048];
    char unref[2049];

    if (!(flags & AT_SYMLINK_NOFOLLOW)) {
      // We need to follow until path is no longer a link
      ssize_t res = XrdResolveLink(path, unref, sizeof(unref));
      if (res < 0) return res;
    } else {
      strncpy(unref, path, 2048);
    }
    if (char *myPath = XrootPath.URL(unref, buff, sizeof(buff))) {
      struct stat st{};
      if (int ret = XrdPosix_Stat(myPath, &st))
        return ret;
      XrdSysStatxHelpers::Stat2Statx(st, *stx);
      return 0;
    } else {
#ifdef SYS_statx
      int ret = syscall(SYS_statx, dirfd, path, flags, mask, stx);
      return ret;
#else
      errno = ENOSYS;
      return -1;
#endif
    }
  } else {
    errno = EFAULT;
  }
  return -1;
}
}

/******************************************************************************/
/*                       X r d P o s i x _ S t a t f s                        */
/******************************************************************************/

extern "C"
{
int XrdPosix_Statfs(const char *path, struct statfs *buf)
{
   char *myPath, buff[2048];

   char unref[2049];

   ssize_t res=XrdResolveLink(path, unref, sizeof(unref));
   if (res < 0) return res;

// Return the results of an open of a Unix file
//
   return ((myPath = XrootPath.URL(unref, buff, sizeof(buff)))
          ? Xroot.Statfs(myPath, buf)
          : Xunix.Statfs64(path, (struct statfs64 *)buf));
}
}

/******************************************************************************/
/*                      X r d P o s i x _ S t a t v f s                       */
/******************************************************************************/

extern "C"
{
int XrdPosix_Statvfs(const char *path, struct statvfs *buf)
{
   char *myPath, buff[2048];
   char unref[2049];

   ssize_t res=XrdResolveLink(path, unref, sizeof(unref));
   if (res < 0) return res;

// Return the results of an open of a Unix file
//
   return ((myPath = XrootPath.URL(unref, buff, sizeof(buff)))
          ? Xroot.Statvfs(myPath, buf)
          : Xunix.Statvfs64(path, (struct statvfs64 *)buf));
}
}

/******************************************************************************/
/*                      X r d P o s i x _ T e l l d i r                       */
/******************************************************************************/

extern "C"
{
long XrdPosix_Telldir(DIR *dirp)
{

// Return result of telldir
//
   return (Xroot.isXrootdDir(dirp) ? Xroot.Telldir(dirp)
                                   : Xunix.Telldir(dirp));
}
}

/******************************************************************************/
/*                     X r d P o s i x _ T r u n c a t e                      */
/******************************************************************************/

extern "C"
{
int XrdPosix_Truncate(const char *path, off_t offset)
{
   char *myPath, buff[2048];
   char unref[2049];

   ssize_t res=XrdResolveLink(path, unref, sizeof(unref));
   if (res < 0) return res;

// Return the results of a truncate of a Unix file system
//
   if (!(myPath = XrootPath.URL(unref, buff, sizeof(buff))))
      return Xunix.Truncate64(path, offset);

// Return the results of an truncate of an xrootd file system
//
   return Xroot.Truncate(myPath, offset);
}
}

/******************************************************************************/
/*                      X r d P o s i x _ U n l i n k                         */
/******************************************************************************/

extern "C"
{
int XrdPosix_Unlink(const char *path)
{
   char *myPath, buff[2048];
   char unref[2049];

   ssize_t res=XrdResolveLink(path, unref, sizeof(unref));
   if (res < 0) return res;

// Return the result of a unlink of a Unix file
//
   if (!(myPath = XrootPath.URL(unref, buff, sizeof(buff))))
      return Xunix.Unlink(path);

// Return the results of an unlink of an xrootd file
//
   return Xroot.Unlink(myPath);
}
}

/******************************************************************************/
/*                        X r d P o s i x _ W r i t e                         */
/******************************************************************************/

extern "C"
{
ssize_t XrdPosix_Write(int fildes, const void *buf, size_t nbyte)
{

// Return the results of the write
//
   return (Xroot.myFD(fildes) ? Xroot.Write(fildes, buf, nbyte)
                              : Xunix.Write(fildes, buf, nbyte));
}
}

/******************************************************************************/
/*                       X r d P o s i x _ W r i t e v                        */
/******************************************************************************/

extern "C"
{
ssize_t XrdPosix_Writev(int fildes, const struct iovec *iov, int iovcnt)
{

// Return results of the writev
//
   return (Xroot.myFD(fildes) ? Xroot.Writev(fildes, iov, iovcnt)
                              : Xunix.Writev(fildes, iov, iovcnt));
}
}

/******************************************************************************/
/*                     X r d P o s i x _ i s M y P a t h                      */
/******************************************************************************/

int XrdPosix_isMyPath(const char *path)
{
    return (0 != XrootPath.URL(path, 0, 0));
}

/******************************************************************************/
/*                          X r d P o s i x _ U R L                           */
/******************************************************************************/

char *XrdPosix_URL(const char *path, char *buff, int blen)
{
   return XrootPath.URL(path, buff, blen);
}
