/******************************************************************************/
/* */
/* X r d O s s M S S . c c */
/* */
/* (C) 2003 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 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
#if defined(AIX) || defined(__solaris__)
#include
#include
#endif
#include
#include
#include
#include
#include "XrdNet/XrdNetOpts.hh"
#include "XrdNet/XrdNetSocket.hh"
#include "XrdOss/XrdOssApi.hh"
#include "XrdOss/XrdOssConfig.hh"
#include "XrdOss/XrdOssError.hh"
#include "XrdOss/XrdOssTrace.hh"
#include "XrdOuc/XrdOucProg.hh"
#include "XrdOuc/XrdOucStream.hh"
#include "XrdSys/XrdSysError.hh"
#include "XrdSys/XrdSysHeaders.hh"
#include "XrdSys/XrdSysLogger.hh"
#include "XrdSys/XrdSysPlatform.hh"
/******************************************************************************/
/* E r r o r R o u t i n g O b j e c t */
/******************************************************************************/
extern XrdSysError OssEroute;
extern XrdOucTrace OssTrace;
/******************************************************************************/
/* d e f i n e s */
/******************************************************************************/
#define NegVal(x) (x <= 0 ? (x < 0 ? x : -1) : -x)
/******************************************************************************/
/* f i l e h a n d l e */
/******************************************************************************/
/* These are private data structures. They are allocated dynamically to the
appropriate size (yes, that means dbx has a tough time).
*/
struct XrdOssHandle
{int hflag;
XrdOucStream *sp;
XrdOssHandle(int type, XrdOucStream *newsp=0) {hflag = type; sp = newsp;}
~XrdOssHandle() {if (sp) delete sp; hflag = 0;}
};
#define XRDOSS_HT_EOF 1
#define XRDOSS_HT_DIR 4
/******************************************************************************/
/* M a s s S t o r a g e R e l a t e d M e t h o d s */
/******************************************************************************/
/******************************************************************************/
/* o p e n d i r */
/******************************************************************************/
void *XrdOssSys::MSS_Opendir(const char *dir_path, int &rc) {
/*
Function: Open the directory `path' and prepare for reading.
Input: path - The fully qualified name of the directory to open.
Output: Returns a directory handle to be used for subsequent
operations. If an error occurs, (-errno) is returned.
*/
const char *epname = "MSS_Opendir";
struct XrdOssHandle *oh;
XrdOucStream *sp;
// Make sure the path is not too long.
//
if (strlen(dir_path) > MAXPATHLEN)
{OssEroute.Emsg(epname, "mss path too long - ", dir_path);
rc = -ENAMETOOLONG;
return (void *)0;
}
// Issue it now to trap any errors but defer reading the result until
// readdir() is called. This does tie up a process, sigh.
//
if ( (rc = MSS_Xeq(&sp, ENOENT, "dlist", dir_path)))
return (void *)0;
// Allocate storage for the handle and return a copy of it.
//
if (!(oh = new XrdOssHandle(XRDOSS_HT_DIR, sp)))
{delete sp; rc = -ENOMEM; return (void *)0;}
return (void *)oh;
}
/******************************************************************************/
/* r e a d d i r */
/******************************************************************************/
int XrdOssSys::MSS_Readdir(void *dir_handle, char *buff, int blen) {
/*
Function: Read the next entry if directory 'dir_handle'.
Input: dir_handle - The value returned by a successful opendir() call.
buff - Buffer to hold directory name.
blen - Size of the buffer.
Output: Upon success, places the contents of the next directory entry
in buff. When the end of the directory is encountered buff
will be set to a null string.
Upon failure, returns a (-errno).
*/
const char *epname = "MSS_Readdir";
int retc;
struct XrdOssHandle *oh = (struct XrdOssHandle *)dir_handle;
char *resp;
// Verify that the handle is correct.
//
if ( !(oh->hflag & XRDOSS_HT_DIR) )
{OssEroute.Emsg(epname, "invalid mss handle"); return -EBADF;}
// Read a record from the directory, if possible.
//
if (oh->hflag & XRDOSS_HT_EOF) *buff = '\0';
else if ((resp = oh->sp->GetLine()))
{if ( ((int)strlen(resp)) >= blen )
{*buff = '\0';
return OssEroute.Emsg(epname, -EOVERFLOW,
"readdir rmt", resp);
}
strlcpy(buff, resp, blen);
} else {
if ((retc = oh->sp->LastError())) return NegVal(retc);
else {*buff = '\0'; oh->hflag |= XRDOSS_HT_EOF;}
}
return XrdOssOK;
}
/******************************************************************************/
/* c l o s e d i r */
/******************************************************************************/
int XrdOssSys::MSS_Closedir(void *dir_handle) {
/*
Function: Close the directory associated with handle "dir_handle".
Input: dir_handle - The handle returned by opendir().
Output: Returns 0 upon success and (-errno) upon failure.
*/
const char *epname = "MSS_Closedir";
struct XrdOssHandle *oh = (struct XrdOssHandle *)dir_handle;
if ( !(oh->hflag & XRDOSS_HT_DIR) )
{OssEroute.Emsg(epname, "invalid mss handle"); return -EBADF;}
delete oh;
return XrdOssOK;
}
/******************************************************************************/
/* c r e a t e */
/******************************************************************************/
int XrdOssSys::MSS_Create(const char *path, mode_t file_mode, XrdOucEnv &env)
/*
Function: Create a file named `path' with 'file_mode' access mode bits set.
Input: path - The fully qualified name of the file to create.
file_mode - The Posix access mode bits to be assigned to the file.
These bits correspond to the standard Unix permission
bits (e.g., 744 == "rwxr--r--").
env Enviornmental information.
Output: Returns zero upon success and (-errno) otherwise.
*/
{
const char *epname = "MSS_Create";
char myMode[16];
// Make sure the path is not too long.
//
if (strlen(path) > MAXPATHLEN)
{OssEroute.Emsg(epname, "mss path too long - ", path);
return -ENAMETOOLONG;
}
// Construct the cmd to create the file. We currently don't support cosid.
//
sprintf(myMode, "%o", static_cast(file_mode));
// Create the file in in the mass store system
//
return MSS_Xeq(0, 0, "create", path, myMode);
}
/******************************************************************************/
/* s t a t */
/******************************************************************************/
/*
Function: Determine if file 'path' actually exists.
Input: path - Is the fully qualified name of the file to be tested.
buff - pointer to a 'stat' structure to hold the attributes
of the file.
Output: Returns 0 upon success and -errno upon failure.
*/
int XrdOssSys::MSS_Stat(const char *path, struct stat *buff)
{
const char *epname = "MSS_Stat";
char ftype, mtype[10], *resp;
int retc, xt_nlink;
long xt_uid, xt_gid, atime, ctime, mtime, xt_blksize, xt_blocks;
long long xt_size;
XrdOucStream *sfd;
// Make sure the path is not too long.
//
if (strlen(path) > MAXPATHLEN)
{OssEroute.Emsg(epname, "mss path too long - ", path);
return -ENAMETOOLONG;
}
// Issue the command. This may be an immediate exists a or full statx.
//
if (!buff) return MSS_Xeq(0, ENOENT, (isMSSC ? "statx" : "exists"), path);
if ((retc = MSS_Xeq(&sfd, ENOENT, "statx", path))) return retc;
// Read in the results.
//
if ( !(resp = sfd ->GetLine()))
return OssEroute.Emsg(epname,-XRDOSS_E8012,"process ",path);
// Extract data from the response.
//
sscanf(resp, "%c %9s %d %ld %ld %ld %ld %ld %lld %ld %ld", &ftype, mtype,
&xt_nlink, &xt_uid, &xt_gid, &atime, &ctime, &mtime,
&xt_size, &xt_blksize, &xt_blocks);
// Set the stat buffer, appropriately.
//
memset( (char *)buff, 0, sizeof(struct stat) );
buff->st_nlink = static_cast(xt_nlink);
buff->st_uid = static_cast(xt_uid);
buff->st_gid = static_cast(xt_gid);
buff->st_atime = static_cast(atime);
buff->st_ctime = static_cast(ctime);
buff->st_mtime = static_cast(mtime);
buff->st_size = static_cast(xt_size);
buff->st_blksize=static_cast(xt_blksize);
#ifdef __APPLE__
buff->st_blocks = xt_blocks;
#else
buff->st_blocks =static_cast(xt_blocks);
#endif
if (ftype == 'd') buff->st_mode |= S_IFDIR;
else if (ftype == 'l') buff->st_mode |= S_IFLNK;
else buff->st_mode |= S_IFREG;
buff->st_mode |= tranmode(&mtype[0]) << 6;
buff->st_mode |= tranmode(&mtype[3]) << 3;
buff->st_mode |= tranmode(&mtype[6]);
delete sfd;
return 0;
}
int XrdOssSys::tranmode(char *mode) {
int mbits = 0;
if (mode[0] == 'r') mbits |= S_IROTH;
if (mode[1] == 'w') mbits |= S_IWOTH;
if (mode[2] == 'x') mbits |= S_IXOTH;
return mbits;
}
/******************************************************************************/
/* r e m o v e */
/******************************************************************************/
/*
Function: Delete a file from the namespace and release it's data storage.
Input: path - Is the fully qualified name of the file to be removed.
Output: Returns 0 upon success and -errno upon failure.
*/
int XrdOssSys::MSS_Unlink(const char *path) {
const char *epname = "MSS_Unlink";
// Make sure the path is not too long.
//
if (strlen(path) > MAXPATHLEN)
{OssEroute.Emsg(epname, "mss path too long - ", path);
return -ENAMETOOLONG;
}
// Remove the file in Mass Store System.
//
return MSS_Xeq(0, ENOENT, "rm", path);
}
/******************************************************************************/
/* r e n a m e */
/******************************************************************************/
/*
Function: Renames a file with name 'old_name' to 'new_name'.
Input: old_name - Is the fully qualified name of the file to be renamed.
new_name - Is the fully qualified name that the file is to have.
Output: Returns 0 upon success and -errno upon failure.
*/
int XrdOssSys::MSS_Rename(const char *oldname, const char *newname) {
const char *epname = "MSS_Rename";
// Make sure the path is not too long.
//
if (strlen(oldname) > MAXPATHLEN
|| strlen(newname) > MAXPATHLEN)
{OssEroute.Emsg(epname,"mss path too long - ", oldname, newname);
return -ENAMETOOLONG;
}
// Rename the file in Mass Store System
//
return MSS_Xeq(0, 0, "mv", oldname, newname);
}
/******************************************************************************/
/* P R I V A T E S E C T I O N */
/******************************************************************************/
/******************************************************************************/
/* M S S _ X e q */
/******************************************************************************/
int XrdOssSys::MSS_Xeq(XrdOucStream **xfd, int okerr,
const char *cmd, const char *arg1, const char *arg2)
{
EPNAME("MSS_Xeq")
static int NoResp = 0;
char *resp;
int retc;
XrdOucStream *sp;
// If we have no gateway command, return an error
//
if (!RSSProg) return -XRDOSS_E8013;
// Allocate a stream for this command
//
if (!(sp = new XrdOucStream(&OssEroute)))
return OssEroute.Emsg("MSS_Xeq",-ENOMEM,"create stream for",RSSCmd);
// Run the command
//
DEBUG("Invoking '" <Run(sp, cmd, arg1, arg2)))
{delete sp; return NegVal(retc);}
// Wait for data to appear. We do this to avoid hanging up and chewing through
// all of the threads while clients retry the requests with a new connection.
//
if ((retc = sp->Wait4Data(RSSTout)))
{if (retc < 0)
{if (!(0xff & NoResp++))
OssEroute.Emsg("MSS_Xeq", -ETIMEDOUT, "execute", cmd);
retc = ETIMEDOUT;
}
delete sp; return NegVal(retc);
}
// Read back the first record. The first records must be the return code
// from the command followed by any output. Make sure that this is the case.
//
if ( !(resp = sp->GetLine()) ) retc = XRDOSS_E8023;
else
{DEBUG("received '" <