/******************************************************************************/ /* */ /* X r d F r m A d m i n C o n v e r t . c c */ /* */ /* (c) 2009 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 . */ /* */ /* 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 "XrdFrc/XrdFrcTrace.hh" #include "XrdFrc/XrdFrcUtils.hh" #include "XrdFrc/XrdFrcXAttr.hh" #include "XrdFrm/XrdFrmAdmin.hh" #include "XrdFrm/XrdFrmConfig.hh" #include "XrdFrm/XrdFrmFiles.hh" #include "XrdOss/XrdOss.hh" #include "XrdOss/XrdOssPath.hh" #include "XrdOuc/XrdOucArgs.hh" #include "XrdOuc/XrdOucNSWalk.hh" #include "XrdOuc/XrdOucPList.hh" #include "XrdOuc/XrdOucTList.hh" #include "XrdOuc/XrdOucXAttr.hh" using namespace XrdFrc; using namespace XrdFrm; /******************************************************************************/ /* C o n v e r t */ /******************************************************************************/ int XrdFrmAdmin::Convert() { static const char *cHelp = "Usage: convert [-a[utoreply]] [-fix] [-t[est]] old2new [names] [spaces]"; static XrdOucArgs Spec(&Say, "frm_admin: ", "", "autoreply", 1, "F", "fix", 3, "f", "test", 1, "l", (const char *)0); static const char *Reqs[] = {"mode", 0}; int Warn, doNames = 0, doSpaces = 0, o2n = 1; // Parse the request // if (!Parse("convert ", Spec, Reqs)) return 1; // Process the correct find // if (!strncmp(Opt.Args[0], "old2new", 4)) o2n = 1; else if (!strncmp(Opt.Args[0], "new2Old", 4)) o2n = 0; else {Emsg("Unknown conversion mode - ", Opt.Args[0]); Msg(cHelp); return 4;} // Now see what to convert // while((Opt.Args[0] = Spec.getarg())) { if (!strcmp(Opt.Args[0], "names")) doNames = 1; else if (!strcmp(Opt.Args[0], "spaces")) doSpaces= 1; else {Emsg("Unknown conversion space - ", Opt.Args[0]); Msg(cHelp); return 4; } } // Set conversion options // if (!doNames && !doSpaces) doNames = doSpaces = 1; // Check for actual conversion // if (!Opt.Local) return (o2n ? Old2New(doNames, doSpaces) : New2Old(doNames, doSpaces)); // This is only a test. See if conversion could be tried // if (!(o2n = ConvTest(doNames, doSpaces))) Msg("Conversion would fail!"); else {if ((Warn = (doNames && Config.runOld))) Msg("Remember to remove the 'oss.runmodeold' directive."); if ((Warn |= (doSpaces && Config.nonXA))) Msg("Change all occurrences of 'oss.cache' to 'oss.space'."); if (Warn) Msg("Then kill or restart all daemons before converting."); Msg("Conversion may succeed."); } return (o2n ? 0 : 8); } /******************************************************************************/ /* C o n v T e s t */ /******************************************************************************/ int XrdFrmAdmin::ConvTest(int doNames, int doSpaces) { XrdSysError *errP; XrdOucTList *tP, *pP, *pList; const char *What; char pDir[MAXPATHLEN+8]; int pdSz, rc, chkFD, Ok = 1; // Get the paths we need to try // if (doNames) {pList = x2xPaths(); What = "namespace";} else {pList = x2xSpaces(); What = "dataspace";} if (!(pP = pList)) return 0; // Turn off messages from the attribute setter // errP = XrdSysFAttr::Xat->SetMsgRoute(0); // For each path, see if it supports extended attributes // do{if (!doNames) strcpy(pDir, pP->text+pP->val); else if (!Config.LocalPath(pP->text,pDir,sizeof(pDir))) {Ok = 0; break;} pdSz = strlen(pDir); strcpy(pDir+pdSz, (doNames ? "/.\b" : ".\b")); if ((chkFD = open(pDir, O_CREAT|O_RDWR, 0740)) < 0) rc = -errno; else {rc = XrdSysFAttr::Xat->Set("XrdFrmTest", "?", 1, pDir, chkFD); close(chkFD); unlink(pDir); } *(pDir+pdSz) = 0; if (!rc) Msg("Verified ", What, " at ", pDir); else {Ok = 0; if (rc == -ENOTSUP) Msg("Extended attributes disabled for ",What," at ",pDir); else Emsg(-rc, "determine xattr status for ", pDir); } tP = pP; pP = pP->next; delete tP; } while(pP); // Cleanup // XrdSysFAttr::Xat->SetMsgRoute(errP); while(pP) {tP = pP; pP = pP->next; delete tP;} // Check if we should do a space check and return final result // return Ok & (doNames && doSpaces ? ConvTest(0, 1) : 1); } /******************************************************************************/ /* N e w 2 O l d */ /******************************************************************************/ int XrdFrmAdmin::New2Old(int doNames, int doSpaces) { Emsg("Backward conversion is not currently supported."); return 4; } /******************************************************************************/ /* O l d 2 N e w */ /******************************************************************************/ int XrdFrmAdmin::Old2New(int doNames, int doSpaces) { static const char *fMsg = "Do you want to proceed?"; XrdOucTList *tP, *pP, *pList; XrdFrmFileset *sP; XrdFrmFiles *fP; const char *What, *Only = "Only "; char Resp = 'y', pDir[MAXPATHLEN+8]; int numActs = 0, numOld = 0, ec = 0, Act = 1, fsetOpts; // Verify that we are in the correct runmode // if (Config.runOld) {Msg("The system is still configured with 'oss.runmodeold'!"); Msg("Remove the directive, kill or restart all daemons, and try again."); finalRC = 4; return 0; } // The following establishes what we are doing // fsetOpts = XrdFrmFiles::GetCpyTim | XrdFrmFiles::NoAutoDel | (doNames ? XrdFrmFiles::Recursive : 0); if (doNames) {pList = x2xPaths(); What = "namespace";} else {pList = x2xSpaces(); What = "dataspace";} // Bag out of we have no paths to convert run on old mode // if (!(pP = pList)) return 0; Config.runOld = 1; Config.runNew = 0; numProb = 0; numOld = 0; // Process each directory // do{if (!doNames) strcpy(pDir, pP->text+pP->val); else if (!Config.LocalPath(pP->text,pDir,sizeof(pDir))) {Act = 0; break;} Msg("Converting ", What, " starting at ", pDir); if (!Opt.Force && (Resp = XrdFrcUtils::Ask('y', fMsg)) == 'a') break; if (Resp == 'y') {fP = new XrdFrmFiles(pDir, fsetOpts); while(Act && (sP = fP->Get(ec,1))) {numFix = 0; Act = (doNames ? o2nFiles(sP, numOld) : o2nSpace(sP, pP->text)); if (Act && numFix) numActs++; delete sP; } delete fP; if (ec || !Act) break; } else numProb++; tP = pP; pP = pP->next; delete tP; } while(pP && !ec && Act); // Cleanup // if (pP) {Act = 0; while(pP) {tP = pP; pP = pP->next; delete tP;}} else if (ec) Act = 0; // Check for old-style spaces // if (numOld && doNames) {sprintf(pDir, "%d old-style dataspace file%s found.", numOld, (numOld == 1 ? "" : "s")); Msg(pDir); } // Print ending status here // if (!Act) Msg("Conversion aborted!"); else Only = ""; if (!numActs) Msg("No ", What ," conversions performed."); else {sprintf(pDir,"%s%d %s conversion%s performed.", Only, numActs, What, (numActs == 1 ? "" : "s")); Msg(pDir); if (numProb) Msg("Warning! ", What, " conversion is incomplete."); } // Re-establish new run mode now // Config.runOld = 0; Config.runNew = 1; // If no space conversion, check if it is really needed // if (!doSpaces && Act) {if (Config.hasCache) Msg("Please change 'oss.cache' to 'oss.space' directives."); if (numOld) Msg("You are encouraged to run 'convert old2new spaces'."); return 0; } // Check if we should and can run space conversion // if (doSpaces == 1) {if (Act && !numProb) return Old2New(0,2); Msg("Space conversion bypassed until namespace problems are fixed."); } // All done // return (Act && !numProb ? 0 : 2); } /******************************************************************************/ /* o 2 n F i l e s */ /******************************************************************************/ int XrdFrmAdmin::o2nFiles(XrdFrmFileset *sP, int &numOld) { const char *basePath = sP->basePath(); char Resp, pfnFile[1032], *linkPath; // If we have a base and a lock file, then set the copy time attribute. But // we must first validate that this is not a dangling symlink // if (sP->baseFile()) {if (sP->baseFile()->Type == XrdOucNSWalk::NSEnt::isLink) {if (Config.Verbose || !Opt.Fix) {Msg("Dangling link: ", basePath); Msg("Missing target: ", sP->baseFile()->Link); } if (Opt.Force) Resp = (Opt.Fix ? 'y' : 'n'); else if ((Resp = XrdFrcUtils::Ask('y', "Remove symlink?")) == 'a') return 0; if (Resp == 'n') {if (sP->lockFile()) {Msg("Corresponding lock file cannot be converted!"); numProb++; } } else if (!x2xRemove("symlink", basePath) || (sP->lockFile() && !x2xRemove("lock file",sP->lockPath()))) return 0; } else if (sP->lockFile()) {if (sP->cpyInfo.Set(basePath) || !x2xRemove("lock file",sP->lockPath(), 1)) return 0; } } // Remove the pin file if it exists // if (sP->pinFile() && !x2xRemove("pin file", sP->pinPath())) return 0; // Remove all mmap type of files if they exist // if (sP->xyzFile(XrdOssPath::isMmap) && !x2xRemove("mmap file", sP->xyzPath(XrdOssPath::isMmap))) return 0; if (sP->xyzFile(XrdOssPath::isMlock) && !x2xRemove("mlock file", sP->xyzPath(XrdOssPath::isMlock))) return 0; if (sP->xyzFile(XrdOssPath::isMkeep) && !x2xRemove("mkeep file", sP->xyzPath(XrdOssPath::isMkeep))) return 0; // Check if this is a symlink elsewhere // if (sP->baseFile() && (linkPath = sP->baseFile()->Link)) {if (!isXA(sP->baseFile())) {numOld++; return 1;} if (XrdSysFAttr::Xat->Set(XrdFrcXAttrPfn::Name(), basePath, strlen(basePath)+1, basePath)) {Msg("Unable to set pfn xattr for ", basePath, "->", linkPath); return 0; } strcpy(pfnFile, linkPath); strcat(pfnFile,".pfn"); return x2xRemove("pfn file", pfnFile, 1); } // All done // return 1; } /******************************************************************************/ /* o 2 n S p a c e */ /******************************************************************************/ int XrdFrmAdmin::o2nSpace(XrdFrmFileset *sP, const char *Space) { XrdFrcXAttrPfn pfnInfo; int rc, oldNP; // If we have no basefile or this is not an old-style file, skip it // if (!sP->baseFile() || sP->baseFile()->File[0] != XrdOssPath::xChar) return 1; // Get the pfn for this file // if (!XrdOssPath::genPFN(pfnInfo.Pfn, sizeof(pfnInfo.Pfn), sP->basePath())) {Msg("Unable to get pfn for ", sP->basePath()); return 0;} // Now make sure the Pfn exists and is a symlink // oldNP = numProb; if ((rc = AuditSpaceAXDC(pfnInfo.Pfn, sP->baseFile())) <= 0) return rc; numProb = oldNP; numFix = 0; // We must now set the pfn attribute as the reloc process will destroy it as // we are asking it to do a pure relocation which assumes the pfn is already set // if ((rc = XrdSysFAttr::Xat->Set(pfnInfo.Name(), pfnInfo.Pfn, pfnInfo.sizeSet(), sP->basePath())) || (rc = Config.ossFS->Reloc("admin", pfnInfo.Pfn, Space, ".")) < 0) {Emsg(-rc, "convert ", sP->basePath()); return 0;} // All went well // VSAY("Converted space for ", pfnInfo.Pfn); numFix = 1; return 1; } /******************************************************************************/ /* x 2 x P a t h s */ /******************************************************************************/ XrdOucTList *XrdFrmAdmin::x2xPaths() { extern XrdOucPListAnchor *XrdOssRPList; XrdOucTList *nP, *pP, *tP, *mypList = 0; char *Path; int Plen; // Verify that we have an RPList // if (!XrdOssRPList) {Say.Emsg("Convert", "Cannot determine paths to convert."); return 0;} // Get complete path list trimmed to prevent rescans // XrdOucPList *fP = XrdOssRPList->First(); while(fP) {Path = fP->Path(); Plen = fP->Plen(); nP = mypList, pP = 0; while(nP && Plen <= nP->val) {if (strncmp(Path, nP->text, Plen)) {pP = nP; nP = nP->next;} else {tP = nP; nP = nP->next; delete tP;} } tP = new XrdOucTList(fP->Path(), Plen, nP); if (pP) pP->next = tP; else mypList = tP; fP = fP->Next(); } // Make sure we have something here // if (!mypList) Emsg("No convertable namespaces found."); // All done // return mypList; } /******************************************************************************/ /* x 2 x R e m o v e */ /******************************************************************************/ int XrdFrmAdmin::x2xRemove(const char *Type, const char *Path, int cvt) { if (unlink(Path) && errno != ENOENT) {Emsg(errno, "remove ", Type, " ", Path); return 0;} VSAY((cvt ? "Converted " : "Removed "), Type, " ", Path); numFix++; return 1; } /******************************************************************************/ /* x 2 x S p a c e s */ /******************************************************************************/ XrdOucTList *XrdFrmAdmin::x2xSpaces() { struct XrdFrmConfig::VPInfo *nP = Config.VPList; XrdOucTList *tP, *sList = 0; char theSpace[1024+256], *tS; int pOff; // There is still an old space defined. Complain and give specific details // if (!Opt.Local && Config.nonXA) {Msg("Configuration file still defines one or more old-style spaces."); Msg("You must change all occurrences of 'oss.cache' to 'oss.space'."); Msg("Then kill or restart all daemons, and try again."); finalRC = 4; return 0; } // We will construct a list of paths that we need to scan // while(nP) {strcpy(theSpace, nP->Name); strcat(theSpace, ":"); pOff = strlen(theSpace); tS = theSpace+pOff; tP = nP->Dir; while(tP) {strcpy(tS, tP->text); sList = new XrdOucTList(theSpace, pOff, sList); tP = tP->next; } nP = nP->Next; } // Return what we have // if (!sList) Emsg("No convertable dataspaces found."); return sList; }