Merge branch 'feature-symlinks' into 'develop'

ENH: OSspecific - softlink handling (fixes #164)

Links are followed in most cases, with some notable exceptions:

- mv, mvBak:
  renames the link, not the underlying file/directory

- rmDir:
  remove the symlink to a directory, does not recurse into the
  underlying directory

See merge request !51
This commit is contained in:
Andrew Heather
2016-07-26 15:25:33 +01:00
7 changed files with 229 additions and 55 deletions

View File

@ -3,7 +3,7 @@
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2011 OpenFOAM Foundation \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
\\/ M anipulation | \\/ M anipulation | Copyright (C) 2016 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -34,6 +34,7 @@ Description
#include "IOobject.H" #include "IOobject.H"
#include "IOstreams.H" #include "IOstreams.H"
#include "OSspecific.H" #include "OSspecific.H"
#include "POSIX.H"
using namespace Foam; using namespace Foam;
@ -99,6 +100,98 @@ int main()
} }
// Test some copying and deletion
{
const fileName dirA("dirA");
const fileName lnA("lnA");
const fileName lnB("lnB");
const fileName dirB("dirB");
Foam::rmDir(dirA);
Foam::rm(lnA);
Foam::rm(lnB);
Foam::rmDir(dirB);
Info<< "Creating directory " << dirA << endl;
Foam::mkDir(dirA);
const int oldPosix = POSIX::debug;
POSIX::debug = 1;
// Create link and test it
Info<< "Creating softlink " << lnA << endl;
Foam::ln(dirA, lnA);
fileName::Type lnAType = lnA.type(false);
if (lnAType != fileName::LINK)
{
FatalErrorIn("Test-fileName") << "Type of softlink " << lnA
<< " should be " << fileName::LINK
<< " but is " << lnAType << exit(FatalError);
}
fileName::Type dirAType = lnA.type(true);
if (dirAType != fileName::DIRECTORY)
{
FatalErrorIn("Test-fileName") << "Type of what softlink " << lnA
<< " points to should be " << fileName::DIRECTORY
<< " but is " << dirAType << exit(FatalError);
}
// Copy link only
{
Info<< "Copying (non-follow) softlink " << lnA << " to " << lnB
<< endl;
Foam::cp(lnA, lnB, false);
if (lnB.type(false) != fileName::LINK)
{
FatalErrorIn("Test-fileName") << "Type of softlink " << lnB
<< " should be " << fileName::LINK
<< " but is " << lnB.type(false) << exit(FatalError);
}
if (lnB.type(true) != fileName::DIRECTORY)
{
FatalErrorIn("Test-fileName") << "Type of softlink " << lnB
<< " should be " << fileName::DIRECTORY
<< " but is " << lnB.type(true) << exit(FatalError);
}
// Delete
Foam::rm(lnB);
}
// Copy contents of link
{
Info<< "Copying (contents of) softlink " << lnA << " to " << lnB
<< endl;
Foam::cp(lnA, lnB, true);
if (lnB.type(false) != fileName::DIRECTORY)
{
FatalErrorIn("Test-fileName") << "Type of softlink " << lnB
<< " should be " << fileName::DIRECTORY
<< " but is " << lnB.type(false) << exit(FatalError);
}
// Delete
Foam::rm(lnB);
}
POSIX::debug = oldPosix;
Foam::rmDir(dirA);
Foam::rm(lnA);
}
// test findEtcFile // test findEtcFile
Info<< "\n\nfindEtcFile tests:" << nl Info<< "\n\nfindEtcFile tests:" << nl
<< " controlDict => " << findEtcFile("controlDict") << nl << " controlDict => " << findEtcFile("controlDict") << nl

View File

@ -593,9 +593,9 @@ bool Foam::chMod(const fileName& name, const mode_t m)
} }
mode_t Foam::mode(const fileName& name) mode_t Foam::mode(const fileName& name, const bool followLink)
{ {
fileStat fileStatus(name); fileStat fileStatus(name, followLink);
if (fileStatus.isValid()) if (fileStatus.isValid())
{ {
return fileStatus.status().st_mode; return fileStatus.status().st_mode;
@ -607,14 +607,18 @@ mode_t Foam::mode(const fileName& name)
} }
Foam::fileName::Type Foam::type(const fileName& name) Foam::fileName::Type Foam::type(const fileName& name, const bool followLink)
{ {
mode_t m = mode(name); mode_t m = mode(name, followLink);
if (S_ISREG(m)) if (S_ISREG(m))
{ {
return fileName::FILE; return fileName::FILE;
} }
else if (S_ISLNK(m))
{
return fileName::LINK;
}
else if (S_ISDIR(m)) else if (S_ISDIR(m))
{ {
return fileName::DIRECTORY; return fileName::DIRECTORY;
@ -626,27 +630,39 @@ Foam::fileName::Type Foam::type(const fileName& name)
} }
bool Foam::exists(const fileName& name, const bool checkGzip) bool Foam::exists
(
const fileName& name,
const bool checkGzip,
const bool followLink
)
{ {
return mode(name) || isFile(name, checkGzip); return mode(name, followLink) || isFile(name, checkGzip, followLink);
} }
bool Foam::isDir(const fileName& name) bool Foam::isDir(const fileName& name, const bool followLink)
{ {
return S_ISDIR(mode(name)); return S_ISDIR(mode(name, followLink));
} }
bool Foam::isFile(const fileName& name, const bool checkGzip) bool Foam::isFile
(
const fileName& name,
const bool checkGzip,
const bool followLink
)
{ {
return S_ISREG(mode(name)) || (checkGzip && S_ISREG(mode(name + ".gz"))); return
S_ISREG(mode(name, followLink))
|| (checkGzip && S_ISREG(mode(name + ".gz", followLink)));
} }
off_t Foam::fileSize(const fileName& name) off_t Foam::fileSize(const fileName& name, const bool followLink)
{ {
fileStat fileStatus(name); fileStat fileStatus(name, followLink);
if (fileStatus.isValid()) if (fileStatus.isValid())
{ {
return fileStatus.status().st_size; return fileStatus.status().st_size;
@ -658,9 +674,9 @@ off_t Foam::fileSize(const fileName& name)
} }
time_t Foam::lastModified(const fileName& name) time_t Foam::lastModified(const fileName& name, const bool followLink)
{ {
fileStat fileStatus(name); fileStat fileStatus(name, followLink);
if (fileStatus.isValid()) if (fileStatus.isValid())
{ {
return fileStatus.status().st_mtime; return fileStatus.status().st_mtime;
@ -676,7 +692,8 @@ Foam::fileNameList Foam::readDir
( (
const fileName& directory, const fileName& directory,
const fileName::Type type, const fileName::Type type,
const bool filtergz const bool filtergz,
const bool followLink
) )
{ {
// Initial filename list size // Initial filename list size
@ -717,10 +734,10 @@ Foam::fileNameList Foam::readDir
{ {
fileName fName(list->d_name); fileName fName(list->d_name);
// ignore files begining with ., i.e. '.', '..' and '.*' // ignore files beginning with ., i.e. '.', '..' and '.*'
if (fName.size() && fName[0] != '.') if (fName.size() && fName[0] != '.')
{ {
word fExt = fName.ext(); const word fExt = fName.ext();
if if
( (
@ -736,7 +753,7 @@ Foam::fileNameList Foam::readDir
) )
) )
{ {
if ((directory/fName).type() == type) if ((directory/fName).type(followLink) == type)
{ {
if (nEntries >= dirEntries.size()) if (nEntries >= dirEntries.size())
{ {
@ -766,7 +783,7 @@ Foam::fileNameList Foam::readDir
} }
bool Foam::cp(const fileName& src, const fileName& dest) bool Foam::cp(const fileName& src, const fileName& dest, const bool followLink)
{ {
// Make sure source exists. // Make sure source exists.
if (!exists(src)) if (!exists(src))
@ -777,7 +794,8 @@ bool Foam::cp(const fileName& src, const fileName& dest)
fileName destFile(dest); fileName destFile(dest);
// Check type of source file. // Check type of source file.
if (src.type() == fileName::FILE) const fileName::Type srcType = src.type(followLink);
if (srcType == fileName::FILE)
{ {
// If dest is a directory, create the destination file name. // If dest is a directory, create the destination file name.
if (destFile.type() == fileName::DIRECTORY) if (destFile.type() == fileName::DIRECTORY)
@ -817,7 +835,23 @@ bool Foam::cp(const fileName& src, const fileName& dest)
return false; return false;
} }
} }
else if (src.type() == fileName::DIRECTORY) else if (srcType == fileName::LINK)
{
// If dest is a directory, create the destination file name.
if (destFile.type() == fileName::DIRECTORY)
{
destFile = destFile/src.name();
}
// Make sure the destination directory exists.
if (!isDir(destFile.path()) && !mkDir(destFile.path()))
{
return false;
}
ln(src, destFile);
}
else if (srcType == fileName::DIRECTORY)
{ {
// If dest is a directory, create the destination file name. // If dest is a directory, create the destination file name.
if (destFile.type() == fileName::DIRECTORY) if (destFile.type() == fileName::DIRECTORY)
@ -832,7 +866,7 @@ bool Foam::cp(const fileName& src, const fileName& dest)
} }
// Copy files // Copy files
fileNameList contents = readDir(src, fileName::FILE, false); fileNameList contents = readDir(src, fileName::FILE, false, followLink);
forAll(contents, i) forAll(contents, i)
{ {
if (POSIX::debug) if (POSIX::debug)
@ -843,11 +877,17 @@ bool Foam::cp(const fileName& src, const fileName& dest)
} }
// File to file. // File to file.
cp(src/contents[i], destFile/contents[i]); cp(src/contents[i], destFile/contents[i], followLink);
} }
// Copy sub directories. // Copy sub directories.
fileNameList subdirs = readDir(src, fileName::DIRECTORY); fileNameList subdirs = readDir
(
src,
fileName::DIRECTORY,
false,
followLink
);
forAll(subdirs, i) forAll(subdirs, i)
{ {
if (POSIX::debug) if (POSIX::debug)
@ -858,9 +898,13 @@ bool Foam::cp(const fileName& src, const fileName& dest)
} }
// Dir to Dir. // Dir to Dir.
cp(src/subdirs[i], destFile); cp(src/subdirs[i], destFile, followLink);
} }
} }
else
{
return false;
}
return true; return true;
} }
@ -903,7 +947,7 @@ bool Foam::ln(const fileName& src, const fileName& dst)
} }
bool Foam::mv(const fileName& src, const fileName& dst) bool Foam::mv(const fileName& src, const fileName& dst, const bool followLink)
{ {
if (POSIX::debug) if (POSIX::debug)
{ {
@ -914,7 +958,7 @@ bool Foam::mv(const fileName& src, const fileName& dst)
if if
( (
dst.type() == fileName::DIRECTORY dst.type() == fileName::DIRECTORY
&& src.type() != fileName::DIRECTORY && src.type(followLink) != fileName::DIRECTORY
) )
{ {
const fileName dstName(dst/src.name()); const fileName dstName(dst/src.name());
@ -1016,7 +1060,7 @@ bool Foam::rmDir(const fileName& directory)
{ {
fileName path = directory/fName; fileName path = directory/fName;
if (path.type() == fileName::DIRECTORY) if (path.type(false) == fileName::DIRECTORY)
{ {
if (!rmDir(path)) if (!rmDir(path))
{ {

View File

@ -3,7 +3,7 @@
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation \\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation
\\/ M anipulation | \\/ M anipulation | Copyright (C) 2016 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -29,6 +29,7 @@ License
#include <signal.h> #include <signal.h>
#include <unistd.h> #include <unistd.h>
#include <sys/sysmacros.h>
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
@ -38,7 +39,12 @@ Foam::fileStat::fileStat()
{} {}
Foam::fileStat::fileStat(const fileName& fName, const unsigned int maxTime) Foam::fileStat::fileStat
(
const fileName& fName,
const bool followLink,
const unsigned int maxTime
)
{ {
// Work on volatile // Work on volatile
volatile bool locIsValid = false; volatile bool locIsValid = false;
@ -47,13 +53,13 @@ Foam::fileStat::fileStat(const fileName& fName, const unsigned int maxTime)
if (!timedOut(myTimer)) if (!timedOut(myTimer))
{ {
if (::stat(fName.c_str(), &status_) != 0) if (followLink)
{ {
locIsValid = false; locIsValid = (::stat(fName.c_str(), &status_) == 0);
} }
else else
{ {
locIsValid = true; locIsValid = (::lstat(fName.c_str(), &status_) == 0);
} }
} }

View File

@ -3,7 +3,7 @@
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation \\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation
\\/ M anipulation | \\/ M anipulation | Copyright (C) 2016 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -25,7 +25,7 @@ Class
Foam::fileStat Foam::fileStat
Description Description
Wrapper for stat() system call. Wrapper for stat() and lstat() system calls.
Warning Warning
on Linux (an maybe on others) a stat() of an nfs mounted (remote) on Linux (an maybe on others) a stat() of an nfs mounted (remote)
@ -79,8 +79,21 @@ public:
//- Empty constructor //- Empty constructor
fileStat(); fileStat();
//- Construct from components //- Construct from components.
fileStat(const fileName& fName, const unsigned int maxTime=0); // \param fName \n
// The file name or directory name to stat.
//
// \param followLink \n
// If it is a link, get the status of the source file/directory.
//
// \param maxTime \n
// The timeout value.
fileStat
(
const fileName& fName,
const bool followLink = true,
const unsigned int maxTime = 0
);
//- Construct from Istream //- Construct from Istream
fileStat(Istream&); fileStat(Istream&);
@ -96,7 +109,7 @@ public:
return status_; return status_;
} }
//- Did constructor fail //- Was file-stat successful?
bool isValid() const bool isValid() const
{ {
return isValid_; return isValid_;

View File

@ -3,7 +3,7 @@
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation \\ / A nd | Copyright (C) 2011-2015 OpenFOAM Foundation
\\/ M anipulation | \\/ M anipulation | Copyright (C) 2016 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -132,44 +132,60 @@ bool mkDir(const fileName&, mode_t=0777);
bool chMod(const fileName&, const mode_t); bool chMod(const fileName&, const mode_t);
//- Return the file mode //- Return the file mode
mode_t mode(const fileName&); mode_t mode(const fileName&, const bool followLink=true);
//- Return the file type: DIRECTORY or FILE //- Return the file type: DIRECTORY or FILE
fileName::Type type(const fileName&); fileName::Type type(const fileName&, const bool followLink=true);
//- Does the name exist (as DIRECTORY or FILE) in the file system? //- Does the name exist (as DIRECTORY or FILE) in the file system?
// Optionally enable/disable check for gzip file. // Optionally enable/disable check for gzip file.
bool exists(const fileName&, const bool checkGzip=true); bool exists
(
const fileName&,
const bool checkGzip=true,
const bool followLink=true
);
//- Does the name exist as a DIRECTORY in the file system? //- Does the name exist as a DIRECTORY in the file system?
bool isDir(const fileName&); bool isDir(const fileName&, const bool followLink=true);
//- Does the name exist as a FILE in the file system? //- Does the name exist as a FILE in the file system?
// Optionally enable/disable check for gzip file. // Optionally enable/disable check for gzip file.
bool isFile(const fileName&, const bool checkGzip=true); bool isFile
(
const fileName&,
const bool checkGzip=true,
const bool followLink=true
);
//- Return size of file //- Return size of file
off_t fileSize(const fileName&); off_t fileSize(const fileName&, const bool followLink=true);
//- Return time of last file modification //- Return time of last file modification
time_t lastModified(const fileName&); time_t lastModified(const fileName&, const bool followLink=true);
//- Read a directory and return the entries as a string list //- Read a directory and return the entries as a string list
fileNameList readDir fileNameList readDir
( (
const fileName&, const fileName&,
const fileName::Type=fileName::FILE, const fileName::Type=fileName::FILE,
const bool filtergz=true const bool filtergz=true,
const bool followLink=true
); );
//- Copy, recursively if necessary, the source to the destination //- Copy, recursively if necessary, the source to the destination
bool cp(const fileName& src, const fileName& dst); bool cp(const fileName& src, const fileName& dst, const bool followLink=true);
//- Create a softlink. dst should not exist. Returns true if successful. //- Create a softlink. dst should not exist. Returns true if successful.
bool ln(const fileName& src, const fileName& dst); bool ln(const fileName& src, const fileName& dst);
//- Rename src to dst //- Rename src to dst
bool mv(const fileName& src, const fileName& dst); bool mv
(
const fileName& src,
const fileName& dst,
const bool followLink=false
);
//- Rename to a corresponding backup file //- Rename to a corresponding backup file
// If the backup file already exists, attempt with "01" .. "99" suffix // If the backup file already exists, attempt with "01" .. "99" suffix

View File

@ -3,7 +3,7 @@
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | \\ / O peration |
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation \\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\/ M anipulation | \\/ M anipulation | Copyright (C) 2016 OpenCFD Ltd.
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
License License
This file is part of OpenFOAM. This file is part of OpenFOAM.
@ -48,9 +48,9 @@ Foam::fileName::fileName(const wordList& lst)
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::fileName::Type Foam::fileName::type() const Foam::fileName::Type Foam::fileName::type(const bool followLink) const
{ {
return ::Foam::type(*this); return ::Foam::type(*this, followLink);
} }
@ -94,6 +94,7 @@ bool Foam::fileName::clean()
( (
string::size_type src = nChar; string::size_type src = nChar;
src < maxLen; src < maxLen;
/*nil*/
) )
{ {
char c = operator[](src++); char c = operator[](src++);

View File

@ -154,8 +154,9 @@ public:
// Interrogation // Interrogation
//- Return the file type: FILE, DIRECTORY or UNDEFINED //- Return the file type: FILE, DIRECTORY, UNDEFINED or
Type type() const; // LINK (only if followLink=false)
Type type(const bool followLink = true) const;
//- Return true if file name is absolute //- Return true if file name is absolute
bool isAbsolute() const; bool isAbsolute() const;