2222#include < sys/statvfs.h>
2323
2424#include " XrdCl/XrdClURL.hh"
25+ #include " XrdCl/XrdClFileSystem.hh"
26+ #include " XrdCl/XrdClFileStateHandler.hh"
2527
2628#include " XrdOuc/XrdOucEnv.hh"
2729#include " XrdOuc/XrdOucUtils.hh"
2830#include " XrdOuc/XrdOucPrivateUtils.hh"
31+ #include " XrdOuc/XrdOucJson.hh"
2932
3033#include " XrdSys/XrdSysTimer.hh"
3134#include " XrdSys/XrdSysTrace.hh"
@@ -425,6 +428,7 @@ File* Cache::GetFile(const std::string& path, IO* io, long long off, long long f
425428 }
426429
427430 // This is always true, now that IOFileBlock is unsupported.
431+
428432 if (filesize == 0 )
429433 {
430434 struct stat st;
@@ -444,7 +448,7 @@ File* Cache::GetFile(const std::string& path, IO* io, long long off, long long f
444448
445449 if (filesize >= 0 )
446450 {
447- file = File::FileOpen (path, off, filesize);
451+ file = File::FileOpen (path, off, filesize, io-> GetInput () );
448452 }
449453
450454 {
@@ -904,6 +908,20 @@ int Cache::LocalFilePath(const char *curl, char *buff, int blen,
904908 return -ENOENT;
905909}
906910
911+ // ______________________________________________________________________________
912+ // If supported, write Cache-Control as xattr to cinfo file.
913+ // One can use file descriptor or full path interchangeably
914+ // ------------------------------------------------------------------------------
915+ void Cache::WriteCacheControlXAttr (int cinfo_fd, const char * path, const std::string& cc)
916+ {
917+ if (m_metaXattr) {
918+ int res = XrdSysXAttrActive->Set (" pfc.cache-control" , cc.c_str (), cc.size (), path, cinfo_fd, 0 );
919+ if (res != 0 ) {
920+ TRACE (Error, " WritecacheControlXAttr error setting xattr " << res);
921+ }
922+ }
923+ }
924+
907925// ______________________________________________________________________________
908926// If supported, write file_size as xattr to cinfo file.
909927// ------------------------------------------------------------------------------
@@ -958,6 +976,47 @@ long long Cache::DetermineFullFileSize(const std::string &cinfo_fname)
958976 return ret;
959977}
960978
979+ // ______________________________________________________________________________
980+ // Get cache control attributes from the corresponding cinfo-file name.
981+ // Returns -error on failure.
982+ // ------------------------------------------------------------------------------
983+ int Cache::GetCacheControlXAttr (const std::string &cinfo_fname, std::string& ival)
984+ {
985+ if (m_metaXattr) {
986+
987+ char pfn[4096 ];
988+ m_oss->Lfn2Pfn (cinfo_fname.c_str (), pfn, 4096 );
989+
990+ char cc[512 ];
991+ int res = XrdSysXAttrActive->Get (" pfc.cache-control" , &cc, 512 , pfn, -1 );
992+ if (res > 0 )
993+ {
994+ std::string tmp (cc, res);
995+ ival = tmp;
996+ }
997+ return res;
998+ }
999+ return 0 ;
1000+ }
1001+
1002+ // ______________________________________________________________________________
1003+ // Get cache control attributes from the corresponding cinfo-file name.
1004+ // Returns -error on failure.
1005+ // ------------------------------------------------------------------------------
1006+ int Cache::GetCacheControlXAttr (int fd, std::string& ival)
1007+ {
1008+ if (m_metaXattr) {
1009+ char cc[512 ];
1010+ int res = XrdSysXAttrActive->Get (" pfc.cache-control" , &cc, 512 , nullptr , fd);
1011+ if (res > 0 )
1012+ {
1013+ ival = std::string (cc, res);
1014+ return res;
1015+ }
1016+ }
1017+ return 0 ;
1018+ }
1019+
9611020// ______________________________________________________________________________
9621021// Calculate if the file is to be considered cached for the purposes of
9631022// only-if-cached and setting of atime of the Stat() calls.
@@ -1097,6 +1156,64 @@ int Cache::Prepare(const char *curl, int oflags, mode_t mode)
10971156 if (m_oss->Stat (i_name.c_str (), &sbuff) == XrdOssOK)
10981157 {
10991158 TRACE (Dump, " Prepare defer open " << f_name);
1159+
1160+ std::string icc;
1161+ if (GetCacheControlXAttr (i_name, icc) > 0 ) {
1162+ using namespace nlohmann ;
1163+ json cc_json = json::parse (icc);
1164+
1165+ bool mustRevalidate = cc_json.contains (" revalidate" ) && (cc_json[" revalidate" ] == true );
1166+ bool hasExpired = false ;
1167+ if (cc_json.contains (" expire" ))
1168+ {
1169+ time_t current_time;
1170+ current_time = time (NULL );
1171+ if (current_time > cc_json[" expire" ])
1172+ hasExpired = true ;
1173+ }
1174+
1175+ bool ccIsValid = true ;
1176+
1177+ if (cc_json.contains (" ETag" ) && (mustRevalidate || hasExpired)) {
1178+ // Compare cinfo xattr etag and the etag from file system query response
1179+ // Note: qeury returns only etag value, not a json string
1180+ XrdCl::FileSystem fs (url);
1181+ XrdCl::Buffer queryArgs (1024 ); // pass file path throug args: reserve bytes to store path
1182+ queryArgs.FromString (curl);
1183+ XrdCl::Buffer *response = nullptr ;
1184+ XrdCl::XRootDStatus st = fs.Query (XrdCl::QueryCode::Head, queryArgs, response);
1185+
1186+ if (st.IsOK ())
1187+ {
1188+ std::string etag = response->ToString ();
1189+ ccIsValid = (etag == cc_json[" ETag" ]);
1190+ TRACE (Info, " Prepare " << f_name << " , ETag valid res: " << ccIsValid);
1191+
1192+ // update expiration time if Etag is valid
1193+ if (cc_json.contains (" max-age" ))
1194+ {
1195+ time_t ma = cc_json[" max-age" ];
1196+ cc_json[" expire" ] = ma + time (NULL );
1197+ char pfn[4096 ];
1198+ m_oss->Lfn2Pfn (i_name.c_str (), pfn, 4096 );
1199+ WriteCacheControlXAttr (-1 , pfn, cc_json.dump ());
1200+ }
1201+ }
1202+ else
1203+ {
1204+ // Message has a status beacuse we are in the block condition for cache-contol xattr
1205+ TRACE (Error, " Prepare() XrdCl::FileSystem::Query failed " << f_name.c_str ());
1206+ ccIsValid = false ;
1207+ }
1208+ }
1209+
1210+ if (!ccIsValid)
1211+ {
1212+ // invalidate cinfo on ETag mismatch
1213+ UnlinkFile (f_name, false );
1214+ }
1215+ } // end chekcing cache control xattr in cinfo file
1216+
11001217 return 1 ;
11011218 }
11021219 else
0 commit comments