/******************************************************************************/ /* */ /* X r d D i g F S . c c */ /* */ /* (c) 2013 by the Board of Trustees of the Leland Stanford, Jr., University */ /* Produced by Andrew Hanushevsky for Stanford University under contract */ /* DE-AC02-76-SFO0515 with the Deprtment 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. */ /******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include "XrdVersion.hh" #include "XrdOuc/XrdOucStream.hh" #include "XrdSys/XrdSysError.hh" #include "XrdSys/XrdSysHeaders.hh" #include "XrdSys/XrdSysLogger.hh" #include "XrdSys/XrdSysPthread.hh" #include "XrdSec/XrdSecInterface.hh" #include "XrdSfs/XrdSfsAio.hh" #include "XrdDig/XrdDigConfig.hh" #include "XrdDig/XrdDigFS.hh" #ifdef AIX #include #endif /******************************************************************************/ /* L o c a l D e f i n e s */ /******************************************************************************/ #ifdef __linux__ #define IS_PROC(x) !strncmp(x+SFS_LCLPLEN, "proc", 4) \ && (*(x+SFS_LCLPLEN+4) == '\0'||*(x+SFS_LCLPLEN+4) == '/') #endif /******************************************************************************/ /* G l o b a l O b j e c t s */ /******************************************************************************/ namespace XrdDig { XrdSysError *eDest; extern XrdDigConfig Config; }; using namespace XrdDig; /******************************************************************************/ /* U n i x F i l e S y s t e m I n t e r f a c e */ /******************************************************************************/ class XrdDigUFS { public: static int Close(int fd) {return close(fd);} static int Open(const char *path, int oflag) {return open(path, oflag);} static int Statfd(int fd, struct stat *buf) {return fstat(fd, buf);} static int Statfn(const char *fn, struct stat *buf) {return stat(fn, buf);} static int Statlk(const char *fn, struct stat *buf) {return lstat(fn, buf);} }; /******************************************************************************/ /* X r d D i g G e t F S */ /******************************************************************************/ XrdSfsFileSystem *XrdDigGetFS(XrdSfsFileSystem *native_fs, XrdSysLogger *lp, const char *cFN, const char *parms) { static XrdSysError Eroute(lp, "XrdDig"); static XrdDigFS myFS; bool isOK; Eroute.Say("Copr. 2013 Stanford University/SLAC dig file system"); eDest = &Eroute; // Configure // eDest->Say("++++++ DigFS initialization started."); isOK = Config.Configure(cFN, parms); eDest->Say("------ DigFS initialization ",(isOK ? "completed." : "failed.")); // All done // return (isOK ? &myFS : 0); } /******************************************************************************/ /* D i r e c t o r y O b j e c t I n t e r f a c e s */ /******************************************************************************/ /******************************************************************************/ /* o p e n */ /******************************************************************************/ int XrdDigDirectory::open(const char *dir_path, // In const XrdSecClientName *client, // In const char *info) // In /* Function: Open the directory `path' and prepare for reading. Input: path - The fully qualified name of the directory to open. cred - Authentication credentials, if any. info - Opaque information, if any. Output: Returns SFS_OK upon success, otherwise SFS_ERROR. */ { static const char *epname = "opendir"; int retc; // Verify that this object is not already associated with an open directory // if (dh || isBase) return XrdDigFS::Emsg(epname, error, EADDRINUSE, "open directory", dir_path); // Check if we are trying to open the root to list it // if (!strcmp(dir_path, SFS_LCLPRFX) || !strcmp(dir_path, SFS_LCLPRFY)) {isBase = true; if ((dirFD = Config.GenAccess(client, dirent_full.aEnt, aESZ)) < 0) return XrdDigFS::Emsg(epname,error,EACCES,"open directory",dir_path); ateof = dirFD == 0; return SFS_OK; } // Authorize this open and get actual file name to open // if ( (retc = XrdDigFS::Validate(dir_path)) || !(fname = Config.GenPath(retc, client, "opendir", dir_path+SFS_LCLPLEN, XrdDigConfig::isDir))) return XrdDigFS::Emsg(epname,error,retc,"open directory",dir_path); // Set up values for this directory object // ateof = false; // Open the directory and get it's id // if (!(dh = opendir(fname))) {if (fname) {free(fname); fname = 0;} return XrdDigFS::Emsg(epname,error,errno,"open directory",dir_path); } // Check if this is a reference to /proc (Linux only) // #ifdef __linux__ if (IS_PROC(dir_path)) {noTag = *(dir_path+SFS_LCLPLEN+4) == 0 || !strcmp(dir_path+SFS_LCLPLEN+4, "/"); isProc = true; dirFD = dirfd(dh); } #endif // All done // return SFS_OK; } /******************************************************************************/ /* n e x t E n t r y */ /******************************************************************************/ const char *XrdDigDirectory::nextEntry() /* Function: Read the next directory entry. Input: None. Output: Upon success, returns the contents of the next directory entry as a null terminated string. Returns a null pointer upon EOF or an error. To differentiate the two cases, getErrorInfo will return 0 upon EOF and an actual error code (i.e., not 0) on error. */ { static const char *epname = "nextEntry"; static const int wMask = ~(S_IWUSR | S_IWGRP | S_IWOTH); struct dirent *rp; int retc; // Check for base listing // if (isBase) {if (dirFD > 0) return dirent_full.aEnt[--dirFD]; ateof = true; return (const char *)0; } // Lock the direcrtory and do any required tracing // if (!dh) {XrdDigFS::Emsg(epname,error,EBADF,"read directory",fname); return (const char *)0; } // Check if we are at EOF (once there we stay there) // if (ateof) return (const char *)0; // Read the next directory entry // #ifdef __linux__ do{errno = 0; rp = readdir(dh); if (!rp) {if (!(retc = errno)) {ateof = 1; error.clear();} else XrdDigFS::Emsg(epname,error,retc,"read directory",fname); d_pnt->d_name[0] = '\0'; return (const char *)0; } #else do{if ((retc = readdir_r(dh, d_pnt, &rp))) {XrdDigFS::Emsg(epname,error,retc,"read directory",fname); d_pnt->d_name[0] = '\0'; return (const char *)0; } // Check if we have reached end of file // if (!rp || !d_pnt->d_name[0]) {ateof = true; error.clear(); return (const char *)0; } #endif // If autostat wanted, do so here // if (sBuff) { #ifdef HAVE_FSTATAT int sFlags = (isProc ? AT_SYMLINK_NOFOLLOW : 0); if (fstatat(dirFD, rp->d_name, sBuff, sFlags)) continue; sBuff->st_mode = (sBuff->st_mode & wMask) | S_IRUSR; #else char dPath[2048]; snprintf(dPath, sizeof(dPath), "%s%s", fname, rp->d_name); if (stat(dPath, sBuff)) continue; sBuff->st_mode = (sBuff->st_mode & wMask) | S_IRUSR; #endif } // We want to extend the directory entry information with symlink information // if this is a symlink. This is only done for /proc (Linux only) // #ifdef __linux__ if (isProc) {struct stat Stat, *sP = (sBuff ? sBuff : &Stat); char *dP; int n, rc; rc = (sBuff ? 0:fstatat(dirFD,rp->d_name,&Stat,AT_SYMLINK_NOFOLLOW)); if (!rc && !noTag && S_ISLNK(sP->st_mode)) {n = strlen(rp->d_name); dP = rp->d_name + n + 4; n = sizeof(dirent_full.nbf) - (n + 8); if ((n = readlinkat(dirFD,rp->d_name,dP,n)) < 0) strcpy(dP,"?"); else *(dP+n) = 0; memcpy(dP-4, " -> ", 4); } } #endif // Return the actual entry // return (const char *)(rp->d_name); } while(1); return 0; // Keep compiler happy } /******************************************************************************/ /* c l o s e */ /******************************************************************************/ int XrdDigDirectory::close() /* Function: Close the directory object. Input: cred - Authentication credentials, if any. Output: Returns SFS_OK upon success and SFS_ERROR upon failure. */ { static const char *epname = "closedir"; // Release the handle // sBuff = 0; if (dh && closedir(dh)) {XrdDigFS::Emsg(epname, error, errno, "close directory", fname); return SFS_ERROR; } // Do some clean-up // if (fname) {free(fname); fname = 0;} dh = (DIR *)0; isProc = isBase = false; return SFS_OK; } /******************************************************************************/ /* F i l e O b j e c t I n t e r f a c e s */ /******************************************************************************/ /******************************************************************************/ /* o p e n */ /******************************************************************************/ int XrdDigFile::open(const char *path, // In XrdSfsFileOpenMode open_mode, // In mode_t Mode, // In const XrdSecClientName *client, // In const char *info) // In /* Function: Open the file `path' in the mode indicated by `open_mode'. Input: path - The fully qualified name of the file to open. open_mode - One of the following flag values: SFS_O_RDONLY - Open file for reading (only allowed) Mode - Ignored. client - Authentication credentials, if any. info - Opaque information to be used as seen fit. Output: Returns SFS_OK upon success, otherwise SFS_ERROR is returned. */ { static const char *epname = "open"; int retc; struct stat buf; // Verify that this object is not already associated with an open file // if (oh >= 0) return XrdDigFS::Emsg(epname,error,EADDRINUSE,"open file",path); // Allow only opens in readonly mode // open_mode &= (SFS_O_RDONLY | SFS_O_WRONLY | SFS_O_RDWR | SFS_O_CREAT); if (open_mode && open_mode != SFS_O_RDONLY) return XrdDigFS::Emsg(epname,error,EROFS,"open file",path); // Authorize this open and get actual file name to open // if ( (retc = XrdDigFS::Validate(path)) || !(fname = Config.GenPath(retc, client, "open", path+SFS_LCLPLEN, XrdDigConfig::isFile))) return XrdDigFS::Emsg(epname,error,retc,"open file",path); // Prohibit opening of a symlink in /proc (linux only) and memory // #ifdef __linux__ if (IS_PROC(path)) {struct stat Stat; if (XrdDigUFS::Statlk(fname, &Stat)) retc = errno; else if (!S_ISREG(Stat.st_mode)) retc = EPERM; else retc = 0; if (!retc && strstr(fname, "/mem")) retc = EACCES; if (retc) {free(fname); return XrdDigFS::Emsg(epname, error, retc, "open proc file", path); } isProc = true; } #endif // Open the file and make sure it is a file // if ((oh = XrdDigUFS::Open(fname, O_RDONLY)) >= 0) {do {retc = XrdDigUFS::Statfd(oh, &buf);} while(retc && errno == EINTR); if (!retc && !(buf.st_mode & S_IFREG)) {XrdDigUFS::Close(oh); oh = (buf.st_mode & S_IFDIR ? -EISDIR : -ENOTBLK); } } else oh = -errno; // All done. // if (oh >= 0) return SFS_OK; if (fname) {free(fname); fname = 0;} return XrdDigFS::Emsg(epname,error,oh,"open file",path); } /******************************************************************************/ /* c l o s e */ /******************************************************************************/ int XrdDigFile::close() /* Function: Close the file object. Input: None Output: Returns SFS_OK upon success and SFS_ERROR upon failure. */ { static const char *epname = "close"; // Release the handle and return // if (oh >= 0 && XrdDigUFS::Close(oh)) return XrdDigFS::Emsg(epname, error, errno, "close", fname); oh = -1; if (fname) {free(fname); fname = 0;} return SFS_OK; } /******************************************************************************/ /* f c t l */ /******************************************************************************/ int XrdDigFile::fctl(const int cmd, const char *args, XrdOucErrInfo &out_error) { // See if we can do this // if (cmd == SFS_FCTL_GETFD) {out_error.setErrCode(isProc ? -1 : oh); return SFS_OK; } // We don't support this // out_error.setErrInfo(EEXIST, "fctl operation not supported"); return SFS_ERROR; } /******************************************************************************/ /* r e a d */ /******************************************************************************/ XrdSfsXferSize XrdDigFile::read(XrdSfsFileOffset offset, // In char *buff, // Out XrdSfsXferSize blen) // In /* Function: Read `blen' bytes at `offset' into 'buff' and return the actual number of bytes read. Input: offset - The absolute byte offset at which to start the read. buff - Address of the buffer in which to place the data. blen - The size of the buffer. This is the maximum number of bytes that will be read from 'fd'. Output: Returns the number of bytes read upon success and SFS_ERROR o/w. */ { static const char *epname = "read"; XrdSfsXferSize nbytes; // Make sure the offset is not too large // #if _FILE_OFFSET_BITS!=64 if (offset > 0x000000007fffffff) return XrdDigFS::Emsg(epname, error, EFBIG, "read", fname); #endif // Read the actual number of bytes // do { nbytes = pread(oh, (void *)buff, (size_t)blen, (off_t)offset); } while(nbytes < 0 && errno == EINTR); if (nbytes < 0) return XrdDigFS::Emsg(epname, error, errno, "read", fname); // Return number of bytes read // return nbytes; } /******************************************************************************/ /* r e a d v */ /******************************************************************************/ XrdSfsXferSize XrdDigFile::readv(XrdOucIOVec *readV, // In int readCount) // In /* Function: Perform all the reads specified in the readV vector. Input: readV - A description of the reads to perform; includes the absolute offset, the size of the read, and the buffer to place the data into. readCount - The size of the readV vector. Output: Returns the number of bytes read upon success and SFS_ERROR o/w. If the number of bytes read is less than requested, it is considered an error. */ { static const char *epname = "readv"; XrdSfsXferSize nbytes = 0; ssize_t curCount; int i; for (i=0; i 0) errno = ESPIPE; return XrdDigFS::Emsg(epname, error, errno, "readv", fname); } nbytes += curCount; } return nbytes; } /******************************************************************************/ /* r e a d A I O */ /******************************************************************************/ int XrdDigFile::read(XrdSfsAio *aiop) { // Execute this request in a synchronous fashion // aiop->Result = this->read((XrdSfsFileOffset)aiop->sfsAio.aio_offset, (char *)aiop->sfsAio.aio_buf, (XrdSfsXferSize)aiop->sfsAio.aio_nbytes); aiop->doneRead(); return 0; } /******************************************************************************/ /* s t a t */ /******************************************************************************/ int XrdDigFile::stat(struct stat *buf) // Out /* Function: Return file status information Input: buf - The stat structiure to hold the results Output: Returns SFS_OK upon success and SFS_ERROR upon failure. */ { static const char *epname = "stat"; static const int wMask = ~(S_IWUSR | S_IWGRP | S_IWOTH); // Execute the function // if (XrdDigUFS::Statfd(oh, buf)) return XrdDigFS::Emsg(epname, error, errno, "stat", fname); // Fixup size when stat is issued into /proc (linux only would set isProc) // if (isProc && !buf->st_size && S_ISREG(buf->st_mode)) buf->st_size = 1048576; // Turn off write bits in the mode // buf->st_mode &= wMask; // All went well // return SFS_OK; } /******************************************************************************/ /* F i l e S y s t e m O b j e c t I n t e r f a c e s */ /******************************************************************************/ /******************************************************************************/ /* E m s g */ /******************************************************************************/ int XrdDigFS::Emsg(const char *pfx, // Message prefix value XrdOucErrInfo &einfo, // Place to put text & error code int ecode, // The error code const char *op, // Operation being performed const char *target) // The target (e.g., fname) { char *etext, buffer[MAXPATHLEN+80], unkbuff[64]; // Get the reason for the error // if (ecode < 0) ecode = -ecode; if (!(etext = strerror(ecode))) {sprintf(unkbuff, "reason unknown (%d)", ecode); etext = unkbuff;} // Format the error message // snprintf(buffer,sizeof(buffer),"Unable to %s %s; %s", op, target, etext); // Print it out if debugging is enabled // #ifndef NODEBUG eDest->Emsg(pfx, buffer); #endif // Place the error message in the error object and return // einfo.setErrInfo(ecode, buffer); return SFS_ERROR; } /******************************************************************************/ /* e x i s t s */ /******************************************************************************/ int XrdDigFS::exists(const char *path, // In XrdSfsFileExistence &file_exists, // Out XrdOucErrInfo &error, // Out const XrdSecClientName *client, // In const char *info) // In /* Function: Determine if file 'path' actually exists. Input: path - Is the fully qualified name of the file to be tested. file_exists - Is the address of the variable to hold the status of 'path' when success is returned. The values may be: XrdSfsFileExistsIsDirectory - file not found but path is valid. XrdSfsFileExistsIsFile - file found. XrdSfsFileExistsIsNo - neither file nor directory. einfo - Error information object holding the details. client - Authentication credentials, if any. info - Opaque information, if any. Output: Returns SFS_OK upon success and SFS_ERROR upon failure. Notes: When failure occurs, 'file_exists' is not modified. */ { static const char *epname = "exists"; struct stat fstat; // Now try to find the file or directory // if (!XrdDigUFS::Statfn(path, &fstat)) { if (S_ISDIR(fstat.st_mode)) file_exists=XrdSfsFileExistIsDirectory; else if (S_ISREG(fstat.st_mode)) file_exists=XrdSfsFileExistIsFile; else file_exists=XrdSfsFileExistNo; return SFS_OK; } if (errno == ENOENT) {file_exists=XrdSfsFileExistNo; return SFS_OK; } // An error occured, return the error info // return XrdDigFS::Emsg(epname, error, errno, "locate", path); } /******************************************************************************/ /* f s c t l */ /******************************************************************************/ int XrdDigFS::fsctl(const int cmd, const char *args, XrdOucErrInfo &eInfo, const XrdSecClientName *client) /* Function: Perform filesystem operations: Input: cmd - Operation command (currently supported): SFS_FSCTL_LOCATE - locate file (always local) arg - Command dependent argument: - Locate: The path whose location is wanted eInfo - Error/Response information structure. client - Authentication credentials, if any. Output: Returns SFS_OK upon success and SFS_ERROR upon failure. */ { // Process the LOCATE request. We only support "*/path" type of requests and // if valid, we return ourselves as the location. Security is not applied here. // if ((cmd & SFS_FSCTL_CMD) == SFS_FSCTL_LOCATE) {if ((*args == '*' && !(SFS_LCLROOT(args+1))) || (*args == '/' && !(SFS_LCLROOT(args )))) {eInfo.setErrInfo(EINVAL, "Invalid locate path"); return SFS_ERROR; } Config.GetLocResp(eInfo, (cmd & SFS_O_HNAME)); return SFS_DATA; } // We don't support anything else // eInfo.setErrInfo(ENOTSUP, "Operation not supported."); return SFS_ERROR; } /******************************************************************************/ /* g e t V e r s i o n */ /******************************************************************************/ const char *XrdDigFS::getVersion() {return XrdVERSION;} /******************************************************************************/ /* Private: R e j e c t */ /******************************************************************************/ int XrdDigFS::Reject(const char *op, const char *trg, XrdOucErrInfo &eInfo) { return XrdDigFS::Emsg("Inspect", eInfo, EROFS, op, trg); } /******************************************************************************/ /* s t a t */ /******************************************************************************/ int XrdDigFS::stat(const char *path, // In struct stat *buf, // Out XrdOucErrInfo &error, // Out const XrdSecClientName *client, // In const char *info) // In /* Function: Get info on 'path'. Input: path - Is the fully qualified name of the file to be tested. buf - The stat structiure to hold the results error - Error information object holding the details. client - Authentication credentials, if any. info - Opaque information, if any. Output: Returns SFS_OK upon success and SFS_ERROR upon failure. */ { static const char *epname = "stat"; static const int wMask = ~(S_IWUSR | S_IWGRP | S_IWOTH); char *fname; int retc; // Check if we are trying to stat the root // if (!strcmp(path, SFS_LCLPRFX) || !strcmp(path, SFS_LCLPRFY)) {const char *auth; if (Config.GenAccess(client, &auth, 1) < 0) return XrdDigFS::Emsg(epname,error,EACCES,"stat directory",path); XrdDigConfig::StatRoot(buf); return SFS_OK; } // Authorize and get the correct fname to stat // if ((retc = Validate(path)) || !(fname = Config.GenPath(retc, client, "stat", path+SFS_LCLPLEN))) return XrdDigFS::Emsg(epname,error,retc,"stat",path); // Fixup filename when stat is issued into /proc (linux only) // #ifdef __linux__ char *myLink; if ((myLink = strstr(fname, " -> "))) *myLink = 0; #endif // Execute the function // if (XrdDigUFS::Statfn(fname, buf)) {retc = errno; free(fname); return XrdDigFS::Emsg(epname, error, retc, "stat", path); } // Turn off write bits in the mode // buf->st_mode &= wMask; // All went well // free(fname); return SFS_OK; } /******************************************************************************/ /* V a l i d a t e */ /******************************************************************************/ int XrdDigFS::Validate(const char *path) { // Make sure this is our path and is legal // return (SFS_LCLPATH(path) && *(path+SFS_LCLPLEN) ? 0 : EPERM); }