BackendVFS.cpp 6.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include "BackendVFS.hpp"
#include "exception/Errnum.hpp"
#include <fstream>
#include <stdlib.h>
#include <ftw.h>
#include <fcntl.h>
#include <sys/syscall.h>
#include <sys/file.h>
#include <stdio.h>
#include <memory>



namespace cta { namespace objectstore {

BackendVFS::BackendVFS() : m_deleteOnExit(true) {
  // Create the directory for storage
  char tplt[] = "/tmp/jobStoreVFSXXXXXX";
  mkdtemp(tplt);
  // If template is an empty string, we failed, otherwise, we're fine.
  if (*tplt) {
    m_root = tplt;
  } else {
    throw cta::exception::Errnum("Failed to create temporary directory");
  }
}

BackendVFS::BackendVFS(std::string path):
  m_root(path), m_deleteOnExit(false) {}

void BackendVFS::noDeleteOnExit() {
  m_deleteOnExit = false;
}

void BackendVFS::deleteOnExit() {
  m_deleteOnExit = true;
}

int deleteFileOrDirectory(const char* fpath, 
  const struct ::stat* sb, int tflag, struct FTW* ftwbuf) {
  switch (tflag) {
    case FTW_D:
    case FTW_DNR:
    case FTW_DP:
      rmdir(fpath);
      break;
    default:
      unlink(fpath);
      break;
  }
  return 0;
}

BackendVFS::~BackendVFS() {
  if (m_deleteOnExit) {
    // Delete the created directories recursively
    nftw (m_root.c_str(), deleteFileOrDirectory, 100, FTW_DEPTH);
  }
}

void BackendVFS::create(std::string name, std::string content) {
  std::string path = m_root + "/" + name;
  std::string lockPath = m_root + "/." + name + ".lock";
64
65
  bool fileCreated = false;
  bool lockCreated = false;
66
  try {
67
    int fd = ::open(path.c_str(), O_WRONLY | O_CREAT | O_EXCL, S_IRWXU);
68
69
    // Create and fill up the path
    cta::exception::Errnum::throwOnMinusOne(fd,
70
71
        "In ObjectStoreVFS::create, failed to open the file");
    fileCreated = true;
72
73
74
75
76
77
    cta::exception::Errnum::throwOnMinusOne(
        ::write(fd, content.c_str(), content.size()),
        "In ObjectStoreVFS::create, failed to write to file");
    cta::exception::Errnum::throwOnMinusOne(::close(fd),
        "In ObjectStoreVFS::create, failed to close the file");
    // Create the lock file
78
79
    int fdLock = ::open(lockPath.c_str(), O_WRONLY | O_CREAT | O_EXCL, S_IRWXU);
    lockCreated = true;
80
81
82
83
84
    cta::exception::Errnum::throwOnMinusOne(fdLock,
        "In ObjectStoreVFS::create, failed to creat the lock file");
    cta::exception::Errnum::throwOnMinusOne(::close(fdLock),
        "In ObjectStoreVFS::create, failed to close the lock file");
  } catch (...) {
85
86
    if (fileCreated) unlink(path.c_str());
    if (lockCreated) unlink(lockPath.c_str());
87
88
89
90
91
92
93
94
95
96
97
    throw;
  }
}
    
void BackendVFS::atomicOverwrite(std::string name, std::string content) {
  // When entering here, we should hold an exclusive lock on the *context
  // file descriptor. We will create a new file, lock it immediately exclusively,
  // create the new content in it, move it over the old file, and close the *context
  // file descriptor.
  std::string tempPath = m_root + "/." + name + ".pre-overwrite";
  std::string targetPath = m_root + "/" + name;
98
99
100
101
  // Make sure the file exists first
  if (!exists(name)) {
    throw cta::exception::Exception("In BackendVFS::atomicOverwrite, trying to update a non-existing object");
  }
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
  // Create the new version of the file, make sure it's visible, lock it.
  int fd = ::creat(tempPath.c_str(), S_IRWXU);
  cta::exception::Errnum::throwOnMinusOne(fd,
      "In ObjectStoreVFS::atomicOverwrite, failed to creat the pre-overwrite file");
  cta::exception::Errnum::throwOnMinusOne(
      ::write(fd, content.c_str(), content.size()),
      "In ObjectStoreVFS::atomicOverwrite, failed to write to the pre-overwrite file");
  cta::exception::Errnum::throwOnMinusOne(::close(fd),
      "In ObjectStoreVFS::atomicOverwrite, failed to close the pre-overwrite file");
  std::stringstream err;
  err << "In ObjectStoreVFS::atomicOverwrite, failed to rename the file"
      << " tempPath=" << tempPath << " targetPath=" << targetPath << " tid=" << syscall(SYS_gettid);
  cta::exception::Errnum::throwOnMinusOne(
      ::rename(tempPath.c_str(), targetPath.c_str()),
      err.str());
}

std::string BackendVFS::read(std::string name) {
  std::string path = m_root + "/" + name;
  std::string ret;
  std::ifstream file(path.c_str());
  if (!file) {
    throw cta::exception::Errnum(
        std::string("In ObjectStoreVFS::read, failed to open file for read: ") +
        path);
  }
  char buff[200];
  while (!file.eof()) {
    file.read(buff, sizeof (buff));
    ret.append(buff, file.gcount());
  }
  return ret;
}

void BackendVFS::remove(std::string name) {
  std::string path = m_root + "/" + name;
  std::string lockPath = m_root + "/." + name + ".lock";
  cta::exception::Errnum::throwOnNonZero(unlink(path.c_str()), "Failed to remove object file");
  cta::exception::Errnum::throwOnNonZero(unlink(lockPath.c_str()), "Failed to remove lock file.");
}

bool BackendVFS::exists(std::string name) {
  std::string path = m_root + "/" + name;
145
146
  std::string lockPath = m_root + "/." + name + ".lock";
  if (::access(path.c_str(), F_OK) || ::access(lockPath.c_str(), F_OK))
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
    return false;
  return true;
}


BackendVFS::Parameters* BackendVFS::getParams() {
  std::auto_ptr<Parameters> ret(new Parameters);
  ret->m_path = m_root;
  return ret.release();
}

void BackendVFS::ScopedLock::release() {
  if (!m_fdSet) return;
  ::flock(m_fd, LOCK_UN);
  ::close(m_fd);
  m_fdSet = false;
  //std::cout << "Unlocked " << name << " with fd=" << fd << " @" << where << " tid=" << syscall(SYS_gettid) << std::endl;
}

BackendVFS::ScopedLock * BackendVFS::lockHelper(
  std::string name, int type) {
  std::string path = m_root + "/." + name + ".lock";
  std::auto_ptr<ScopedLock> ret(new ScopedLock);
  ret->set(::open(path.c_str(), O_RDONLY, S_IRWXU));
  cta::exception::Errnum::throwOnMinusOne(ret->m_fd,
      "In BackendStoreVFS::lockHelper, failed to open the lock file.");
  cta::exception::Errnum::throwOnMinusOne(::flock(ret->m_fd, LOCK_EX),
      "In BackendStoreVFS::lockHelper, failed to flock the lock file.");
  return ret.release();
}

BackendVFS::ScopedLock * BackendVFS::lockExclusive(std::string name) {
  return lockHelper(name, LOCK_EX);
  //std::cout << "LockedExclusive " << name << " with fd=" << context.get(0) << " @" << where << " tid=" << syscall(SYS_gettid) << std::endl;
}

BackendVFS::ScopedLock * BackendVFS::lockShared(std::string name) {
  return lockHelper(name, LOCK_SH);
  //std::cout << "LockedShared " << name << " with fd=" << context.get(0) << " @" << where << " tid=" << syscall(SYS_gettid) << std::endl;
}

std::string BackendVFS::Parameters::toStr() {
  std::stringstream ret;
  ret << "path=" << m_path;
  return ret.str();
}

}} // end of cta::objectstore