1 |
|
|
/** |
2 |
|
|
* This file is part of the CernVM File System. |
3 |
|
|
* |
4 |
|
|
* CernVM-FS is a FUSE module which implements an HTTP read-only filesystem. |
5 |
|
|
* The original idea is based on GROW-FS. |
6 |
|
|
* |
7 |
|
|
* CernVM-FS shows a remote HTTP directory as local file system. The client |
8 |
|
|
* sees all available files. On first access, a file is downloaded and |
9 |
|
|
* cached locally. All downloaded pieces are verified by a cryptographic |
10 |
|
|
* content hash. |
11 |
|
|
* |
12 |
|
|
* To do so, a directory hive has to be transformed into a CVMFS2 |
13 |
|
|
* "repository". This can be done by the CernVM-FS server tools. |
14 |
|
|
* |
15 |
|
|
* This preparation of directories is transparent to web servers and |
16 |
|
|
* web proxies. They just serve static content, i.e. arbitrary files. |
17 |
|
|
* Any HTTP server should do the job. We use Apache + Squid. Serving |
18 |
|
|
* files from the memory of a web proxy brings a significant performance |
19 |
|
|
* improvement. |
20 |
|
|
*/ |
21 |
|
|
|
22 |
|
|
// TODO(jblomer): the file system root should probably always return 1 for an |
23 |
|
|
// inode. See also integration test #23. |
24 |
|
|
|
25 |
|
|
#define ENOATTR ENODATA /**< instead of including attr/xattr.h */ |
26 |
|
|
#define FUSE_USE_VERSION 26 |
27 |
|
|
|
28 |
|
|
#ifndef __STDC_FORMAT_MACROS |
29 |
|
|
#define __STDC_FORMAT_MACROS |
30 |
|
|
#endif |
31 |
|
|
|
32 |
|
|
// sys/xattr.h conflicts with linux/xattr.h and needs to be loaded very early |
33 |
|
|
#include <sys/xattr.h> // NOLINT |
34 |
|
|
|
35 |
|
|
#include "cvmfs_config.h" |
36 |
|
|
#include "cvmfs.h" |
37 |
|
|
|
38 |
|
|
#include <dirent.h> |
39 |
|
|
#include <errno.h> |
40 |
|
|
#include <fcntl.h> |
41 |
|
|
#include <fuse/fuse_lowlevel.h> |
42 |
|
|
#include <fuse/fuse_opt.h> |
43 |
|
|
#include <google/dense_hash_map> |
44 |
|
|
#include <inttypes.h> |
45 |
|
|
#include <openssl/crypto.h> |
46 |
|
|
#include <pthread.h> |
47 |
|
|
#include <stddef.h> |
48 |
|
|
#include <stdint.h> |
49 |
|
|
#include <sys/errno.h> |
50 |
|
|
#include <sys/file.h> |
51 |
|
|
#include <sys/mount.h> |
52 |
|
|
#include <sys/resource.h> |
53 |
|
|
#include <sys/stat.h> |
54 |
|
|
#include <sys/time.h> |
55 |
|
|
#include <sys/types.h> |
56 |
|
|
#include <sys/wait.h> |
57 |
|
|
#include <unistd.h> |
58 |
|
|
|
59 |
|
|
#include <algorithm> |
60 |
|
|
#include <cassert> |
61 |
|
|
#include <cstdio> |
62 |
|
|
#include <cstdlib> |
63 |
|
|
#include <cstring> |
64 |
|
|
#include <ctime> |
65 |
|
|
#include <functional> |
66 |
|
|
#include <map> |
67 |
|
|
#include <new> |
68 |
|
|
#include <string> |
69 |
|
|
#include <vector> |
70 |
|
|
|
71 |
|
|
#include "atomic.h" |
72 |
|
|
#include "authz/authz_session.h" |
73 |
|
|
#include "auto_umount.h" |
74 |
|
|
#include "backoff.h" |
75 |
|
|
#include "cache.h" |
76 |
|
|
#include "catalog_mgr_client.h" |
77 |
|
|
#include "clientctx.h" |
78 |
|
|
#include "compat.h" |
79 |
|
|
#include "compression.h" |
80 |
|
|
#include "directory_entry.h" |
81 |
|
|
#include "download.h" |
82 |
|
|
#include "fence.h" |
83 |
|
|
#include "fetch.h" |
84 |
|
|
#include "file_chunk.h" |
85 |
|
|
#include "fuse_inode_gen.h" |
86 |
|
|
#include "fuse_remount.h" |
87 |
|
|
#include "globals.h" |
88 |
|
|
#include "glue_buffer.h" |
89 |
|
|
#include "hash.h" |
90 |
|
|
#include "history_sqlite.h" |
91 |
|
|
#include "loader.h" |
92 |
|
|
#include "logging.h" |
93 |
|
|
#include "lru_md.h" |
94 |
|
|
#include "manifest_fetch.h" |
95 |
|
|
#include "monitor.h" |
96 |
|
|
#include "mountpoint.h" |
97 |
|
|
#include "nfs_maps.h" |
98 |
|
|
#include "options.h" |
99 |
|
|
#include "platform.h" |
100 |
|
|
#include "quota_listener.h" |
101 |
|
|
#include "quota_posix.h" |
102 |
|
|
#include "shortstring.h" |
103 |
|
|
#include "signature.h" |
104 |
|
|
#include "smalloc.h" |
105 |
|
|
#include "sqlitemem.h" |
106 |
|
|
#include "sqlitevfs.h" |
107 |
|
|
#include "statistics.h" |
108 |
|
|
#include "talk.h" |
109 |
|
|
#include "tracer.h" |
110 |
|
|
#include "util_concurrency.h" |
111 |
|
|
#include "uuid.h" |
112 |
|
|
#include "wpad.h" |
113 |
|
|
#include "xattr.h" |
114 |
|
|
|
115 |
|
|
using namespace std; // NOLINT |
116 |
|
|
|
117 |
|
|
namespace cvmfs { |
118 |
|
|
|
119 |
|
|
FileSystem *file_system_ = NULL; |
120 |
|
|
MountPoint *mount_point_ = NULL; |
121 |
|
|
TalkManager *talk_mgr_ = NULL; |
122 |
|
|
Watchdog *watchdog_ = NULL; |
123 |
|
|
FuseRemounter *fuse_remounter_ = NULL; |
124 |
|
|
InodeGenerationInfo inode_generation_info_; |
125 |
|
|
|
126 |
|
|
|
127 |
|
|
/** |
128 |
|
|
* For cvmfs_opendir / cvmfs_readdir |
129 |
|
|
* TODO: use mmap for very large listings |
130 |
|
|
*/ |
131 |
|
|
struct DirectoryListing { |
132 |
|
|
char *buffer; /**< Filled by fuse_add_direntry */ |
133 |
|
|
|
134 |
|
|
// Not really used anymore. But directory listing needs to be migrated during |
135 |
|
|
// hotpatch. If buffer is allocated by smmap, capacity is zero. |
136 |
|
|
size_t size; |
137 |
|
|
size_t capacity; |
138 |
|
|
|
139 |
|
|
DirectoryListing() : buffer(NULL), size(0), capacity(0) { } |
140 |
|
|
}; |
141 |
|
|
|
142 |
|
|
const loader::LoaderExports *loader_exports_ = NULL; |
143 |
|
|
OptionsManager *options_mgr_ = NULL; |
144 |
|
|
pid_t pid_ = 0; /**< will be set after deamon() */ |
145 |
|
|
quota::ListenerHandle *watchdog_listener_ = NULL; |
146 |
|
|
quota::ListenerHandle *unpin_listener_ = NULL; |
147 |
|
|
|
148 |
|
|
|
149 |
|
|
typedef google::dense_hash_map<uint64_t, DirectoryListing, |
150 |
|
|
hash_murmur<uint64_t> > |
151 |
|
|
DirectoryHandles; |
152 |
|
|
DirectoryHandles *directory_handles_ = NULL; |
153 |
|
|
pthread_mutex_t lock_directory_handles_ = PTHREAD_MUTEX_INITIALIZER; |
154 |
|
|
uint64_t next_directory_handle_ = 0; |
155 |
|
|
|
156 |
|
|
unsigned max_open_files_; /**< maximum allowed number of open files */ |
157 |
|
|
/** |
158 |
|
|
* Number of reserved file descriptors for internal use |
159 |
|
|
*/ |
160 |
|
|
const int kNumReservedFd = 512; |
161 |
|
|
|
162 |
|
|
|
163 |
|
|
static inline double GetKcacheTimeout() { |
164 |
|
|
if (!fuse_remounter_->IsCaching()) |
165 |
|
|
return 0.0; |
166 |
|
|
return mount_point_->kcache_timeout_sec(); |
167 |
|
|
} |
168 |
|
|
|
169 |
|
|
|
170 |
|
|
void GetReloadStatus(bool *drainout_mode, bool *maintenance_mode) { |
171 |
|
|
*drainout_mode = fuse_remounter_->IsInDrainoutMode(); |
172 |
|
|
*maintenance_mode = fuse_remounter_->IsInMaintenanceMode(); |
173 |
|
|
} |
174 |
|
|
|
175 |
|
|
|
176 |
|
|
static bool UseWatchdog() { |
177 |
|
|
if (loader_exports_ == NULL || loader_exports_->version < 2) { |
178 |
|
|
return true; // spawn watchdog by default |
179 |
|
|
// Note: with library versions before 2.1.8 it might not |
180 |
|
|
// create stack traces properly in all cases |
181 |
|
|
} |
182 |
|
|
|
183 |
|
|
return !loader_exports_->disable_watchdog; |
184 |
|
|
} |
185 |
|
|
|
186 |
|
|
std::string PrintInodeGeneration() { |
187 |
|
|
return "init-catalog-revision: " + |
188 |
|
|
StringifyInt(inode_generation_info_.initial_revision) + " " + |
189 |
|
|
"current-catalog-revision: " + |
190 |
|
|
StringifyInt(mount_point_->catalog_mgr()->GetRevision()) + " " + |
191 |
|
|
"incarnation: " + StringifyInt(inode_generation_info_.incarnation) + " " + |
192 |
|
|
"inode generation: " + StringifyInt(inode_generation_info_.inode_generation) |
193 |
|
|
+ "\n"; |
194 |
|
|
} |
195 |
|
|
|
196 |
|
|
|
197 |
|
|
static bool CheckVoms(const fuse_ctx &fctx) { |
198 |
|
|
if (!mount_point_->has_membership_req()) |
199 |
|
|
return true; |
200 |
|
|
string mreq = mount_point_->membership_req(); |
201 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "Got VOMS authz %s from filesystem " |
202 |
|
|
"properties", mreq.c_str()); |
203 |
|
|
|
204 |
|
|
if (fctx.uid == 0) |
205 |
|
|
return true; |
206 |
|
|
|
207 |
|
|
return mount_point_->authz_session_mgr()->IsMemberOf(fctx.pid, mreq); |
208 |
|
|
} |
209 |
|
|
|
210 |
|
|
|
211 |
|
|
static bool GetDirentForInode(const fuse_ino_t ino, |
212 |
|
|
catalog::DirectoryEntry *dirent) |
213 |
|
|
{ |
214 |
|
|
// Lookup inode in cache |
215 |
|
|
if (mount_point_->inode_cache()->Lookup(ino, dirent)) |
216 |
|
|
return true; |
217 |
|
|
|
218 |
|
|
// Look in the catalogs in 2 steps: lookup inode->path, lookup path |
219 |
|
|
catalog::DirectoryEntry dirent_negative = |
220 |
|
|
catalog::DirectoryEntry(catalog::kDirentNegative); |
221 |
|
|
// Reset directory entry. If the function returns false and dirent is no |
222 |
|
|
// the kDirentNegative, it was an I/O error |
223 |
|
|
*dirent = catalog::DirectoryEntry(); |
224 |
|
|
|
225 |
|
|
catalog::ClientCatalogManager *catalog_mgr = mount_point_->catalog_mgr(); |
226 |
|
|
|
227 |
|
|
if (file_system_->IsNfsSource()) { |
228 |
|
|
// NFS mode |
229 |
|
|
PathString path; |
230 |
|
|
bool retval = file_system_->nfs_maps()->GetPath(ino, &path); |
231 |
|
|
if (!retval) { |
232 |
|
|
*dirent = dirent_negative; |
233 |
|
|
return false; |
234 |
|
|
} |
235 |
|
|
if (catalog_mgr->LookupPath(path, catalog::kLookupSole, dirent)) { |
236 |
|
|
// Fix inodes |
237 |
|
|
dirent->set_inode(ino); |
238 |
|
|
mount_point_->inode_cache()->Insert(ino, *dirent); |
239 |
|
|
return true; |
240 |
|
|
} |
241 |
|
|
return false; // Not found in catalog or catalog load error |
242 |
|
|
} |
243 |
|
|
|
244 |
|
|
// Non-NFS mode |
245 |
|
|
PathString path; |
246 |
|
|
if (ino == catalog_mgr->GetRootInode()) { |
247 |
|
|
bool retval = |
248 |
|
|
catalog_mgr->LookupPath(PathString(), catalog::kLookupSole, dirent); |
249 |
|
|
assert(retval); |
250 |
|
|
dirent->set_inode(ino); |
251 |
|
|
mount_point_->inode_cache()->Insert(ino, *dirent); |
252 |
|
|
return true; |
253 |
|
|
} |
254 |
|
|
|
255 |
|
|
bool retval = mount_point_->inode_tracker()->FindPath(ino, &path); |
256 |
|
|
if (!retval) { |
257 |
|
|
// Can this ever happen? |
258 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr, |
259 |
|
|
"GetDirentForInode inode lookup failure %" PRId64, ino); |
260 |
|
|
*dirent = dirent_negative; |
261 |
|
|
return false; |
262 |
|
|
} |
263 |
|
|
if (catalog_mgr->LookupPath(path, catalog::kLookupSole, dirent)) { |
264 |
|
|
// Fix inodes |
265 |
|
|
dirent->set_inode(ino); |
266 |
|
|
mount_point_->inode_cache()->Insert(ino, *dirent); |
267 |
|
|
return true; |
268 |
|
|
} |
269 |
|
|
|
270 |
|
|
// Can happen after reload of catalogs or on catalog load failure |
271 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "GetDirentForInode path lookup failure"); |
272 |
|
|
return false; |
273 |
|
|
} |
274 |
|
|
|
275 |
|
|
|
276 |
|
|
static bool GetDirentForPath(const PathString &path, |
277 |
|
|
catalog::DirectoryEntry *dirent) |
278 |
|
|
{ |
279 |
|
|
uint64_t live_inode = 0; |
280 |
|
|
if (!file_system_->IsNfsSource()) |
281 |
|
|
live_inode = mount_point_->inode_tracker()->FindInode(path); |
282 |
|
|
|
283 |
|
|
shash::Md5 md5path(path.GetChars(), path.GetLength()); |
284 |
|
|
if (mount_point_->md5path_cache()->Lookup(md5path, dirent)) { |
285 |
|
|
if (dirent->GetSpecial() == catalog::kDirentNegative) |
286 |
|
|
return false; |
287 |
|
|
if (!file_system_->IsNfsSource() && (live_inode != 0)) |
288 |
|
|
dirent->set_inode(live_inode); |
289 |
|
|
return true; |
290 |
|
|
} |
291 |
|
|
|
292 |
|
|
catalog::ClientCatalogManager *catalog_mgr = mount_point_->catalog_mgr(); |
293 |
|
|
|
294 |
|
|
// Lookup inode in catalog TODO: not twice md5 calculation |
295 |
|
|
bool retval; |
296 |
|
|
retval = catalog_mgr->LookupPath(path, catalog::kLookupSole, dirent); |
297 |
|
|
if (retval) { |
298 |
|
|
if (file_system_->IsNfsSource()) { |
299 |
|
|
// Fix inode |
300 |
|
|
dirent->set_inode(file_system_->nfs_maps()->GetInode(path)); |
301 |
|
|
} else { |
302 |
|
|
if (live_inode != 0) |
303 |
|
|
dirent->set_inode(live_inode); |
304 |
|
|
} |
305 |
|
|
mount_point_->md5path_cache()->Insert(md5path, *dirent); |
306 |
|
|
return true; |
307 |
|
|
} |
308 |
|
|
|
309 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "GetDirentForPath, no entry"); |
310 |
|
|
// Only insert ENOENT results into negative cache. Otherwise it was an |
311 |
|
|
// error loading nested catalogs |
312 |
|
|
if (dirent->GetSpecial() == catalog::kDirentNegative) |
313 |
|
|
mount_point_->md5path_cache()->InsertNegative(md5path); |
314 |
|
|
return false; |
315 |
|
|
} |
316 |
|
|
|
317 |
|
|
|
318 |
|
|
static bool GetPathForInode(const fuse_ino_t ino, PathString *path) { |
319 |
|
|
// Check the path cache first |
320 |
|
|
if (mount_point_->path_cache()->Lookup(ino, path)) |
321 |
|
|
return true; |
322 |
|
|
|
323 |
|
|
if (file_system_->IsNfsSource()) { |
324 |
|
|
// NFS mode, just a lookup |
325 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "MISS %d - lookup in NFS maps", ino); |
326 |
|
|
if (file_system_->nfs_maps()->GetPath(ino, path)) { |
327 |
|
|
mount_point_->path_cache()->Insert(ino, *path); |
328 |
|
|
return true; |
329 |
|
|
} |
330 |
|
|
return false; |
331 |
|
|
} |
332 |
|
|
|
333 |
|
|
if (ino == mount_point_->catalog_mgr()->GetRootInode()) |
334 |
|
|
return true; |
335 |
|
|
|
336 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "MISS %d - looking in inode tracker", ino); |
337 |
|
|
bool retval = mount_point_->inode_tracker()->FindPath(ino, path); |
338 |
|
|
assert(retval); |
339 |
|
|
mount_point_->path_cache()->Insert(ino, *path); |
340 |
|
|
return true; |
341 |
|
|
} |
342 |
|
|
|
343 |
|
|
static void DoTraceInode(const int event, |
344 |
|
|
fuse_ino_t ino, |
345 |
|
|
const std::string &msg) |
346 |
|
|
{ |
347 |
|
|
PathString path; |
348 |
|
|
bool found = GetPathForInode(ino, &path); |
349 |
|
|
if (!found) { |
350 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, |
351 |
|
|
"Tracing: Could not find path for inode %" PRIu64, uint64_t(ino)); |
352 |
|
|
mount_point_->tracer()->Trace(event, PathString("@UNKNOWN"), msg); |
353 |
|
|
} else { |
354 |
|
|
mount_point_->tracer()->Trace(event, path, msg); |
355 |
|
|
} |
356 |
|
|
} |
357 |
|
|
|
358 |
|
|
static void inline TraceInode(const int event, |
359 |
|
|
fuse_ino_t ino, |
360 |
|
|
const std::string &msg) |
361 |
|
|
{ |
362 |
|
|
if (mount_point_->tracer()->IsActive()) DoTraceInode(event, ino, msg); |
363 |
|
|
} |
364 |
|
|
|
365 |
|
|
/** |
366 |
|
|
* Find the inode number of a file name in a directory given by inode. |
367 |
|
|
* This or getattr is called as kind of prerequisit to every operation. |
368 |
|
|
* We do check catalog TTL here (and reload, if necessary). |
369 |
|
|
*/ |
370 |
|
|
static void cvmfs_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) { |
371 |
|
|
perf::Inc(file_system_->n_fs_lookup()); |
372 |
|
|
const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req); |
373 |
|
|
ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid); |
374 |
|
|
fuse_remounter_->TryFinish(); |
375 |
|
|
|
376 |
|
|
fuse_remounter_->fence()->Enter(); |
377 |
|
|
catalog::ClientCatalogManager *catalog_mgr = mount_point_->catalog_mgr(); |
378 |
|
|
|
379 |
|
|
parent = catalog_mgr->MangleInode(parent); |
380 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, |
381 |
|
|
"cvmfs_lookup in parent inode: %" PRIu64 " for name: %s", |
382 |
|
|
uint64_t(parent), name); |
383 |
|
|
|
384 |
|
|
PathString path; |
385 |
|
|
PathString parent_path; |
386 |
|
|
catalog::DirectoryEntry dirent; |
387 |
|
|
struct fuse_entry_param result; |
388 |
|
|
|
389 |
|
|
memset(&result, 0, sizeof(result)); |
390 |
|
|
double timeout = GetKcacheTimeout(); |
391 |
|
|
result.attr_timeout = timeout; |
392 |
|
|
result.entry_timeout = timeout; |
393 |
|
|
|
394 |
|
|
// Special NFS lookups: . and .. |
395 |
|
|
if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0)) { |
396 |
|
|
if (GetDirentForInode(parent, &dirent)) { |
397 |
|
|
if (strcmp(name, ".") == 0) { |
398 |
|
|
goto lookup_reply_positive; |
399 |
|
|
} else { |
400 |
|
|
// Lookup for ".." |
401 |
|
|
if (dirent.inode() == catalog_mgr->GetRootInode()) { |
402 |
|
|
dirent.set_inode(1); |
403 |
|
|
goto lookup_reply_positive; |
404 |
|
|
} |
405 |
|
|
if (!GetPathForInode(parent, &parent_path)) |
406 |
|
|
goto lookup_reply_negative; |
407 |
|
|
if (GetDirentForPath(GetParentPath(parent_path), &dirent)) |
408 |
|
|
goto lookup_reply_positive; |
409 |
|
|
} |
410 |
|
|
} |
411 |
|
|
// No entry for "." or no entry for ".." |
412 |
|
|
if (dirent.GetSpecial() == catalog::kDirentNegative) |
413 |
|
|
goto lookup_reply_negative; |
414 |
|
|
else |
415 |
|
|
goto lookup_reply_error; |
416 |
|
|
assert(false); |
417 |
|
|
} |
418 |
|
|
|
419 |
|
|
if (!GetPathForInode(parent, &parent_path)) { |
420 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "no path for parent inode found"); |
421 |
|
|
goto lookup_reply_negative; |
422 |
|
|
} |
423 |
|
|
|
424 |
|
|
path.Assign(parent_path); |
425 |
|
|
path.Append("/", 1); |
426 |
|
|
path.Append(name, strlen(name)); |
427 |
|
|
mount_point_->tracer()->Trace(Tracer::kEventLookup, path, "lookup()"); |
428 |
|
|
if (!GetDirentForPath(path, &dirent)) { |
429 |
|
|
if (dirent.GetSpecial() == catalog::kDirentNegative) |
430 |
|
|
goto lookup_reply_negative; |
431 |
|
|
else |
432 |
|
|
goto lookup_reply_error; |
433 |
|
|
} |
434 |
|
|
|
435 |
|
|
lookup_reply_positive: |
436 |
|
|
if (!file_system_->IsNfsSource()) |
437 |
|
|
mount_point_->inode_tracker()->VfsGet(dirent.inode(), path); |
438 |
|
|
fuse_remounter_->fence()->Leave(); |
439 |
|
|
result.ino = dirent.inode(); |
440 |
|
|
result.attr = dirent.GetStatStructure(); |
441 |
|
|
fuse_reply_entry(req, &result); |
442 |
|
|
return; |
443 |
|
|
|
444 |
|
|
lookup_reply_negative: |
445 |
|
|
fuse_remounter_->fence()->Leave(); |
446 |
|
|
perf::Inc(file_system_->n_fs_lookup_negative()); |
447 |
|
|
result.ino = 0; |
448 |
|
|
fuse_reply_entry(req, &result); |
449 |
|
|
return; |
450 |
|
|
|
451 |
|
|
lookup_reply_error: |
452 |
|
|
fuse_remounter_->fence()->Leave(); |
453 |
|
|
fuse_reply_err(req, EIO); |
454 |
|
|
} |
455 |
|
|
|
456 |
|
|
|
457 |
|
|
/** |
458 |
|
|
* |
459 |
|
|
*/ |
460 |
|
|
static void cvmfs_forget( |
461 |
|
|
fuse_req_t req, |
462 |
|
|
fuse_ino_t ino, |
463 |
|
|
unsigned long nlookup // NOLINT |
464 |
|
|
) { |
465 |
|
|
perf::Inc(file_system_->n_fs_forget()); |
466 |
|
|
|
467 |
|
|
// The libfuse high-level library does the same |
468 |
|
|
if (ino == FUSE_ROOT_ID) { |
469 |
|
|
fuse_reply_none(req); |
470 |
|
|
return; |
471 |
|
|
} |
472 |
|
|
|
473 |
|
|
fuse_remounter_->fence()->Enter(); |
474 |
|
|
ino = mount_point_->catalog_mgr()->MangleInode(ino); |
475 |
|
|
// This has been seen to deadlock on the debug log mutex on SL5. Problem of |
476 |
|
|
// old kernel/fuse? |
477 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "forget on inode %" PRIu64 " by %u", |
478 |
|
|
uint64_t(ino), nlookup); |
479 |
|
|
if (!file_system_->IsNfsSource()) |
480 |
|
|
mount_point_->inode_tracker()->VfsPut(ino, nlookup); |
481 |
|
|
fuse_remounter_->fence()->Leave(); |
482 |
|
|
fuse_reply_none(req); |
483 |
|
|
} |
484 |
|
|
|
485 |
|
|
|
486 |
|
|
/** |
487 |
|
|
* Looks into dirent to decide if this is an EIO negative reply or an |
488 |
|
|
* ENOENT negative reply |
489 |
|
|
*/ |
490 |
|
|
static void ReplyNegative(const catalog::DirectoryEntry &dirent, |
491 |
|
|
fuse_req_t req) |
492 |
|
|
{ |
493 |
|
|
if (dirent.GetSpecial() == catalog::kDirentNegative) |
494 |
|
|
fuse_reply_err(req, ENOENT); |
495 |
|
|
else |
496 |
|
|
fuse_reply_err(req, EIO); |
497 |
|
|
} |
498 |
|
|
|
499 |
|
|
|
500 |
|
|
/** |
501 |
|
|
* Transform a cvmfs dirent into a struct stat. |
502 |
|
|
*/ |
503 |
|
|
static void cvmfs_getattr(fuse_req_t req, fuse_ino_t ino, |
504 |
|
|
struct fuse_file_info *fi) |
505 |
|
|
{ |
506 |
|
|
perf::Inc(file_system_->n_fs_stat()); |
507 |
|
|
const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req); |
508 |
|
|
ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid); |
509 |
|
|
fuse_remounter_->TryFinish(); |
510 |
|
|
|
511 |
|
|
fuse_remounter_->fence()->Enter(); |
512 |
|
|
ino = mount_point_->catalog_mgr()->MangleInode(ino); |
513 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_getattr (stat) for inode: %" PRIu64, |
514 |
|
|
uint64_t(ino)); |
515 |
|
|
|
516 |
|
|
if (!CheckVoms(*fuse_ctx)) { |
517 |
|
|
fuse_remounter_->fence()->Leave(); |
518 |
|
|
fuse_reply_err(req, EACCES); |
519 |
|
|
return; |
520 |
|
|
} |
521 |
|
|
catalog::DirectoryEntry dirent; |
522 |
|
|
const bool found = GetDirentForInode(ino, &dirent); |
523 |
|
|
TraceInode(Tracer::kEventGetAttr, ino, "getattr()"); |
524 |
|
|
fuse_remounter_->fence()->Leave(); |
525 |
|
|
|
526 |
|
|
if (!found) { |
527 |
|
|
ReplyNegative(dirent, req); |
528 |
|
|
return; |
529 |
|
|
} |
530 |
|
|
|
531 |
|
|
struct stat info = dirent.GetStatStructure(); |
532 |
|
|
|
533 |
|
|
fuse_reply_attr(req, &info, GetKcacheTimeout()); |
534 |
|
|
} |
535 |
|
|
|
536 |
|
|
|
537 |
|
|
/** |
538 |
|
|
* Reads a symlink from the catalog. Environment variables are expanded. |
539 |
|
|
*/ |
540 |
|
|
static void cvmfs_readlink(fuse_req_t req, fuse_ino_t ino) { |
541 |
|
|
perf::Inc(file_system_->n_fs_readlink()); |
542 |
|
|
const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req); |
543 |
|
|
ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid); |
544 |
|
|
|
545 |
|
|
fuse_remounter_->fence()->Enter(); |
546 |
|
|
ino = mount_point_->catalog_mgr()->MangleInode(ino); |
547 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_readlink on inode: %" PRIu64, |
548 |
|
|
uint64_t(ino)); |
549 |
|
|
|
550 |
|
|
catalog::DirectoryEntry dirent; |
551 |
|
|
const bool found = GetDirentForInode(ino, &dirent); |
552 |
|
|
TraceInode(Tracer::kEventReadlink, ino, "readlink()"); |
553 |
|
|
fuse_remounter_->fence()->Leave(); |
554 |
|
|
|
555 |
|
|
if (!found) { |
556 |
|
|
ReplyNegative(dirent, req); |
557 |
|
|
return; |
558 |
|
|
} |
559 |
|
|
|
560 |
|
|
if (!dirent.IsLink()) { |
561 |
|
|
fuse_reply_err(req, EINVAL); |
562 |
|
|
return; |
563 |
|
|
} |
564 |
|
|
|
565 |
|
|
fuse_reply_readlink(req, dirent.symlink().c_str()); |
566 |
|
|
} |
567 |
|
|
|
568 |
|
|
|
569 |
|
|
static void AddToDirListing(const fuse_req_t req, |
570 |
|
|
const char *name, const struct stat *stat_info, |
571 |
|
|
BigVector<char> *listing) |
572 |
|
|
{ |
573 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "Add to listing: %s, inode %" PRIu64, |
574 |
|
|
name, uint64_t(stat_info->st_ino)); |
575 |
|
|
size_t remaining_size = listing->capacity() - listing->size(); |
576 |
|
|
const size_t entry_size = fuse_add_direntry(req, NULL, 0, name, stat_info, 0); |
577 |
|
|
|
578 |
|
|
while (entry_size > remaining_size) { |
579 |
|
|
listing->DoubleCapacity(); |
580 |
|
|
remaining_size = listing->capacity() - listing->size(); |
581 |
|
|
} |
582 |
|
|
|
583 |
|
|
char *buffer; |
584 |
|
|
bool large_alloc; |
585 |
|
|
listing->ShareBuffer(&buffer, &large_alloc); |
586 |
|
|
fuse_add_direntry(req, buffer + listing->size(), |
587 |
|
|
remaining_size, name, stat_info, |
588 |
|
|
listing->size() + entry_size); |
589 |
|
|
listing->SetSize(listing->size() + entry_size); |
590 |
|
|
} |
591 |
|
|
|
592 |
|
|
|
593 |
|
|
/** |
594 |
|
|
* Open a directory for listing. |
595 |
|
|
*/ |
596 |
|
|
static void cvmfs_opendir(fuse_req_t req, fuse_ino_t ino, |
597 |
|
|
struct fuse_file_info *fi) |
598 |
|
|
{ |
599 |
|
|
const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req); |
600 |
|
|
ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid); |
601 |
|
|
fuse_remounter_->TryFinish(); |
602 |
|
|
|
603 |
|
|
fuse_remounter_->fence()->Enter(); |
604 |
|
|
catalog::ClientCatalogManager *catalog_mgr = mount_point_->catalog_mgr(); |
605 |
|
|
ino = catalog_mgr->MangleInode(ino); |
606 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_opendir on inode: %" PRIu64, |
607 |
|
|
uint64_t(ino)); |
608 |
|
|
if (!CheckVoms(*fuse_ctx)) { |
609 |
|
|
fuse_remounter_->fence()->Leave(); |
610 |
|
|
fuse_reply_err(req, EACCES); |
611 |
|
|
return; |
612 |
|
|
} |
613 |
|
|
|
614 |
|
|
TraceInode(Tracer::kEventOpenDir, ino, "opendir()"); |
615 |
|
|
PathString path; |
616 |
|
|
catalog::DirectoryEntry d; |
617 |
|
|
bool found = GetPathForInode(ino, &path); |
618 |
|
|
if (!found) { |
619 |
|
|
fuse_remounter_->fence()->Leave(); |
620 |
|
|
fuse_reply_err(req, ENOENT); |
621 |
|
|
return; |
622 |
|
|
} |
623 |
|
|
found = GetDirentForInode(ino, &d); |
624 |
|
|
|
625 |
|
|
if (!found) { |
626 |
|
|
fuse_remounter_->fence()->Leave(); |
627 |
|
|
ReplyNegative(d, req); |
628 |
|
|
return; |
629 |
|
|
} |
630 |
|
|
if (!d.IsDirectory()) { |
631 |
|
|
fuse_remounter_->fence()->Leave(); |
632 |
|
|
fuse_reply_err(req, ENOTDIR); |
633 |
|
|
return; |
634 |
|
|
} |
635 |
|
|
|
636 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_opendir on inode: %" PRIu64 ", path %s", |
637 |
|
|
uint64_t(ino), path.c_str()); |
638 |
|
|
|
639 |
|
|
// Build listing |
640 |
|
|
BigVector<char> fuse_listing(512); |
641 |
|
|
|
642 |
|
|
// Add current directory link |
643 |
|
|
struct stat info; |
644 |
|
|
info = d.GetStatStructure(); |
645 |
|
|
AddToDirListing(req, ".", &info, &fuse_listing); |
646 |
|
|
|
647 |
|
|
// Add parent directory link |
648 |
|
|
catalog::DirectoryEntry p; |
649 |
|
|
if (d.inode() != catalog_mgr->GetRootInode() && |
650 |
|
|
GetDirentForPath(GetParentPath(path), &p)) |
651 |
|
|
{ |
652 |
|
|
info = p.GetStatStructure(); |
653 |
|
|
AddToDirListing(req, "..", &info, &fuse_listing); |
654 |
|
|
} |
655 |
|
|
|
656 |
|
|
// Add all names |
657 |
|
|
catalog::StatEntryList listing_from_catalog; |
658 |
|
|
bool retval = catalog_mgr->ListingStat(path, &listing_from_catalog); |
659 |
|
|
|
660 |
|
|
if (!retval) { |
661 |
|
|
fuse_remounter_->fence()->Leave(); |
662 |
|
|
fuse_listing.Clear(); // Buffer is shared, empty manually |
663 |
|
|
fuse_reply_err(req, EIO); |
664 |
|
|
return; |
665 |
|
|
} |
666 |
|
|
for (unsigned i = 0; i < listing_from_catalog.size(); ++i) { |
667 |
|
|
// Fix inodes |
668 |
|
|
PathString entry_path; |
669 |
|
|
entry_path.Assign(path); |
670 |
|
|
entry_path.Append("/", 1); |
671 |
|
|
entry_path.Append(listing_from_catalog.AtPtr(i)->name.GetChars(), |
672 |
|
|
listing_from_catalog.AtPtr(i)->name.GetLength()); |
673 |
|
|
|
674 |
|
|
catalog::DirectoryEntry entry_dirent; |
675 |
|
|
if (!GetDirentForPath(entry_path, &entry_dirent)) { |
676 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "listing entry %s vanished, skipping", |
677 |
|
|
entry_path.c_str()); |
678 |
|
|
continue; |
679 |
|
|
} |
680 |
|
|
|
681 |
|
|
struct stat fixed_info = listing_from_catalog.AtPtr(i)->info; |
682 |
|
|
fixed_info.st_ino = entry_dirent.inode(); |
683 |
|
|
AddToDirListing(req, listing_from_catalog.AtPtr(i)->name.c_str(), |
684 |
|
|
&fixed_info, &fuse_listing); |
685 |
|
|
} |
686 |
|
|
fuse_remounter_->fence()->Leave(); |
687 |
|
|
|
688 |
|
|
DirectoryListing stream_listing; |
689 |
|
|
stream_listing.size = fuse_listing.size(); |
690 |
|
|
stream_listing.capacity = fuse_listing.capacity(); |
691 |
|
|
bool large_alloc; |
692 |
|
|
fuse_listing.ShareBuffer(&stream_listing.buffer, &large_alloc); |
693 |
|
|
if (large_alloc) |
694 |
|
|
stream_listing.capacity = 0; |
695 |
|
|
|
696 |
|
|
// Save the directory listing and return a handle to the listing |
697 |
|
|
pthread_mutex_lock(&lock_directory_handles_); |
698 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, |
699 |
|
|
"linking directory handle %d to dir inode: %" PRIu64, |
700 |
|
|
next_directory_handle_, uint64_t(ino)); |
701 |
|
|
(*directory_handles_)[next_directory_handle_] = stream_listing; |
702 |
|
|
fi->fh = next_directory_handle_; |
703 |
|
|
++next_directory_handle_; |
704 |
|
|
pthread_mutex_unlock(&lock_directory_handles_); |
705 |
|
|
perf::Inc(file_system_->n_fs_dir_open()); |
706 |
|
|
perf::Inc(file_system_->no_open_dirs()); |
707 |
|
|
|
708 |
|
|
fuse_reply_open(req, fi); |
709 |
|
|
} |
710 |
|
|
|
711 |
|
|
|
712 |
|
|
/** |
713 |
|
|
* Release a directory. |
714 |
|
|
*/ |
715 |
|
|
static void cvmfs_releasedir(fuse_req_t req, fuse_ino_t ino, |
716 |
|
|
struct fuse_file_info *fi) |
717 |
|
|
{ |
718 |
|
|
ino = mount_point_->catalog_mgr()->MangleInode(ino); |
719 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_releasedir on inode %" PRIu64 |
720 |
|
|
", handle %d", uint64_t(ino), fi->fh); |
721 |
|
|
|
722 |
|
|
int reply = 0; |
723 |
|
|
|
724 |
|
|
pthread_mutex_lock(&lock_directory_handles_); |
725 |
|
|
DirectoryHandles::iterator iter_handle = |
726 |
|
|
directory_handles_->find(fi->fh); |
727 |
|
|
if (iter_handle != directory_handles_->end()) { |
728 |
|
|
if (iter_handle->second.capacity == 0) |
729 |
|
|
smunmap(iter_handle->second.buffer); |
730 |
|
|
else |
731 |
|
|
free(iter_handle->second.buffer); |
732 |
|
|
directory_handles_->erase(iter_handle); |
733 |
|
|
pthread_mutex_unlock(&lock_directory_handles_); |
734 |
|
|
perf::Dec(file_system_->no_open_dirs()); |
735 |
|
|
} else { |
736 |
|
|
pthread_mutex_unlock(&lock_directory_handles_); |
737 |
|
|
reply = EINVAL; |
738 |
|
|
} |
739 |
|
|
|
740 |
|
|
fuse_reply_err(req, reply); |
741 |
|
|
} |
742 |
|
|
|
743 |
|
|
|
744 |
|
|
/** |
745 |
|
|
* Very large directory listings have to be sent in slices. |
746 |
|
|
*/ |
747 |
|
|
static void ReplyBufferSlice(const fuse_req_t req, const char *buffer, |
748 |
|
|
const size_t buffer_size, const off_t offset, |
749 |
|
|
const size_t max_size) |
750 |
|
|
{ |
751 |
|
|
if (offset < static_cast<int>(buffer_size)) { |
752 |
|
|
fuse_reply_buf(req, buffer + offset, |
753 |
|
|
std::min(static_cast<size_t>(buffer_size - offset), max_size)); |
754 |
|
|
} else { |
755 |
|
|
fuse_reply_buf(req, NULL, 0); |
756 |
|
|
} |
757 |
|
|
} |
758 |
|
|
|
759 |
|
|
|
760 |
|
|
/** |
761 |
|
|
* Read the directory listing. |
762 |
|
|
*/ |
763 |
|
|
static void cvmfs_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, |
764 |
|
|
off_t off, struct fuse_file_info *fi) |
765 |
|
|
{ |
766 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, |
767 |
|
|
"cvmfs_readdir on inode %" PRIu64 " reading %d bytes from offset %d", |
768 |
|
|
uint64_t(mount_point_->catalog_mgr()->MangleInode(ino)), size, off); |
769 |
|
|
|
770 |
|
|
DirectoryListing listing; |
771 |
|
|
|
772 |
|
|
pthread_mutex_lock(&lock_directory_handles_); |
773 |
|
|
DirectoryHandles::const_iterator iter_handle = |
774 |
|
|
directory_handles_->find(fi->fh); |
775 |
|
|
if (iter_handle != directory_handles_->end()) { |
776 |
|
|
listing = iter_handle->second; |
777 |
|
|
pthread_mutex_unlock(&lock_directory_handles_); |
778 |
|
|
|
779 |
|
|
ReplyBufferSlice(req, listing.buffer, listing.size, off, size); |
780 |
|
|
return; |
781 |
|
|
} |
782 |
|
|
|
783 |
|
|
pthread_mutex_unlock(&lock_directory_handles_); |
784 |
|
|
fuse_reply_err(req, EINVAL); |
785 |
|
|
} |
786 |
|
|
|
787 |
|
|
|
788 |
|
|
/** |
789 |
|
|
* Open a file from cache. If necessary, file is downloaded first. |
790 |
|
|
* |
791 |
|
|
* \return Read-only file descriptor in fi->fh or kChunkedFileHandle for |
792 |
|
|
* chunked files |
793 |
|
|
*/ |
794 |
|
|
static void cvmfs_open(fuse_req_t req, fuse_ino_t ino, |
795 |
|
|
struct fuse_file_info *fi) |
796 |
|
|
{ |
797 |
|
|
const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req); |
798 |
|
|
ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid); |
799 |
|
|
fuse_remounter_->fence()->Enter(); |
800 |
|
|
catalog::ClientCatalogManager *catalog_mgr = mount_point_->catalog_mgr(); |
801 |
|
|
ino = catalog_mgr->MangleInode(ino); |
802 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_open on inode: %" PRIu64, |
803 |
|
|
uint64_t(ino)); |
804 |
|
|
|
805 |
|
|
int fd = -1; |
806 |
|
|
catalog::DirectoryEntry dirent; |
807 |
|
|
PathString path; |
808 |
|
|
|
809 |
|
|
bool found = GetPathForInode(ino, &path); |
810 |
|
|
if (!found) { |
811 |
|
|
fuse_remounter_->fence()->Leave(); |
812 |
|
|
fuse_reply_err(req, ENOENT); |
813 |
|
|
return; |
814 |
|
|
} |
815 |
|
|
found = GetDirentForInode(ino, &dirent); |
816 |
|
|
if (!found) { |
817 |
|
|
fuse_remounter_->fence()->Leave(); |
818 |
|
|
ReplyNegative(dirent, req); |
819 |
|
|
return; |
820 |
|
|
} |
821 |
|
|
|
822 |
|
|
if (!CheckVoms(*fuse_ctx)) { |
823 |
|
|
fuse_remounter_->fence()->Leave(); |
824 |
|
|
fuse_reply_err(req, EACCES); |
825 |
|
|
return; |
826 |
|
|
} |
827 |
|
|
|
828 |
|
|
mount_point_->tracer()->Trace(Tracer::kEventOpen, path, "open()"); |
829 |
|
|
// Don't check. Either done by the OS or one wants to purposefully work |
830 |
|
|
// around wrong open flags |
831 |
|
|
// if ((fi->flags & 3) != O_RDONLY) { |
832 |
|
|
// fuse_reply_err(req, EROFS); |
833 |
|
|
// return; |
834 |
|
|
// } |
835 |
|
|
#ifdef __APPLE__ |
836 |
|
|
if ((fi->flags & O_SHLOCK) || (fi->flags & O_EXLOCK)) { |
837 |
|
|
fuse_remounter_->fence()->Leave(); |
838 |
|
|
fuse_reply_err(req, EOPNOTSUPP); |
839 |
|
|
return; |
840 |
|
|
} |
841 |
|
|
#endif |
842 |
|
|
if (fi->flags & O_EXCL) { |
843 |
|
|
fuse_remounter_->fence()->Leave(); |
844 |
|
|
fuse_reply_err(req, EEXIST); |
845 |
|
|
return; |
846 |
|
|
} |
847 |
|
|
|
848 |
|
|
perf::Inc(file_system_->n_fs_open()); // Count actual open / fetch operations |
849 |
|
|
|
850 |
|
|
if (!dirent.IsChunkedFile()) { |
851 |
|
|
fuse_remounter_->fence()->Leave(); |
852 |
|
|
} else { |
853 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, |
854 |
|
|
"chunked file %s opened (download delayed to read() call)", |
855 |
|
|
path.c_str()); |
856 |
|
|
|
857 |
|
|
if (perf::Xadd(file_system_->no_open_files(), 1) >= |
858 |
|
|
(static_cast<int>(max_open_files_))-kNumReservedFd) |
859 |
|
|
{ |
860 |
|
|
perf::Dec(file_system_->no_open_files()); |
861 |
|
|
fuse_remounter_->fence()->Leave(); |
862 |
|
|
LogCvmfs(kLogCvmfs, kLogSyslogErr, "open file descriptor limit exceeded"); |
863 |
|
|
fuse_reply_err(req, EMFILE); |
864 |
|
|
return; |
865 |
|
|
} |
866 |
|
|
|
867 |
|
|
// Figure out unique inode from annotated catalog |
868 |
|
|
catalog::DirectoryEntry dirent_origin; |
869 |
|
|
if (!catalog_mgr->LookupPath(path, catalog::kLookupSole, &dirent_origin)) { |
870 |
|
|
fuse_remounter_->fence()->Leave(); |
871 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr, |
872 |
|
|
"chunked file %s vanished unexpectedly", path.c_str()); |
873 |
|
|
fuse_reply_err(req, ENOENT); |
874 |
|
|
return; |
875 |
|
|
} |
876 |
|
|
const uint64_t unique_inode = dirent_origin.inode(); |
877 |
|
|
|
878 |
|
|
ChunkTables *chunk_tables = mount_point_->chunk_tables(); |
879 |
|
|
chunk_tables->Lock(); |
880 |
|
|
if (!chunk_tables->inode2chunks.Contains(unique_inode)) { |
881 |
|
|
chunk_tables->Unlock(); |
882 |
|
|
|
883 |
|
|
// Retrieve File chunks from the catalog |
884 |
|
|
UniquePtr<FileChunkList> chunks(new FileChunkList()); |
885 |
|
|
if (!catalog_mgr->ListFileChunks(path, dirent.hash_algorithm(), |
886 |
|
|
chunks.weak_ref()) || |
887 |
|
|
chunks->IsEmpty()) |
888 |
|
|
{ |
889 |
|
|
fuse_remounter_->fence()->Leave(); |
890 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug| kLogSyslogErr, "file %s is marked as " |
891 |
|
|
"'chunked', but no chunks found.", path.c_str()); |
892 |
|
|
fuse_reply_err(req, EIO); |
893 |
|
|
return; |
894 |
|
|
} |
895 |
|
|
fuse_remounter_->fence()->Leave(); |
896 |
|
|
|
897 |
|
|
chunk_tables->Lock(); |
898 |
|
|
// Check again to avoid race |
899 |
|
|
if (!chunk_tables->inode2chunks.Contains(unique_inode)) { |
900 |
|
|
chunk_tables->inode2chunks.Insert( |
901 |
|
|
unique_inode, FileChunkReflist(chunks.Release(), path, |
902 |
|
|
dirent.compression_algorithm(), |
903 |
|
|
dirent.IsExternalFile())); |
904 |
|
|
chunk_tables->inode2references.Insert(unique_inode, 1); |
905 |
|
|
} else { |
906 |
|
|
uint32_t refctr; |
907 |
|
|
bool retval = |
908 |
|
|
chunk_tables->inode2references.Lookup(unique_inode, &refctr); |
909 |
|
|
assert(retval); |
910 |
|
|
chunk_tables->inode2references.Insert(unique_inode, refctr+1); |
911 |
|
|
} |
912 |
|
|
} else { |
913 |
|
|
fuse_remounter_->fence()->Leave(); |
914 |
|
|
uint32_t refctr; |
915 |
|
|
bool retval = |
916 |
|
|
chunk_tables->inode2references.Lookup(unique_inode, &refctr); |
917 |
|
|
assert(retval); |
918 |
|
|
chunk_tables->inode2references.Insert(unique_inode, refctr+1); |
919 |
|
|
} |
920 |
|
|
|
921 |
|
|
// Update the chunk handle list |
922 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, |
923 |
|
|
"linking chunk handle %d to unique inode: %" PRIu64, |
924 |
|
|
chunk_tables->next_handle, uint64_t(unique_inode)); |
925 |
|
|
chunk_tables->handle2fd.Insert(chunk_tables->next_handle, ChunkFd()); |
926 |
|
|
chunk_tables->handle2uniqino.Insert(chunk_tables->next_handle, |
927 |
|
|
unique_inode); |
928 |
|
|
// The same inode can refer to different revisions of a path. Don't cache. |
929 |
|
|
fi->keep_cache = 0; |
930 |
|
|
fi->fh = static_cast<uint64_t>(-chunk_tables->next_handle); |
931 |
|
|
++chunk_tables->next_handle; |
932 |
|
|
chunk_tables->Unlock(); |
933 |
|
|
|
934 |
|
|
fuse_reply_open(req, fi); |
935 |
|
|
return; |
936 |
|
|
} |
937 |
|
|
|
938 |
|
|
Fetcher *this_fetcher = dirent.IsExternalFile() |
939 |
|
|
? mount_point_->external_fetcher() |
940 |
|
|
: mount_point_->fetcher(); |
941 |
|
|
fd = this_fetcher->Fetch( |
942 |
|
|
dirent.checksum(), |
943 |
|
|
dirent.size(), |
944 |
|
|
string(path.GetChars(), path.GetLength()), |
945 |
|
|
dirent.compression_algorithm(), |
946 |
|
|
mount_point_->catalog_mgr()->volatile_flag() |
947 |
|
|
? CacheManager::kTypeVolatile |
948 |
|
|
: CacheManager::kTypeRegular); |
949 |
|
|
|
950 |
|
|
if (fd >= 0) { |
951 |
|
|
if (perf::Xadd(file_system_->no_open_files(), 1) < |
952 |
|
|
(static_cast<int>(max_open_files_))-kNumReservedFd) { |
953 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "file %s opened (fd %d)", |
954 |
|
|
path.c_str(), fd); |
955 |
|
|
// The same inode can refer to different revisions of a path. Don't cache. |
956 |
|
|
fi->keep_cache = 0; |
957 |
|
|
fi->fh = fd; |
958 |
|
|
fuse_reply_open(req, fi); |
959 |
|
|
return; |
960 |
|
|
} else { |
961 |
|
|
if (file_system_->cache_mgr()->Close(fd) == 0) |
962 |
|
|
perf::Dec(file_system_->no_open_files()); |
963 |
|
|
LogCvmfs(kLogCvmfs, kLogSyslogErr, "open file descriptor limit exceeded"); |
964 |
|
|
fuse_reply_err(req, EMFILE); |
965 |
|
|
return; |
966 |
|
|
} |
967 |
|
|
assert(false); |
968 |
|
|
} |
969 |
|
|
|
970 |
|
|
// fd < 0 |
971 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr, |
972 |
|
|
"failed to open inode: %" PRIu64 ", CAS key %s, error code %d", |
973 |
|
|
uint64_t(ino), dirent.checksum().ToString().c_str(), errno); |
974 |
|
|
if (errno == EMFILE) { |
975 |
|
|
fuse_reply_err(req, EMFILE); |
976 |
|
|
return; |
977 |
|
|
} |
978 |
|
|
|
979 |
|
|
mount_point_->backoff_throttle()->Throttle(); |
980 |
|
|
|
981 |
|
|
perf::Inc(file_system_->n_io_error()); |
982 |
|
|
fuse_reply_err(req, -fd); |
983 |
|
|
} |
984 |
|
|
|
985 |
|
|
|
986 |
|
|
/** |
987 |
|
|
* Redirected to pread into cache. |
988 |
|
|
*/ |
989 |
|
|
static void cvmfs_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, |
990 |
|
|
struct fuse_file_info *fi) |
991 |
|
|
{ |
992 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, |
993 |
|
|
"cvmfs_read inode: %" PRIu64 " reading %d bytes from offset %d " |
994 |
|
|
"fd %d", uint64_t(mount_point_->catalog_mgr()->MangleInode(ino)), |
995 |
|
|
size, off, fi->fh); |
996 |
|
|
perf::Inc(file_system_->n_fs_read()); |
997 |
|
|
|
998 |
|
|
// Get data chunk (<=128k guaranteed by Fuse) |
999 |
|
|
char *data = static_cast<char *>(alloca(size)); |
1000 |
|
|
unsigned int overall_bytes_fetched = 0; |
1001 |
|
|
|
1002 |
|
|
// Do we have a a chunked file? |
1003 |
|
|
if (static_cast<int64_t>(fi->fh) < 0) { |
1004 |
|
|
const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req); |
1005 |
|
|
ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid); |
1006 |
|
|
|
1007 |
|
|
const uint64_t chunk_handle = |
1008 |
|
|
static_cast<uint64_t>(-static_cast<int64_t>(fi->fh)); |
1009 |
|
|
uint64_t unique_inode; |
1010 |
|
|
ChunkFd chunk_fd; |
1011 |
|
|
FileChunkReflist chunks; |
1012 |
|
|
bool retval; |
1013 |
|
|
|
1014 |
|
|
// Fetch unique inode, chunk list and file descriptor |
1015 |
|
|
ChunkTables *chunk_tables = mount_point_->chunk_tables(); |
1016 |
|
|
chunk_tables->Lock(); |
1017 |
|
|
retval = chunk_tables->handle2uniqino.Lookup(chunk_handle, &unique_inode); |
1018 |
|
|
if (!retval) { |
1019 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "no unique inode, fall back to fuse ino"); |
1020 |
|
|
unique_inode = ino; |
1021 |
|
|
} |
1022 |
|
|
retval = chunk_tables->inode2chunks.Lookup(unique_inode, &chunks); |
1023 |
|
|
assert(retval); |
1024 |
|
|
chunk_tables->Unlock(); |
1025 |
|
|
|
1026 |
|
|
unsigned chunk_idx = chunks.FindChunkIdx(off); |
1027 |
|
|
|
1028 |
|
|
// Lock chunk handle |
1029 |
|
|
pthread_mutex_t *handle_lock = chunk_tables->Handle2Lock(chunk_handle); |
1030 |
|
|
LockMutex(handle_lock); |
1031 |
|
|
chunk_tables->Lock(); |
1032 |
|
|
retval = chunk_tables->handle2fd.Lookup(chunk_handle, &chunk_fd); |
1033 |
|
|
assert(retval); |
1034 |
|
|
chunk_tables->Unlock(); |
1035 |
|
|
|
1036 |
|
|
// Fetch all needed chunks and read the requested data |
1037 |
|
|
off_t offset_in_chunk = off - chunks.list->AtPtr(chunk_idx)->offset(); |
1038 |
|
|
do { |
1039 |
|
|
// Open file descriptor to chunk |
1040 |
|
|
if ((chunk_fd.fd == -1) || (chunk_fd.chunk_idx != chunk_idx)) { |
1041 |
|
|
if (chunk_fd.fd != -1) file_system_->cache_mgr()->Close(chunk_fd.fd); |
1042 |
|
|
string verbose_path = "Part of " + chunks.path.ToString(); |
1043 |
|
|
if (chunks.external_data) { |
1044 |
|
|
chunk_fd.fd = mount_point_->external_fetcher()->Fetch( |
1045 |
|
|
chunks.list->AtPtr(chunk_idx)->content_hash(), |
1046 |
|
|
chunks.list->AtPtr(chunk_idx)->size(), |
1047 |
|
|
verbose_path, |
1048 |
|
|
chunks.compression_alg, |
1049 |
|
|
mount_point_->catalog_mgr()->volatile_flag() |
1050 |
|
|
? CacheManager::kTypeVolatile |
1051 |
|
|
: CacheManager::kTypeRegular, |
1052 |
|
|
chunks.path.ToString(), |
1053 |
|
|
chunks.list->AtPtr(chunk_idx)->offset()); |
1054 |
|
|
} else { |
1055 |
|
|
chunk_fd.fd = mount_point_->fetcher()->Fetch( |
1056 |
|
|
chunks.list->AtPtr(chunk_idx)->content_hash(), |
1057 |
|
|
chunks.list->AtPtr(chunk_idx)->size(), |
1058 |
|
|
verbose_path, |
1059 |
|
|
chunks.compression_alg, |
1060 |
|
|
mount_point_->catalog_mgr()->volatile_flag() |
1061 |
|
|
? CacheManager::kTypeVolatile |
1062 |
|
|
: CacheManager::kTypeRegular); |
1063 |
|
|
} |
1064 |
|
|
if (chunk_fd.fd < 0) { |
1065 |
|
|
chunk_fd.fd = -1; |
1066 |
|
|
chunk_tables->Lock(); |
1067 |
|
|
chunk_tables->handle2fd.Insert(chunk_handle, chunk_fd); |
1068 |
|
|
chunk_tables->Unlock(); |
1069 |
|
|
UnlockMutex(handle_lock); |
1070 |
|
|
fuse_reply_err(req, EIO); |
1071 |
|
|
return; |
1072 |
|
|
} |
1073 |
|
|
chunk_fd.chunk_idx = chunk_idx; |
1074 |
|
|
} |
1075 |
|
|
|
1076 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "reading from chunk fd %d", |
1077 |
|
|
chunk_fd.fd); |
1078 |
|
|
// Read data from chunk |
1079 |
|
|
const size_t bytes_to_read = size - overall_bytes_fetched; |
1080 |
|
|
const size_t remaining_bytes_in_chunk = |
1081 |
|
|
chunks.list->AtPtr(chunk_idx)->size() - offset_in_chunk; |
1082 |
|
|
size_t bytes_to_read_in_chunk = |
1083 |
|
|
std::min(bytes_to_read, remaining_bytes_in_chunk); |
1084 |
|
|
const int64_t bytes_fetched = file_system_->cache_mgr()->Pread( |
1085 |
|
|
chunk_fd.fd, |
1086 |
|
|
data + overall_bytes_fetched, |
1087 |
|
|
bytes_to_read_in_chunk, |
1088 |
|
|
offset_in_chunk); |
1089 |
|
|
|
1090 |
|
|
if (bytes_fetched < 0) { |
1091 |
|
|
LogCvmfs(kLogCvmfs, kLogSyslogErr, "read err no %" PRId64 " (%s)", |
1092 |
|
|
bytes_fetched, chunks.path.ToString().c_str()); |
1093 |
|
|
chunk_tables->Lock(); |
1094 |
|
|
chunk_tables->handle2fd.Insert(chunk_handle, chunk_fd); |
1095 |
|
|
chunk_tables->Unlock(); |
1096 |
|
|
UnlockMutex(handle_lock); |
1097 |
|
|
fuse_reply_err(req, -bytes_fetched); |
1098 |
|
|
return; |
1099 |
|
|
} |
1100 |
|
|
overall_bytes_fetched += bytes_fetched; |
1101 |
|
|
|
1102 |
|
|
// Proceed to the next chunk to keep on reading data |
1103 |
|
|
++chunk_idx; |
1104 |
|
|
offset_in_chunk = 0; |
1105 |
|
|
} while ((overall_bytes_fetched < size) && |
1106 |
|
|
(chunk_idx < chunks.list->size())); |
1107 |
|
|
|
1108 |
|
|
// Update chunk file descriptor |
1109 |
|
|
chunk_tables->Lock(); |
1110 |
|
|
chunk_tables->handle2fd.Insert(chunk_handle, chunk_fd); |
1111 |
|
|
chunk_tables->Unlock(); |
1112 |
|
|
UnlockMutex(handle_lock); |
1113 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "released chunk file descriptor %d", |
1114 |
|
|
chunk_fd.fd); |
1115 |
|
|
} else { |
1116 |
|
|
const int64_t fd = fi->fh; |
1117 |
|
|
int64_t nbytes = file_system_->cache_mgr()->Pread(fd, data, size, off); |
1118 |
|
|
if (nbytes < 0) { |
1119 |
|
|
fuse_reply_err(req, -nbytes); |
1120 |
|
|
return; |
1121 |
|
|
} |
1122 |
|
|
overall_bytes_fetched = nbytes; |
1123 |
|
|
} |
1124 |
|
|
|
1125 |
|
|
// Push it to user |
1126 |
|
|
fuse_reply_buf(req, data, overall_bytes_fetched); |
1127 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "pushed %d bytes to user", |
1128 |
|
|
overall_bytes_fetched); |
1129 |
|
|
} |
1130 |
|
|
|
1131 |
|
|
|
1132 |
|
|
/** |
1133 |
|
|
* File close operation, redirected into cache. |
1134 |
|
|
*/ |
1135 |
|
|
static void cvmfs_release(fuse_req_t req, fuse_ino_t ino, |
1136 |
|
|
struct fuse_file_info *fi) |
1137 |
|
|
{ |
1138 |
|
|
ino = mount_point_->catalog_mgr()->MangleInode(ino); |
1139 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_release on inode: %" PRIu64, |
1140 |
|
|
uint64_t(ino)); |
1141 |
|
|
const int64_t fd = fi->fh; |
1142 |
|
|
|
1143 |
|
|
// do we have a chunked file? |
1144 |
|
|
if (static_cast<int64_t>(fi->fh) < 0) { |
1145 |
|
|
const uint64_t chunk_handle = |
1146 |
|
|
static_cast<uint64_t>(-static_cast<int64_t>(fi->fh)); |
1147 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "releasing chunk handle %" PRIu64, |
1148 |
|
|
chunk_handle); |
1149 |
|
|
uint64_t unique_inode; |
1150 |
|
|
ChunkFd chunk_fd; |
1151 |
|
|
FileChunkReflist chunks; |
1152 |
|
|
uint32_t refctr; |
1153 |
|
|
bool retval; |
1154 |
|
|
|
1155 |
|
|
ChunkTables *chunk_tables = mount_point_->chunk_tables(); |
1156 |
|
|
chunk_tables->Lock(); |
1157 |
|
|
retval = chunk_tables->handle2uniqino.Lookup(chunk_handle, &unique_inode); |
1158 |
|
|
if (!retval) { |
1159 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "no unique inode, fall back to fuse ino"); |
1160 |
|
|
unique_inode = ino; |
1161 |
|
|
} else { |
1162 |
|
|
chunk_tables->handle2uniqino.Erase(chunk_handle); |
1163 |
|
|
} |
1164 |
|
|
retval = chunk_tables->handle2fd.Lookup(chunk_handle, &chunk_fd); |
1165 |
|
|
assert(retval); |
1166 |
|
|
chunk_tables->handle2fd.Erase(chunk_handle); |
1167 |
|
|
|
1168 |
|
|
retval = chunk_tables->inode2references.Lookup(unique_inode, &refctr); |
1169 |
|
|
assert(retval); |
1170 |
|
|
refctr--; |
1171 |
|
|
if (refctr == 0) { |
1172 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "releasing chunk list for inode %" PRIu64, |
1173 |
|
|
uint64_t(unique_inode)); |
1174 |
|
|
FileChunkReflist to_delete; |
1175 |
|
|
retval = chunk_tables->inode2chunks.Lookup(unique_inode, &to_delete); |
1176 |
|
|
assert(retval); |
1177 |
|
|
chunk_tables->inode2references.Erase(unique_inode); |
1178 |
|
|
chunk_tables->inode2chunks.Erase(unique_inode); |
1179 |
|
|
delete to_delete.list; |
1180 |
|
|
} else { |
1181 |
|
|
chunk_tables->inode2references.Insert(unique_inode, refctr); |
1182 |
|
|
} |
1183 |
|
|
chunk_tables->Unlock(); |
1184 |
|
|
|
1185 |
|
|
if (chunk_fd.fd != -1) |
1186 |
|
|
file_system_->cache_mgr()->Close(chunk_fd.fd); |
1187 |
|
|
perf::Dec(file_system_->no_open_files()); |
1188 |
|
|
} else { |
1189 |
|
|
if (file_system_->cache_mgr()->Close(fd) == 0) { |
1190 |
|
|
perf::Dec(file_system_->no_open_files()); |
1191 |
|
|
} |
1192 |
|
|
} |
1193 |
|
|
fuse_reply_err(req, 0); |
1194 |
|
|
} |
1195 |
|
|
|
1196 |
|
|
|
1197 |
|
|
static void cvmfs_statfs(fuse_req_t req, fuse_ino_t ino) { |
1198 |
|
|
ino = mount_point_->catalog_mgr()->MangleInode(ino); |
1199 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_statfs on inode: %" PRIu64, |
1200 |
|
|
uint64_t(ino)); |
1201 |
|
|
|
1202 |
|
|
// If we return 0 it will cause the fs to be ignored in "df" |
1203 |
|
|
struct statvfs info; |
1204 |
|
|
memset(&info, 0, sizeof(info)); |
1205 |
|
|
|
1206 |
|
|
TraceInode(Tracer::kEventStatFs, ino, "statfs()"); |
1207 |
|
|
|
1208 |
|
|
// Unmanaged cache |
1209 |
|
|
if (!file_system_->cache_mgr()->quota_mgr()->HasCapability( |
1210 |
|
|
QuotaManager::kCapIntrospectSize)) |
1211 |
|
|
{ |
1212 |
|
|
fuse_reply_statfs(req, &info); |
1213 |
|
|
return; |
1214 |
|
|
} |
1215 |
|
|
|
1216 |
|
|
uint64_t available = 0; |
1217 |
|
|
uint64_t size = file_system_->cache_mgr()->quota_mgr()->GetSize(); |
1218 |
|
|
uint64_t capacity = file_system_->cache_mgr()->quota_mgr()->GetCapacity(); |
1219 |
|
|
// Fuse/OS X doesn't like values < 512 |
1220 |
|
|
info.f_bsize = info.f_frsize = 512; |
1221 |
|
|
|
1222 |
|
|
if (capacity == (uint64_t)(-1)) { |
1223 |
|
|
// Unknown capacity, set capacity = size |
1224 |
|
|
info.f_blocks = size / info.f_bsize; |
1225 |
|
|
} else { |
1226 |
|
|
// Take values from LRU module |
1227 |
|
|
info.f_blocks = capacity / info.f_bsize; |
1228 |
|
|
available = capacity - size; |
1229 |
|
|
} |
1230 |
|
|
|
1231 |
|
|
info.f_bfree = info.f_bavail = available / info.f_bsize; |
1232 |
|
|
|
1233 |
|
|
// Inodes / entries |
1234 |
|
|
fuse_remounter_->fence()->Enter(); |
1235 |
|
|
uint64_t all_inodes = mount_point_->catalog_mgr()->all_inodes(); |
1236 |
|
|
uint64_t loaded_inode = mount_point_->catalog_mgr()->loaded_inodes(); |
1237 |
|
|
info.f_files = all_inodes; |
1238 |
|
|
info.f_ffree = info.f_favail = all_inodes - loaded_inode; |
1239 |
|
|
fuse_remounter_->fence()->Leave(); |
1240 |
|
|
|
1241 |
|
|
fuse_reply_statfs(req, &info); |
1242 |
|
|
} |
1243 |
|
|
|
1244 |
|
|
|
1245 |
|
|
#ifdef __APPLE__ |
1246 |
|
|
static void cvmfs_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, |
1247 |
|
|
size_t size, uint32_t position) |
1248 |
|
|
#else |
1249 |
|
|
static void cvmfs_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, |
1250 |
|
|
size_t size) |
1251 |
|
|
#endif |
1252 |
|
|
{ |
1253 |
|
|
const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req); |
1254 |
|
|
ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid); |
1255 |
|
|
|
1256 |
|
|
fuse_remounter_->fence()->Enter(); |
1257 |
|
|
catalog::ClientCatalogManager *catalog_mgr = mount_point_->catalog_mgr(); |
1258 |
|
|
ino = catalog_mgr->MangleInode(ino); |
1259 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, |
1260 |
|
|
"cvmfs_getxattr on inode: %" PRIu64 " for xattr: %s", |
1261 |
|
|
uint64_t(ino), name); |
1262 |
|
|
if (!CheckVoms(*fuse_ctx)) { |
1263 |
|
|
fuse_remounter_->fence()->Leave(); |
1264 |
|
|
fuse_reply_err(req, EACCES); |
1265 |
|
|
return; |
1266 |
|
|
} |
1267 |
|
|
TraceInode(Tracer::kEventGetXAttr, ino, "getxattr()"); |
1268 |
|
|
|
1269 |
|
|
const string attr = name; |
1270 |
|
|
catalog::DirectoryEntry d; |
1271 |
|
|
const bool found = GetDirentForInode(ino, &d); |
1272 |
|
|
bool retval; |
1273 |
|
|
XattrList xattrs; |
1274 |
|
|
|
1275 |
|
|
PathString path; |
1276 |
|
|
retval = GetPathForInode(ino, &path); |
1277 |
|
|
assert(retval); |
1278 |
|
|
if (d.IsLink()) { |
1279 |
|
|
catalog::LookupOptions lookup_options = static_cast<catalog::LookupOptions>( |
1280 |
|
|
catalog::kLookupSole | catalog::kLookupRawSymlink); |
1281 |
|
|
catalog::DirectoryEntry raw_symlink; |
1282 |
|
|
retval = catalog_mgr->LookupPath(path, lookup_options, &raw_symlink); |
1283 |
|
|
assert(retval); |
1284 |
|
|
d.set_symlink(raw_symlink.symlink()); |
1285 |
|
|
} |
1286 |
|
|
if (d.HasXattrs()) { |
1287 |
|
|
retval = catalog_mgr->LookupXattrs(path, &xattrs); |
1288 |
|
|
assert(retval); |
1289 |
|
|
} |
1290 |
|
|
fuse_remounter_->fence()->Leave(); |
1291 |
|
|
|
1292 |
|
|
if (!found) { |
1293 |
|
|
ReplyNegative(d, req); |
1294 |
|
|
return; |
1295 |
|
|
} |
1296 |
|
|
|
1297 |
|
|
string attribute_value; |
1298 |
|
|
|
1299 |
|
|
if (attr == "user.pid") { |
1300 |
|
|
attribute_value = StringifyInt(pid_); |
1301 |
|
|
} else if (attr == "user.version") { |
1302 |
|
|
attribute_value = string(VERSION) + "." + string(CVMFS_PATCH_LEVEL); |
1303 |
|
|
} else if (attr == "user.pubkeys") { |
1304 |
|
|
attribute_value = mount_point_->signature_mgr()->GetActivePubkeys(); |
1305 |
|
|
} else if (attr == "user.hash") { |
1306 |
|
|
if (!d.checksum().IsNull()) { |
1307 |
|
|
attribute_value = d.checksum().ToString(); |
1308 |
|
|
} else { |
1309 |
|
|
fuse_reply_err(req, ENOATTR); |
1310 |
|
|
return; |
1311 |
|
|
} |
1312 |
|
|
} else if (attr == "user.lhash") { |
1313 |
|
|
if (!d.checksum().IsNull()) { |
1314 |
|
|
string result; |
1315 |
|
|
CacheManager::ObjectInfo object_info; |
1316 |
|
|
object_info.description = path.ToString(); |
1317 |
|
|
if (mount_point_->catalog_mgr()->volatile_flag()) |
1318 |
|
|
object_info.type = CacheManager::kTypeVolatile; |
1319 |
|
|
int fd = file_system_->cache_mgr()->Open( |
1320 |
|
|
CacheManager::Bless(d.checksum(), object_info)); |
1321 |
|
|
if (fd < 0) { |
1322 |
|
|
attribute_value = "Not in cache"; |
1323 |
|
|
} else { |
1324 |
|
|
shash::Any hash(d.checksum().algorithm); |
1325 |
|
|
int retval_i = file_system_->cache_mgr()->ChecksumFd(fd, &hash); |
1326 |
|
|
if (retval_i != 0) |
1327 |
|
|
attribute_value = "I/O error (" + StringifyInt(retval_i) + ")"; |
1328 |
|
|
else |
1329 |
|
|
attribute_value = hash.ToString(); |
1330 |
|
|
file_system_->cache_mgr()->Close(fd); |
1331 |
|
|
} |
1332 |
|
|
} else { |
1333 |
|
|
fuse_reply_err(req, ENOATTR); |
1334 |
|
|
return; |
1335 |
|
|
} |
1336 |
|
|
} else if ((attr == "xfsroot.rawlink") || (attr == "user.rawlink")) { |
1337 |
|
|
if (d.IsLink()) { |
1338 |
|
|
attribute_value = d.symlink().ToString(); |
1339 |
|
|
} else { |
1340 |
|
|
fuse_reply_err(req, ENOATTR); |
1341 |
|
|
return; |
1342 |
|
|
} |
1343 |
|
|
} else if (attr == "user.revision") { |
1344 |
|
|
const uint64_t revision = catalog_mgr->GetRevision(); |
1345 |
|
|
attribute_value = StringifyInt(revision); |
1346 |
|
|
} else if (attr == "user.root_hash") { |
1347 |
|
|
attribute_value = catalog_mgr->GetRootHash().ToString(); |
1348 |
|
|
} else if (attr == "user.tag") { |
1349 |
|
|
attribute_value = mount_point_->repository_tag(); |
1350 |
|
|
} else if (attr == "user.expires") { |
1351 |
|
|
if (fuse_remounter_->catalogs_valid_until() == |
1352 |
|
|
MountPoint::kIndefiniteDeadline) |
1353 |
|
|
{ |
1354 |
|
|
attribute_value = "never (fixed root catalog)"; |
1355 |
|
|
} else { |
1356 |
|
|
time_t now = time(NULL); |
1357 |
|
|
attribute_value = StringifyInt( |
1358 |
|
|
(fuse_remounter_->catalogs_valid_until() - now) / 60); |
1359 |
|
|
} |
1360 |
|
|
} else if (attr == "user.maxfd") { |
1361 |
|
|
attribute_value = StringifyInt(max_open_files_ - kNumReservedFd); |
1362 |
|
|
} else if (attr == "user.usedfd") { |
1363 |
|
|
attribute_value = file_system_->no_open_files()->ToString(); |
1364 |
|
|
} else if (attr == "user.useddirp") { |
1365 |
|
|
attribute_value = file_system_->no_open_dirs()->ToString(); |
1366 |
|
|
} else if (attr == "user.nioerr") { |
1367 |
|
|
attribute_value = file_system_->n_io_error()->ToString(); |
1368 |
|
|
} else if (attr == "user.proxy") { |
1369 |
|
|
vector< vector<download::DownloadManager::ProxyInfo> > proxy_chain; |
1370 |
|
|
unsigned current_group; |
1371 |
|
|
mount_point_->download_mgr()->GetProxyInfo( |
1372 |
|
|
&proxy_chain, ¤t_group, NULL); |
1373 |
|
|
if (proxy_chain.size()) { |
1374 |
|
|
attribute_value = proxy_chain[current_group][0].url; |
1375 |
|
|
} else { |
1376 |
|
|
attribute_value = "DIRECT"; |
1377 |
|
|
} |
1378 |
|
|
} else if (attr == "user.authz") { |
1379 |
|
|
if (!mount_point_->has_membership_req()) { |
1380 |
|
|
fuse_reply_err(req, ENOATTR); |
1381 |
|
|
return; |
1382 |
|
|
} |
1383 |
|
|
attribute_value = mount_point_->membership_req(); |
1384 |
|
|
} else if (attr == "user.chunks") { |
1385 |
|
|
if (d.IsRegular()) { |
1386 |
|
|
if (d.IsChunkedFile()) { |
1387 |
|
|
FileChunkList chunks; |
1388 |
|
|
if (!catalog_mgr->ListFileChunks(path, d.hash_algorithm(), &chunks) || |
1389 |
|
|
chunks.IsEmpty()) |
1390 |
|
|
{ |
1391 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug| kLogSyslogErr, "file %s is marked as " |
1392 |
|
|
"'chunked', but no chunks found.", path.c_str()); |
1393 |
|
|
fuse_reply_err(req, EIO); |
1394 |
|
|
return; |
1395 |
|
|
} else { |
1396 |
|
|
attribute_value = StringifyInt(chunks.size()); |
1397 |
|
|
} |
1398 |
|
|
} else { |
1399 |
|
|
attribute_value = "1"; |
1400 |
|
|
} |
1401 |
|
|
} else { |
1402 |
|
|
fuse_reply_err(req, ENOATTR); |
1403 |
|
|
return; |
1404 |
|
|
} |
1405 |
|
|
} else if (attr == "user.external_file") { |
1406 |
|
|
if (d.IsRegular()) { |
1407 |
|
|
attribute_value = d.IsExternalFile() ? "1" : "0"; |
1408 |
|
|
} else { |
1409 |
|
|
fuse_reply_err(req, ENOATTR); |
1410 |
|
|
return; |
1411 |
|
|
} |
1412 |
|
|
} else if (attr == "user.external_host") { |
1413 |
|
|
vector<string> host_chain; |
1414 |
|
|
vector<int> rtt; |
1415 |
|
|
unsigned current_host; |
1416 |
|
|
mount_point_->external_download_mgr()->GetHostInfo( |
1417 |
|
|
&host_chain, &rtt, ¤t_host); |
1418 |
|
|
if (host_chain.size()) { |
1419 |
|
|
attribute_value = string(host_chain[current_host]); |
1420 |
|
|
} else { |
1421 |
|
|
attribute_value = "internal error: no hosts defined"; |
1422 |
|
|
} |
1423 |
|
|
} else if (attr == "user.compression") { |
1424 |
|
|
if (d.IsRegular()) { |
1425 |
|
|
attribute_value = zlib::AlgorithmName(d.compression_algorithm()); |
1426 |
|
|
} else { |
1427 |
|
|
fuse_reply_err(req, ENOATTR); |
1428 |
|
|
return; |
1429 |
|
|
} |
1430 |
|
|
} else if (attr == "user.host") { |
1431 |
|
|
vector<string> host_chain; |
1432 |
|
|
vector<int> rtt; |
1433 |
|
|
unsigned current_host; |
1434 |
|
|
mount_point_->download_mgr()->GetHostInfo(&host_chain, &rtt, ¤t_host); |
1435 |
|
|
if (host_chain.size()) { |
1436 |
|
|
attribute_value = string(host_chain[current_host]); |
1437 |
|
|
} else { |
1438 |
|
|
attribute_value = "internal error: no hosts defined"; |
1439 |
|
|
} |
1440 |
|
|
} else if (attr == "user.host_list") { |
1441 |
|
|
vector<string> host_chain; |
1442 |
|
|
vector<int> rtt; |
1443 |
|
|
unsigned current_host; |
1444 |
|
|
mount_point_->download_mgr()->GetHostInfo(&host_chain, &rtt, ¤t_host); |
1445 |
|
|
if (host_chain.size()) { |
1446 |
|
|
attribute_value = host_chain[current_host]; |
1447 |
|
|
for (unsigned i = 1; i < host_chain.size(); ++i) { |
1448 |
|
|
attribute_value += |
1449 |
|
|
";" + host_chain[(i+current_host) % host_chain.size()]; |
1450 |
|
|
} |
1451 |
|
|
} else { |
1452 |
|
|
attribute_value = "internal error: no hosts defined"; |
1453 |
|
|
} |
1454 |
|
|
} else if (attr == "user.uptime") { |
1455 |
|
|
time_t now = time(NULL); |
1456 |
|
|
uint64_t uptime = now - loader_exports_->boot_time; |
1457 |
|
|
attribute_value = StringifyInt(uptime / 60); |
1458 |
|
|
} else if (attr == "user.nclg") { |
1459 |
|
|
const int num_catalogs = catalog_mgr->GetNumCatalogs(); |
1460 |
|
|
attribute_value = StringifyInt(num_catalogs); |
1461 |
|
|
} else if (attr == "user.nopen") { |
1462 |
|
|
attribute_value = file_system_->n_fs_open()->ToString(); |
1463 |
|
|
} else if (attr == "user.ndiropen") { |
1464 |
|
|
attribute_value = file_system_->n_fs_dir_open()->ToString(); |
1465 |
|
|
} else if (attr == "user.ndownload") { |
1466 |
|
|
attribute_value = |
1467 |
|
|
mount_point_->statistics()->Lookup("fetch.n_downloads")->Print(); |
1468 |
|
|
} else if (attr == "user.timeout") { |
1469 |
|
|
unsigned seconds, seconds_direct; |
1470 |
|
|
mount_point_->download_mgr()->GetTimeout(&seconds, &seconds_direct); |
1471 |
|
|
attribute_value = StringifyInt(seconds); |
1472 |
|
|
} else if (attr == "user.timeout_direct") { |
1473 |
|
|
unsigned seconds, seconds_direct; |
1474 |
|
|
mount_point_->download_mgr()->GetTimeout(&seconds, &seconds_direct); |
1475 |
|
|
attribute_value = StringifyInt(seconds_direct); |
1476 |
|
|
} else if (attr == "user.external_timeout") { |
1477 |
|
|
unsigned seconds, seconds_direct; |
1478 |
|
|
mount_point_->download_mgr()->GetTimeout(&seconds, &seconds_direct); |
1479 |
|
|
attribute_value = StringifyInt(seconds_direct); |
1480 |
|
|
} else if (attr == "user.rx") { |
1481 |
|
|
perf::Statistics *statistics = mount_point_->statistics(); |
1482 |
|
|
int64_t rx = statistics->Lookup("download.sz_transferred_bytes")->Get(); |
1483 |
|
|
attribute_value = StringifyInt(rx/1024); |
1484 |
|
|
} else if (attr == "user.speed") { |
1485 |
|
|
perf::Statistics *statistics = mount_point_->statistics(); |
1486 |
|
|
int64_t rx = statistics->Lookup("download.sz_transferred_bytes")->Get(); |
1487 |
|
|
int64_t time = statistics->Lookup("download.sz_transfer_time")->Get(); |
1488 |
|
|
if (time == 0) |
1489 |
|
|
attribute_value = "n/a"; |
1490 |
|
|
else |
1491 |
|
|
attribute_value = StringifyInt((1000 * (rx/1024))/time); |
1492 |
|
|
} else if (attr == "user.fqrn") { |
1493 |
|
|
attribute_value = loader_exports_->repository_name; |
1494 |
|
|
} else if (attr == "user.inode_max") { |
1495 |
|
|
attribute_value = StringifyInt( |
1496 |
|
|
inode_generation_info_.inode_generation + |
1497 |
|
|
catalog_mgr->inode_gauge()); |
1498 |
|
|
} else if (attr == "user.ncleanup24") { |
1499 |
|
|
QuotaManager *quota_mgr = file_system_->cache_mgr()->quota_mgr(); |
1500 |
|
|
if (!quota_mgr->HasCapability(QuotaManager::kCapIntrospectCleanupRate)) { |
1501 |
|
|
attribute_value = StringifyInt(-1); |
1502 |
|
|
} else { |
1503 |
|
|
const uint64_t period_s = 24 * 60 * 60; |
1504 |
|
|
const uint64_t rate = quota_mgr->GetCleanupRate(period_s); |
1505 |
|
|
attribute_value = StringifyInt(rate); |
1506 |
|
|
} |
1507 |
|
|
} else { |
1508 |
|
|
if (!xattrs.Get(attr, &attribute_value)) { |
1509 |
|
|
fuse_reply_err(req, ENOATTR); |
1510 |
|
|
return; |
1511 |
|
|
} |
1512 |
|
|
} |
1513 |
|
|
|
1514 |
|
|
if (size == 0) { |
1515 |
|
|
fuse_reply_xattr(req, attribute_value.length()); |
1516 |
|
|
} else if (size >= attribute_value.length()) { |
1517 |
|
|
fuse_reply_buf(req, &attribute_value[0], attribute_value.length()); |
1518 |
|
|
} else { |
1519 |
|
|
fuse_reply_err(req, ERANGE); |
1520 |
|
|
} |
1521 |
|
|
} |
1522 |
|
|
|
1523 |
|
|
|
1524 |
|
|
static void cvmfs_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) { |
1525 |
|
|
const struct fuse_ctx *fuse_ctx = fuse_req_ctx(req); |
1526 |
|
|
ClientCtxGuard ctx_guard(fuse_ctx->uid, fuse_ctx->gid, fuse_ctx->pid); |
1527 |
|
|
|
1528 |
|
|
fuse_remounter_->fence()->Enter(); |
1529 |
|
|
catalog::ClientCatalogManager *catalog_mgr = mount_point_->catalog_mgr(); |
1530 |
|
|
ino = catalog_mgr->MangleInode(ino); |
1531 |
|
|
TraceInode(Tracer::kEventListAttr, ino, "listxattr()"); |
1532 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, |
1533 |
|
|
"cvmfs_listxattr on inode: %" PRIu64 ", size %u [hide xattrs %d]", |
1534 |
|
|
uint64_t(ino), size, mount_point_->hide_magic_xattrs()); |
1535 |
|
|
|
1536 |
|
|
catalog::DirectoryEntry d; |
1537 |
|
|
const bool found = GetDirentForInode(ino, &d); |
1538 |
|
|
XattrList xattrs; |
1539 |
|
|
if (d.HasXattrs()) { |
1540 |
|
|
PathString path; |
1541 |
|
|
bool retval = GetPathForInode(ino, &path); |
1542 |
|
|
assert(retval); |
1543 |
|
|
retval = catalog_mgr->LookupXattrs(path, &xattrs); |
1544 |
|
|
assert(retval); |
1545 |
|
|
} |
1546 |
|
|
fuse_remounter_->fence()->Leave(); |
1547 |
|
|
|
1548 |
|
|
if (!found) { |
1549 |
|
|
ReplyNegative(d, req); |
1550 |
|
|
return; |
1551 |
|
|
} |
1552 |
|
|
|
1553 |
|
|
const char base_list[] = "user.pid\0user.version\0user.revision\0" |
1554 |
|
|
"user.root_hash\0user.expires\0user.maxfd\0user.usedfd\0user.nioerr\0" |
1555 |
|
|
"user.host\0user.proxy\0user.uptime\0user.nclg\0user.nopen\0" |
1556 |
|
|
"user.ndownload\0user.timeout\0user.timeout_direct\0user.rx\0user.speed\0" |
1557 |
|
|
"user.fqrn\0user.ndiropen\0user.inode_max\0user.tag\0user.host_list\0" |
1558 |
|
|
"user.external_host\0user.external_timeout\0user.pubkeys\0" |
1559 |
|
|
"user.ncleanup24\0"; |
1560 |
|
|
string attribute_list; |
1561 |
|
|
if (mount_point_->hide_magic_xattrs()) { |
1562 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "Hiding extended attributes"); |
1563 |
|
|
attribute_list = xattrs.ListKeysPosix(""); |
1564 |
|
|
} else { |
1565 |
|
|
attribute_list = string(base_list, sizeof(base_list)-1); |
1566 |
|
|
if (!d.checksum().IsNull()) { |
1567 |
|
|
const char regular_file_list[] = "user.hash\0user.lhash\0"; |
1568 |
|
|
attribute_list += string(regular_file_list, sizeof(regular_file_list)-1); |
1569 |
|
|
} |
1570 |
|
|
|
1571 |
|
|
if (d.IsLink()) { |
1572 |
|
|
const char symlink_list[] = "xfsroot.rawlink\0user.rawlink\0"; |
1573 |
|
|
attribute_list += string(symlink_list, sizeof(symlink_list)-1); |
1574 |
|
|
} else if (d.IsRegular()) { |
1575 |
|
|
const char regular_file_list[] = "user.external_file\0user.compression\0" |
1576 |
|
|
"user.chunks\0"; |
1577 |
|
|
attribute_list += string(regular_file_list, sizeof(regular_file_list)-1); |
1578 |
|
|
} |
1579 |
|
|
|
1580 |
|
|
if (mount_point_->has_membership_req()) { |
1581 |
|
|
attribute_list += "user.authz\0"; |
1582 |
|
|
} |
1583 |
|
|
attribute_list = xattrs.ListKeysPosix(attribute_list); |
1584 |
|
|
} |
1585 |
|
|
|
1586 |
|
|
if (size == 0) { |
1587 |
|
|
fuse_reply_xattr(req, attribute_list.length()); |
1588 |
|
|
} else if (size >= attribute_list.length()) { |
1589 |
|
|
if (attribute_list.empty()) |
1590 |
|
|
fuse_reply_buf(req, NULL, 0); |
1591 |
|
|
else |
1592 |
|
|
fuse_reply_buf(req, &attribute_list[0], attribute_list.length()); |
1593 |
|
|
} else { |
1594 |
|
|
fuse_reply_err(req, ERANGE); |
1595 |
|
|
} |
1596 |
|
|
} |
1597 |
|
|
|
1598 |
|
|
|
1599 |
|
|
bool Evict(const string &path) { |
1600 |
|
|
catalog::DirectoryEntry dirent; |
1601 |
|
|
fuse_remounter_->fence()->Enter(); |
1602 |
|
|
const bool found = GetDirentForPath(PathString(path), &dirent); |
1603 |
|
|
fuse_remounter_->fence()->Leave(); |
1604 |
|
|
|
1605 |
|
|
if (!found || !dirent.IsRegular()) |
1606 |
|
|
return false; |
1607 |
|
|
file_system_->cache_mgr()->quota_mgr()->Remove(dirent.checksum()); |
1608 |
|
|
return true; |
1609 |
|
|
} |
1610 |
|
|
|
1611 |
|
|
|
1612 |
|
|
bool Pin(const string &path) { |
1613 |
|
|
catalog::DirectoryEntry dirent; |
1614 |
|
|
fuse_remounter_->fence()->Enter(); |
1615 |
|
|
const bool found = GetDirentForPath(PathString(path), &dirent); |
1616 |
|
|
if (!found || !dirent.IsRegular()) { |
1617 |
|
|
fuse_remounter_->fence()->Leave(); |
1618 |
|
|
return false; |
1619 |
|
|
} |
1620 |
|
|
|
1621 |
|
|
if (!dirent.IsChunkedFile()) { |
1622 |
|
|
fuse_remounter_->fence()->Leave(); |
1623 |
|
|
} else { |
1624 |
|
|
FileChunkList chunks; |
1625 |
|
|
mount_point_->catalog_mgr()->ListFileChunks( |
1626 |
|
|
PathString(path), dirent.hash_algorithm(), &chunks); |
1627 |
|
|
fuse_remounter_->fence()->Leave(); |
1628 |
|
|
for (unsigned i = 0; i < chunks.size(); ++i) { |
1629 |
|
|
bool retval = |
1630 |
|
|
file_system_->cache_mgr()->quota_mgr()->Pin( |
1631 |
|
|
chunks.AtPtr(i)->content_hash(), |
1632 |
|
|
chunks.AtPtr(i)->size(), |
1633 |
|
|
"Part of " + path, |
1634 |
|
|
false); |
1635 |
|
|
if (!retval) |
1636 |
|
|
return false; |
1637 |
|
|
int fd = -1; |
1638 |
|
|
if (dirent.IsExternalFile()) { |
1639 |
|
|
fd = mount_point_->external_fetcher()->Fetch( |
1640 |
|
|
chunks.AtPtr(i)->content_hash(), |
1641 |
|
|
chunks.AtPtr(i)->size(), |
1642 |
|
|
"Part of " + path, |
1643 |
|
|
dirent.compression_algorithm(), |
1644 |
|
|
CacheManager::kTypePinned, |
1645 |
|
|
path, |
1646 |
|
|
chunks.AtPtr(i)->offset()); |
1647 |
|
|
} else { |
1648 |
|
|
fd = mount_point_->fetcher()->Fetch( |
1649 |
|
|
chunks.AtPtr(i)->content_hash(), |
1650 |
|
|
chunks.AtPtr(i)->size(), |
1651 |
|
|
"Part of " + path, |
1652 |
|
|
dirent.compression_algorithm(), |
1653 |
|
|
CacheManager::kTypePinned); |
1654 |
|
|
} |
1655 |
|
|
if (fd < 0) { |
1656 |
|
|
return false; |
1657 |
|
|
} |
1658 |
|
|
file_system_->cache_mgr()->Close(fd); |
1659 |
|
|
} |
1660 |
|
|
return true; |
1661 |
|
|
} |
1662 |
|
|
|
1663 |
|
|
bool retval = file_system_->cache_mgr()->quota_mgr()->Pin( |
1664 |
|
|
dirent.checksum(), dirent.size(), path, false); |
1665 |
|
|
if (!retval) |
1666 |
|
|
return false; |
1667 |
|
|
Fetcher *this_fetcher = dirent.IsExternalFile() |
1668 |
|
|
? mount_point_->external_fetcher() |
1669 |
|
|
: mount_point_->fetcher(); |
1670 |
|
|
int fd = this_fetcher->Fetch( |
1671 |
|
|
dirent.checksum(), dirent.size(), path, dirent.compression_algorithm(), |
1672 |
|
|
CacheManager::kTypePinned); |
1673 |
|
|
if (fd < 0) { |
1674 |
|
|
return false; |
1675 |
|
|
} |
1676 |
|
|
file_system_->cache_mgr()->Close(fd); |
1677 |
|
|
return true; |
1678 |
|
|
} |
1679 |
|
|
|
1680 |
|
|
|
1681 |
|
|
/** |
1682 |
|
|
* Do after-daemon() initialization |
1683 |
|
|
*/ |
1684 |
|
|
static void cvmfs_init(void *userdata, struct fuse_conn_info *conn) { |
1685 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_init"); |
1686 |
|
|
|
1687 |
|
|
// NFS support |
1688 |
|
|
#ifdef CVMFS_NFS_SUPPORT |
1689 |
|
|
conn->want |= FUSE_CAP_EXPORT_SUPPORT; |
1690 |
|
|
#endif |
1691 |
|
|
} |
1692 |
|
|
|
1693 |
|
|
static void cvmfs_destroy(void *unused __attribute__((unused))) { |
1694 |
|
|
// The debug log is already closed at this point |
1695 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "cvmfs_destroy"); |
1696 |
|
|
} |
1697 |
|
|
|
1698 |
|
|
/** |
1699 |
|
|
* Puts the callback functions in one single structure |
1700 |
|
|
*/ |
1701 |
|
|
static void SetCvmfsOperations(struct fuse_lowlevel_ops *cvmfs_operations) { |
1702 |
|
|
memset(cvmfs_operations, 0, sizeof(*cvmfs_operations)); |
1703 |
|
|
|
1704 |
|
|
// Init/Fini |
1705 |
|
|
cvmfs_operations->init = cvmfs_init; |
1706 |
|
|
cvmfs_operations->destroy = cvmfs_destroy; |
1707 |
|
|
|
1708 |
|
|
cvmfs_operations->lookup = cvmfs_lookup; |
1709 |
|
|
cvmfs_operations->getattr = cvmfs_getattr; |
1710 |
|
|
cvmfs_operations->readlink = cvmfs_readlink; |
1711 |
|
|
cvmfs_operations->open = cvmfs_open; |
1712 |
|
|
cvmfs_operations->read = cvmfs_read; |
1713 |
|
|
cvmfs_operations->release = cvmfs_release; |
1714 |
|
|
cvmfs_operations->opendir = cvmfs_opendir; |
1715 |
|
|
cvmfs_operations->readdir = cvmfs_readdir; |
1716 |
|
|
cvmfs_operations->releasedir = cvmfs_releasedir; |
1717 |
|
|
cvmfs_operations->statfs = cvmfs_statfs; |
1718 |
|
|
cvmfs_operations->getxattr = cvmfs_getxattr; |
1719 |
|
|
cvmfs_operations->listxattr = cvmfs_listxattr; |
1720 |
|
|
cvmfs_operations->forget = cvmfs_forget; |
1721 |
|
|
} |
1722 |
|
|
|
1723 |
|
|
// Called by cvmfs_talk when switching into read-only cache mode |
1724 |
|
|
void UnregisterQuotaListener() { |
1725 |
|
|
if (cvmfs::unpin_listener_) { |
1726 |
|
|
quota::UnregisterListener(cvmfs::unpin_listener_); |
1727 |
|
|
cvmfs::unpin_listener_ = NULL; |
1728 |
|
|
} |
1729 |
|
|
if (cvmfs::watchdog_listener_) { |
1730 |
|
|
quota::UnregisterListener(cvmfs::watchdog_listener_); |
1731 |
|
|
cvmfs::watchdog_listener_ = NULL; |
1732 |
|
|
} |
1733 |
|
|
} |
1734 |
|
|
|
1735 |
|
|
} // namespace cvmfs |
1736 |
|
|
|
1737 |
|
|
|
1738 |
|
|
string *g_boot_error = NULL; |
1739 |
|
|
|
1740 |
|
|
__attribute__((visibility("default"))) |
1741 |
|
|
loader::CvmfsExports *g_cvmfs_exports = NULL; |
1742 |
|
|
|
1743 |
|
|
|
1744 |
|
|
/** |
1745 |
|
|
* Construct a file system but prevent hanging when already mounted. That |
1746 |
|
|
* means: at most one "system" mount of any given repository name. |
1747 |
|
|
*/ |
1748 |
|
|
static FileSystem *InitSystemFs( |
1749 |
|
|
const string &mount_path, |
1750 |
|
|
const string &fqrn, |
1751 |
|
|
FileSystem::FileSystemInfo fs_info) |
1752 |
|
|
{ |
1753 |
|
|
fs_info.wait_workspace = false; |
1754 |
|
|
FileSystem *file_system = FileSystem::Create(fs_info); |
1755 |
|
|
|
1756 |
|
|
if (file_system->boot_status() == loader::kFailLockWorkspace) { |
1757 |
|
|
string fqrn_from_xattr; |
1758 |
|
|
int retval = platform_getxattr(mount_path, "user.fqrn", &fqrn_from_xattr); |
1759 |
|
|
if (!retval) { |
1760 |
|
|
// Cvmfs not mounted anymore, but another cvmfs process is still in |
1761 |
|
|
// shutdown procedure. Try again and wait for lock |
1762 |
|
|
delete file_system; |
1763 |
|
|
fs_info.wait_workspace = true; |
1764 |
|
|
file_system = FileSystem::Create(fs_info); |
1765 |
|
|
} else { |
1766 |
|
|
if (fqrn_from_xattr == fqrn) { |
1767 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogWarn, |
1768 |
|
|
"repository already mounted on %s", mount_path.c_str()); |
1769 |
|
|
file_system->set_boot_status(loader::kFailDoubleMount); |
1770 |
|
|
} else { |
1771 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr, |
1772 |
|
|
"CernVM-FS repository %s already mounted on %s", |
1773 |
|
|
fqrn.c_str(), mount_path.c_str()); |
1774 |
|
|
file_system->set_boot_status(loader::kFailOtherMount); |
1775 |
|
|
} |
1776 |
|
|
} |
1777 |
|
|
} |
1778 |
|
|
|
1779 |
|
|
return file_system; |
1780 |
|
|
} |
1781 |
|
|
|
1782 |
|
|
|
1783 |
|
|
static void InitOptionsMgr(const loader::LoaderExports *loader_exports) { |
1784 |
|
|
if (loader_exports->version >= 3 && loader_exports->simple_options_parsing) { |
1785 |
|
|
cvmfs::options_mgr_ = new SimpleOptionsParser( |
1786 |
|
|
new DefaultOptionsTemplateManager(loader_exports->repository_name)); |
1787 |
|
|
} else { |
1788 |
|
|
cvmfs::options_mgr_ = new BashOptionsManager( |
1789 |
|
|
new DefaultOptionsTemplateManager(loader_exports->repository_name)); |
1790 |
|
|
} |
1791 |
|
|
|
1792 |
|
|
if (loader_exports->config_files != "") { |
1793 |
|
|
vector<string> tokens = SplitString(loader_exports->config_files, ':'); |
1794 |
|
|
for (unsigned i = 0, s = tokens.size(); i < s; ++i) { |
1795 |
|
|
cvmfs::options_mgr_->ParsePath(tokens[i], false); |
1796 |
|
|
} |
1797 |
|
|
} else { |
1798 |
|
|
cvmfs::options_mgr_->ParseDefault(loader_exports->repository_name); |
1799 |
|
|
} |
1800 |
|
|
} |
1801 |
|
|
|
1802 |
|
|
|
1803 |
|
|
static int Init(const loader::LoaderExports *loader_exports) { |
1804 |
|
|
g_boot_error = new string("unknown error"); |
1805 |
|
|
cvmfs::loader_exports_ = loader_exports; |
1806 |
|
|
InitOptionsMgr(loader_exports); |
1807 |
|
|
|
1808 |
|
|
FileSystem::FileSystemInfo fs_info; |
1809 |
|
|
fs_info.type = FileSystem::kFsFuse; |
1810 |
|
|
fs_info.name = loader_exports->repository_name; |
1811 |
|
|
fs_info.exe_path = loader_exports->program_name; |
1812 |
|
|
fs_info.options_mgr = cvmfs::options_mgr_; |
1813 |
|
|
fs_info.foreground = loader_exports->foreground; |
1814 |
|
|
cvmfs::file_system_ = InitSystemFs( |
1815 |
|
|
loader_exports->mount_point, |
1816 |
|
|
loader_exports->repository_name, |
1817 |
|
|
fs_info); |
1818 |
|
|
if (!cvmfs::file_system_->IsValid()) { |
1819 |
|
|
*g_boot_error = cvmfs::file_system_->boot_error(); |
1820 |
|
|
return cvmfs::file_system_->boot_status(); |
1821 |
|
|
} |
1822 |
|
|
|
1823 |
|
|
cvmfs::mount_point_ = MountPoint::Create(loader_exports->repository_name, |
1824 |
|
|
cvmfs::file_system_); |
1825 |
|
|
if (!cvmfs::mount_point_->IsValid()) { |
1826 |
|
|
*g_boot_error = cvmfs::mount_point_->boot_error(); |
1827 |
|
|
return cvmfs::mount_point_->boot_status(); |
1828 |
|
|
} |
1829 |
|
|
|
1830 |
|
|
cvmfs::directory_handles_ = new cvmfs::DirectoryHandles(); |
1831 |
|
|
cvmfs::directory_handles_->set_empty_key((uint64_t)(-1)); |
1832 |
|
|
cvmfs::directory_handles_->set_deleted_key((uint64_t)(-2)); |
1833 |
|
|
|
1834 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "fuse inode size is %d bits", |
1835 |
|
|
sizeof(fuse_ino_t) * 8); |
1836 |
|
|
|
1837 |
|
|
cvmfs::inode_generation_info_.initial_revision = |
1838 |
|
|
cvmfs::mount_point_->catalog_mgr()->GetRevision(); |
1839 |
|
|
cvmfs::inode_generation_info_.inode_generation = |
1840 |
|
|
cvmfs::mount_point_->inode_annotation()->GetGeneration(); |
1841 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "root inode is %" PRIu64, |
1842 |
|
|
uint64_t(cvmfs::mount_point_->catalog_mgr()->GetRootInode())); |
1843 |
|
|
|
1844 |
|
|
struct fuse_chan **channel = NULL; |
1845 |
|
|
if (loader_exports->version >= 4) |
1846 |
|
|
channel = loader_exports->fuse_channel; |
1847 |
|
|
|
1848 |
|
|
bool fuse_notify_invalidation = true; |
1849 |
|
|
std::string buf; |
1850 |
|
|
if (cvmfs::options_mgr_->GetValue("CVMFS_FUSE_NOTIFY_INVALIDATION", |
1851 |
|
|
&buf)) { |
1852 |
|
|
if (!cvmfs::options_mgr_->IsOn(buf)) { |
1853 |
|
|
fuse_notify_invalidation = false; |
1854 |
|
|
} |
1855 |
|
|
} |
1856 |
|
|
cvmfs::fuse_remounter_ = |
1857 |
|
|
new FuseRemounter(cvmfs::mount_point_, &cvmfs::inode_generation_info_, |
1858 |
|
|
channel, fuse_notify_invalidation); |
1859 |
|
|
|
1860 |
|
|
// Monitor, check for maximum number of open files |
1861 |
|
|
if (cvmfs::UseWatchdog()) { |
1862 |
|
|
cvmfs::watchdog_ = Watchdog::Create("./stacktrace." + |
1863 |
|
|
loader_exports->repository_name); |
1864 |
|
|
if (cvmfs::watchdog_ == NULL) { |
1865 |
|
|
*g_boot_error = "failed to initialize watchdog."; |
1866 |
|
|
return loader::kFailMonitor; |
1867 |
|
|
} |
1868 |
|
|
} |
1869 |
|
|
cvmfs::max_open_files_ = monitor::GetMaxOpenFiles(); |
1870 |
|
|
|
1871 |
|
|
// Control & command interface |
1872 |
|
|
cvmfs::talk_mgr_ = TalkManager::Create( |
1873 |
|
|
"./cvmfs_io." + cvmfs::mount_point_->fqrn(), |
1874 |
|
|
cvmfs::mount_point_, |
1875 |
|
|
cvmfs::fuse_remounter_); |
1876 |
|
|
if (cvmfs::talk_mgr_ == NULL) { |
1877 |
|
|
*g_boot_error = "failed to initialize talk socket (" + |
1878 |
|
|
StringifyInt(errno) + ")"; |
1879 |
|
|
return loader::kFailTalk; |
1880 |
|
|
} |
1881 |
|
|
|
1882 |
|
|
auto_umount::SetMountpoint(loader_exports->mount_point); |
1883 |
|
|
|
1884 |
|
|
return loader::kFailOk; |
1885 |
|
|
} |
1886 |
|
|
|
1887 |
|
|
|
1888 |
|
|
/** |
1889 |
|
|
* Things that have to be executed after fork() / daemon() |
1890 |
|
|
*/ |
1891 |
|
|
static void Spawn() { |
1892 |
|
|
// First thing: fork off the watchdog while we still have a single-threaded |
1893 |
|
|
// well-defined state |
1894 |
|
|
cvmfs::pid_ = getpid(); |
1895 |
|
|
if (cvmfs::watchdog_) { |
1896 |
|
|
cvmfs::watchdog_->RegisterOnCrash(auto_umount::UmountOnCrash); |
1897 |
|
|
cvmfs::watchdog_->Spawn(); |
1898 |
|
|
} |
1899 |
|
|
|
1900 |
|
|
cvmfs::fuse_remounter_->Spawn(); |
1901 |
|
|
|
1902 |
|
|
cvmfs::mount_point_->download_mgr()->Spawn(); |
1903 |
|
|
cvmfs::mount_point_->external_download_mgr()->Spawn(); |
1904 |
|
|
if (cvmfs::mount_point_->resolv_conf_watcher() != NULL) |
1905 |
|
|
cvmfs::mount_point_->resolv_conf_watcher()->Spawn(); |
1906 |
|
|
QuotaManager *quota_mgr = cvmfs::file_system_->cache_mgr()->quota_mgr(); |
1907 |
|
|
quota_mgr->Spawn(); |
1908 |
|
|
if (quota_mgr->HasCapability(QuotaManager::kCapListeners)) { |
1909 |
|
|
cvmfs::watchdog_listener_ = quota::RegisterWatchdogListener( |
1910 |
|
|
quota_mgr, |
1911 |
|
|
cvmfs::mount_point_->uuid()->uuid() + "-watchdog"); |
1912 |
|
|
cvmfs::unpin_listener_ = quota::RegisterUnpinListener( |
1913 |
|
|
quota_mgr, |
1914 |
|
|
cvmfs::mount_point_->catalog_mgr(), |
1915 |
|
|
cvmfs::mount_point_->uuid()->uuid() + "-unpin"); |
1916 |
|
|
} |
1917 |
|
|
cvmfs::mount_point_->tracer()->Spawn(); |
1918 |
|
|
cvmfs::talk_mgr_->Spawn(); |
1919 |
|
|
if (cvmfs::file_system_->nfs_maps() != NULL) |
1920 |
|
|
cvmfs::file_system_->nfs_maps()->Spawn(); |
1921 |
|
|
|
1922 |
|
|
cvmfs::file_system_->cache_mgr()->Spawn(); |
1923 |
|
|
} |
1924 |
|
|
|
1925 |
|
|
|
1926 |
|
|
static string GetErrorMsg() { |
1927 |
|
|
if (g_boot_error) |
1928 |
|
|
return *g_boot_error; |
1929 |
|
|
return ""; |
1930 |
|
|
} |
1931 |
|
|
|
1932 |
|
|
|
1933 |
|
|
static void Fini() { |
1934 |
|
|
delete cvmfs::talk_mgr_; |
1935 |
|
|
cvmfs::talk_mgr_ = NULL; |
1936 |
|
|
|
1937 |
|
|
// The remonter has a reference to the mount point and the inode generation |
1938 |
|
|
delete cvmfs::fuse_remounter_; |
1939 |
|
|
cvmfs::fuse_remounter_ = NULL; |
1940 |
|
|
|
1941 |
|
|
// The unpin listener requires the catalog, so this must be unregistered |
1942 |
|
|
// before the catalog manager is removed |
1943 |
|
|
if (cvmfs::unpin_listener_ != NULL) { |
1944 |
|
|
quota::UnregisterListener(cvmfs::unpin_listener_); |
1945 |
|
|
cvmfs::unpin_listener_ = NULL; |
1946 |
|
|
} |
1947 |
|
|
if (cvmfs::watchdog_listener_ != NULL) { |
1948 |
|
|
quota::UnregisterListener(cvmfs::watchdog_listener_); |
1949 |
|
|
cvmfs::watchdog_listener_ = NULL; |
1950 |
|
|
} |
1951 |
|
|
|
1952 |
|
|
delete cvmfs::directory_handles_; |
1953 |
|
|
delete cvmfs::mount_point_; |
1954 |
|
|
delete cvmfs::file_system_; |
1955 |
|
|
delete cvmfs::options_mgr_; |
1956 |
|
|
cvmfs::directory_handles_ = NULL; |
1957 |
|
|
cvmfs::mount_point_ = NULL; |
1958 |
|
|
cvmfs::file_system_ = NULL; |
1959 |
|
|
cvmfs::options_mgr_ = NULL; |
1960 |
|
|
|
1961 |
|
|
delete cvmfs::watchdog_; |
1962 |
|
|
cvmfs::watchdog_ = NULL; |
1963 |
|
|
|
1964 |
|
|
delete g_boot_error; |
1965 |
|
|
g_boot_error = NULL; |
1966 |
|
|
auto_umount::SetMountpoint(""); |
1967 |
|
|
} |
1968 |
|
|
|
1969 |
|
|
|
1970 |
|
|
static int AltProcessFlavor(int argc, char **argv) { |
1971 |
|
|
if (strcmp(argv[1], "__cachemgr__") == 0) { |
1972 |
|
|
return PosixQuotaManager::MainCacheManager(argc, argv); |
1973 |
|
|
} |
1974 |
|
|
if (strcmp(argv[1], "__wpad__") == 0) { |
1975 |
|
|
return download::MainResolveProxyDescription(argc, argv); |
1976 |
|
|
} |
1977 |
|
|
return 1; |
1978 |
|
|
} |
1979 |
|
|
|
1980 |
|
|
|
1981 |
|
|
static bool MaintenanceMode(const int fd_progress) { |
1982 |
|
|
SendMsg2Socket(fd_progress, "Entering maintenance mode\n"); |
1983 |
|
|
string msg_progress = "Draining out kernel caches ("; |
1984 |
|
|
if (FuseInvalidator::HasFuseNotifyInval()) |
1985 |
|
|
msg_progress += "up to "; |
1986 |
|
|
msg_progress += StringifyInt(static_cast<int>( |
1987 |
|
|
cvmfs::mount_point_->kcache_timeout_sec())) + |
1988 |
|
|
"s)\n"; |
1989 |
|
|
SendMsg2Socket(fd_progress, msg_progress); |
1990 |
|
|
cvmfs::fuse_remounter_->EnterMaintenanceMode(); |
1991 |
|
|
return true; |
1992 |
|
|
} |
1993 |
|
|
|
1994 |
|
|
|
1995 |
|
|
static bool SaveState(const int fd_progress, loader::StateList *saved_states) { |
1996 |
|
|
string msg_progress; |
1997 |
|
|
|
1998 |
|
|
unsigned num_open_dirs = cvmfs::directory_handles_->size(); |
1999 |
|
|
if (num_open_dirs != 0) { |
2000 |
|
|
#ifdef DEBUGMSG |
2001 |
|
|
for (cvmfs::DirectoryHandles::iterator i = |
2002 |
|
|
cvmfs::directory_handles_->begin(), |
2003 |
|
|
iEnd = cvmfs::directory_handles_->end(); i != iEnd; ++i) |
2004 |
|
|
{ |
2005 |
|
|
LogCvmfs(kLogCvmfs, kLogDebug, "saving dirhandle %d", i->first); |
2006 |
|
|
} |
2007 |
|
|
#endif |
2008 |
|
|
|
2009 |
|
|
msg_progress = "Saving open directory handles (" + |
2010 |
|
|
StringifyInt(num_open_dirs) + " handles)\n"; |
2011 |
|
|
SendMsg2Socket(fd_progress, msg_progress); |
2012 |
|
|
|
2013 |
|
|
// TODO(jblomer): should rather be saved just in a malloc'd memory block |
2014 |
|
|
cvmfs::DirectoryHandles *saved_handles = |
2015 |
|
|
new cvmfs::DirectoryHandles(*cvmfs::directory_handles_); |
2016 |
|
|
loader::SavedState *save_open_dirs = new loader::SavedState(); |
2017 |
|
|
save_open_dirs->state_id = loader::kStateOpenDirs; |
2018 |
|
|
save_open_dirs->state = saved_handles; |
2019 |
|
|
saved_states->push_back(save_open_dirs); |
2020 |
|
|
} |
2021 |
|
|
|
2022 |
|
|
if (!cvmfs::file_system_->IsNfsSource()) { |
2023 |
|
|
msg_progress = "Saving inode tracker\n"; |
2024 |
|
|
SendMsg2Socket(fd_progress, msg_progress); |
2025 |
|
|
glue::InodeTracker *saved_inode_tracker = |
2026 |
|
|
new glue::InodeTracker(*cvmfs::mount_point_->inode_tracker()); |
2027 |
|
|
loader::SavedState *state_glue_buffer = new loader::SavedState(); |
2028 |
|
|
state_glue_buffer->state_id = loader::kStateGlueBufferV4; |
2029 |
|
|
state_glue_buffer->state = saved_inode_tracker; |
2030 |
|
|
saved_states->push_back(state_glue_buffer); |
2031 |
|
|
} |
2032 |
|
|
|
2033 |
|
|
msg_progress = "Saving chunk tables\n"; |
2034 |
|
|
SendMsg2Socket(fd_progress, msg_progress); |
2035 |
|
|
ChunkTables *saved_chunk_tables = new ChunkTables( |
2036 |
|
|
*cvmfs::mount_point_->chunk_tables()); |
2037 |
|
|
loader::SavedState *state_chunk_tables = new loader::SavedState(); |
2038 |
|
|
state_chunk_tables->state_id = loader::kStateOpenChunksV4; |
2039 |
|
|
state_chunk_tables->state = saved_chunk_tables; |
2040 |
|
|
saved_states->push_back(state_chunk_tables); |
2041 |
|
|
|
2042 |
|
|
msg_progress = "Saving inode generation\n"; |
2043 |
|
|
SendMsg2Socket(fd_progress, msg_progress); |
2044 |
|
|
cvmfs::inode_generation_info_.inode_generation += |
2045 |
|
|
cvmfs::mount_point_->catalog_mgr()->inode_gauge(); |
2046 |
|
|
cvmfs::InodeGenerationInfo *saved_inode_generation = |
2047 |
|
|
new cvmfs::InodeGenerationInfo(cvmfs::inode_generation_info_); |
2048 |
|
|
loader::SavedState *state_inode_generation = new loader::SavedState(); |
2049 |
|
|
state_inode_generation->state_id = loader::kStateInodeGeneration; |
2050 |
|
|
state_inode_generation->state = saved_inode_generation; |
2051 |
|
|
saved_states->push_back(state_inode_generation); |
2052 |
|
|
|
2053 |
|
|
SendMsg2Socket(fd_progress, msg_progress); |
2054 |
|
|
loader::SavedState *state_cache_mgr = new loader::SavedState(); |
2055 |
|
|
state_cache_mgr->state_id = loader::kStateOpenFiles; |
2056 |
|
|
state_cache_mgr->state = |
2057 |
|
|
cvmfs::file_system_->cache_mgr()->SaveState(fd_progress); |
2058 |
|
|
saved_states->push_back(state_cache_mgr); |
2059 |
|
|
|
2060 |
|
|
msg_progress = "Saving open files counter\n"; |
2061 |
|
|
SendMsg2Socket(fd_progress, msg_progress); |
2062 |
|
|
uint32_t *saved_num_fd = |
2063 |
|
|
new uint32_t(cvmfs::file_system_->no_open_files()->Get()); |
2064 |
|
|
loader::SavedState *state_num_fd = new loader::SavedState(); |
2065 |
|
|
state_num_fd->state_id = loader::kStateOpenFilesCounter; |
2066 |
|
|
state_num_fd->state = saved_num_fd; |
2067 |
|
|
saved_states->push_back(state_num_fd); |
2068 |
|
|
|
2069 |
|
|
return true; |
2070 |
|
|
} |
2071 |
|
|
|
2072 |
|
|
|
2073 |
|
|
static bool RestoreState(const int fd_progress, |
2074 |
|
|
const loader::StateList &saved_states) |
2075 |
|
|
{ |
2076 |
|
|
for (unsigned i = 0, l = saved_states.size(); i < l; ++i) { |
2077 |
|
|
if (saved_states[i]->state_id == loader::kStateOpenDirs) { |
2078 |
|
|
SendMsg2Socket(fd_progress, "Restoring open directory handles... "); |
2079 |
|
|
delete cvmfs::directory_handles_; |
2080 |
|
|
cvmfs::DirectoryHandles *saved_handles = |
2081 |
|
|
(cvmfs::DirectoryHandles *)saved_states[i]->state; |
2082 |
|
|
cvmfs::directory_handles_ = new cvmfs::DirectoryHandles(*saved_handles); |
2083 |
|
|
cvmfs::file_system_->no_open_dirs()->Set( |
2084 |
|
|
cvmfs::directory_handles_->size()); |
2085 |
|
|
cvmfs::DirectoryHandles::const_iterator i = |
2086 |
|
|
cvmfs::directory_handles_->begin(); |
2087 |
|
|
for (; i != cvmfs::directory_handles_->end(); ++i) { |
2088 |
|
|
if (i->first >= cvmfs::next_directory_handle_) |
2089 |
|
|
cvmfs::next_directory_handle_ = i->first + 1; |
2090 |
|
|
} |
2091 |
|
|
|
2092 |
|
|
SendMsg2Socket(fd_progress, |
2093 |
|
|
StringifyInt(cvmfs::directory_handles_->size()) + " handles\n"); |
2094 |
|
|
} |
2095 |
|
|
|
2096 |
|
|
if (saved_states[i]->state_id == loader::kStateGlueBuffer) { |
2097 |
|
|
SendMsg2Socket(fd_progress, "Migrating inode tracker (v1 to v4)... "); |
2098 |
|
|
compat::inode_tracker::InodeTracker *saved_inode_tracker = |
2099 |
|
|
(compat::inode_tracker::InodeTracker *)saved_states[i]->state; |
2100 |
|
|
compat::inode_tracker::Migrate( |
2101 |
|
|
saved_inode_tracker, cvmfs::mount_point_->inode_tracker()); |
2102 |
|
|
SendMsg2Socket(fd_progress, " done\n"); |
2103 |
|
|
} |
2104 |
|
|
|
2105 |
|
|
if (saved_states[i]->state_id == loader::kStateGlueBufferV2) { |
2106 |
|
|
SendMsg2Socket(fd_progress, "Migrating inode tracker (v2 to v4)... "); |
2107 |
|
|
compat::inode_tracker_v2::InodeTracker *saved_inode_tracker = |
2108 |
|
|
(compat::inode_tracker_v2::InodeTracker *)saved_states[i]->state; |
2109 |
|
|
compat::inode_tracker_v2::Migrate(saved_inode_tracker, |
2110 |
|
|
cvmfs::mount_point_->inode_tracker()); |
2111 |
|
|
SendMsg2Socket(fd_progress, " done\n"); |
2112 |
|
|
} |
2113 |
|
|
|
2114 |
|
|
if (saved_states[i]->state_id == loader::kStateGlueBufferV3) { |
2115 |
|
|
SendMsg2Socket(fd_progress, "Migrating inode tracker (v3 to v4)... "); |
2116 |
|
|
compat::inode_tracker_v3::InodeTracker *saved_inode_tracker = |
2117 |
|
|
(compat::inode_tracker_v3::InodeTracker *)saved_states[i]->state; |
2118 |
|
|
compat::inode_tracker_v3::Migrate(saved_inode_tracker, |
2119 |
|
|
cvmfs::mount_point_->inode_tracker()); |
2120 |
|
|
SendMsg2Socket(fd_progress, " done\n"); |
2121 |
|
|
} |
2122 |
|
|
|
2123 |
|
|
if (saved_states[i]->state_id == loader::kStateGlueBufferV4) { |
2124 |
|
|
SendMsg2Socket(fd_progress, "Restoring inode tracker... "); |
2125 |
|
|
cvmfs::mount_point_->inode_tracker()->~InodeTracker(); |
2126 |
|
|
glue::InodeTracker *saved_inode_tracker = |
2127 |
|
|
(glue::InodeTracker *)saved_states[i]->state; |
2128 |
|
|
new (cvmfs::mount_point_->inode_tracker()) |
2129 |
|
|
glue::InodeTracker(*saved_inode_tracker); |
2130 |
|
|
SendMsg2Socket(fd_progress, " done\n"); |
2131 |
|
|
} |
2132 |
|
|
|
2133 |
|
|
ChunkTables *chunk_tables = cvmfs::mount_point_->chunk_tables(); |
2134 |
|
|
|
2135 |
|
|
if (saved_states[i]->state_id == loader::kStateOpenChunks) { |
2136 |
|
|
SendMsg2Socket(fd_progress, "Migrating chunk tables (v1 to v4)... "); |
2137 |
|
|
compat::chunk_tables::ChunkTables *saved_chunk_tables = |
2138 |
|
|
(compat::chunk_tables::ChunkTables *)saved_states[i]->state; |
2139 |
|
|
compat::chunk_tables::Migrate(saved_chunk_tables, chunk_tables); |
2140 |
|
|
SendMsg2Socket(fd_progress, |
2141 |
|
|
StringifyInt(chunk_tables->handle2fd.size()) + " handles\n"); |
2142 |
|
|
} |
2143 |
|
|
|
2144 |
|
|
if (saved_states[i]->state_id == loader::kStateOpenChunksV2) { |
2145 |
|
|
SendMsg2Socket(fd_progress, "Migrating chunk tables (v2 to v4)... "); |
2146 |
|
|
compat::chunk_tables_v2::ChunkTables *saved_chunk_tables = |
2147 |
|
|
(compat::chunk_tables_v2::ChunkTables *)saved_states[i]->state; |
2148 |
|
|
compat::chunk_tables_v2::Migrate(saved_chunk_tables, chunk_tables); |
2149 |
|
|
SendMsg2Socket(fd_progress, |
2150 |
|
|
StringifyInt(chunk_tables->handle2fd.size()) + " handles\n"); |
2151 |
|
|
} |
2152 |
|
|
|
2153 |
|
|
if (saved_states[i]->state_id == loader::kStateOpenChunksV3) { |
2154 |
|
|
SendMsg2Socket(fd_progress, "Migrating chunk tables (v3 to v4)... "); |
2155 |
|
|
compat::chunk_tables_v3::ChunkTables *saved_chunk_tables = |
2156 |
|
|
(compat::chunk_tables_v3::ChunkTables *)saved_states[i]->state; |
2157 |
|
|
compat::chunk_tables_v3::Migrate(saved_chunk_tables, chunk_tables); |
2158 |
|
|
SendMsg2Socket(fd_progress, |
2159 |
|
|
StringifyInt(chunk_tables->handle2fd.size()) + " handles\n"); |
2160 |
|
|
} |
2161 |
|
|
|
2162 |
|
|
if (saved_states[i]->state_id == loader::kStateOpenChunksV4) { |
2163 |
|
|
SendMsg2Socket(fd_progress, "Restoring chunk tables... "); |
2164 |
|
|
chunk_tables->~ChunkTables(); |
2165 |
|
|
ChunkTables *saved_chunk_tables = reinterpret_cast<ChunkTables *>( |
2166 |
|
|
saved_states[i]->state); |
2167 |
|
|
new (chunk_tables) ChunkTables(*saved_chunk_tables); |
2168 |
|
|
SendMsg2Socket(fd_progress, " done\n"); |
2169 |
|
|
} |
2170 |
|
|
|
2171 |
|
|
if (saved_states[i]->state_id == loader::kStateInodeGeneration) { |
2172 |
|
|
SendMsg2Socket(fd_progress, "Restoring inode generation... "); |
2173 |
|
|
cvmfs::InodeGenerationInfo *old_info = |
2174 |
|
|
(cvmfs::InodeGenerationInfo *)saved_states[i]->state; |
2175 |
|
|
if (old_info->version == 1) { |
2176 |
|
|
// Migration |
2177 |
|
|
cvmfs::inode_generation_info_.initial_revision = |
2178 |
|
|
old_info->initial_revision; |
2179 |
|
|
cvmfs::inode_generation_info_.incarnation = old_info->incarnation; |
2180 |
|
|
// Note: in the rare case of inode generation being 0 before, inode |
2181 |
|
|
// can clash after reload before remount |
2182 |
|
|
} else { |
2183 |
|
|
cvmfs::inode_generation_info_ = *old_info; |
2184 |
|
|
} |
2185 |
|
|
++cvmfs::inode_generation_info_.incarnation; |
2186 |
|
|
SendMsg2Socket(fd_progress, " done\n"); |
2187 |
|
|
} |
2188 |
|
|
|
2189 |
|
|
if (saved_states[i]->state_id == loader::kStateOpenFilesCounter) { |
2190 |
|
|
SendMsg2Socket(fd_progress, "Restoring open files counter... "); |
2191 |
|
|
cvmfs::file_system_->no_open_files()->Set(*(reinterpret_cast<uint32_t *>( |
2192 |
|
|
saved_states[i]->state))); |
2193 |
|
|
SendMsg2Socket(fd_progress, " done\n"); |
2194 |
|
|
} |
2195 |
|
|
|
2196 |
|
|
if (saved_states[i]->state_id == loader::kStateOpenFiles) { |
2197 |
|
|
cvmfs::file_system_->cache_mgr()->RestoreState( |
2198 |
|
|
fd_progress, saved_states[i]->state); |
2199 |
|
|
} |
2200 |
|
|
} |
2201 |
|
|
if (cvmfs::mount_point_->inode_annotation()) { |
2202 |
|
|
uint64_t saved_generation = cvmfs::inode_generation_info_.inode_generation; |
2203 |
|
|
cvmfs::mount_point_->inode_annotation()->IncGeneration(saved_generation); |
2204 |
|
|
} |
2205 |
|
|
|
2206 |
|
|
return true; |
2207 |
|
|
} |
2208 |
|
|
|
2209 |
|
|
|
2210 |
|
|
static void FreeSavedState(const int fd_progress, |
2211 |
|
|
const loader::StateList &saved_states) |
2212 |
|
|
{ |
2213 |
|
|
for (unsigned i = 0, l = saved_states.size(); i < l; ++i) { |
2214 |
|
|
switch (saved_states[i]->state_id) { |
2215 |
|
|
case loader::kStateOpenDirs: |
2216 |
|
|
SendMsg2Socket(fd_progress, "Releasing saved open directory handles\n"); |
2217 |
|
|
delete static_cast<cvmfs::DirectoryHandles *>(saved_states[i]->state); |
2218 |
|
|
break; |
2219 |
|
|
case loader::kStateGlueBuffer: |
2220 |
|
|
SendMsg2Socket( |
2221 |
|
|
fd_progress, "Releasing saved glue buffer (version 1)\n"); |
2222 |
|
|
delete static_cast<compat::inode_tracker::InodeTracker *>( |
2223 |
|
|
saved_states[i]->state); |
2224 |
|
|
break; |
2225 |
|
|
case loader::kStateGlueBufferV2: |
2226 |
|
|
SendMsg2Socket( |
2227 |
|
|
fd_progress, "Releasing saved glue buffer (version 2)\n"); |
2228 |
|
|
delete static_cast<compat::inode_tracker_v2::InodeTracker *>( |
2229 |
|
|
saved_states[i]->state); |
2230 |
|
|
break; |
2231 |
|
|
case loader::kStateGlueBufferV3: |
2232 |
|
|
SendMsg2Socket( |
2233 |
|
|
fd_progress, "Releasing saved glue buffer (version 3)\n"); |
2234 |
|
|
delete static_cast<compat::inode_tracker_v3::InodeTracker *>( |
2235 |
|
|
saved_states[i]->state); |
2236 |
|
|
break; |
2237 |
|
|
case loader::kStateGlueBufferV4: |
2238 |
|
|
SendMsg2Socket(fd_progress, "Releasing saved glue buffer\n"); |
2239 |
|
|
delete static_cast<glue::InodeTracker *>(saved_states[i]->state); |
2240 |
|
|
break; |
2241 |
|
|
case loader::kStateOpenChunks: |
2242 |
|
|
SendMsg2Socket(fd_progress, "Releasing chunk tables (version 1)\n"); |
2243 |
|
|
delete static_cast<compat::chunk_tables::ChunkTables *>( |
2244 |
|
|
saved_states[i]->state); |
2245 |
|
|
break; |
2246 |
|
|
case loader::kStateOpenChunksV2: |
2247 |
|
|
SendMsg2Socket(fd_progress, "Releasing chunk tables (version 2)\n"); |
2248 |
|
|
delete static_cast<compat::chunk_tables_v2::ChunkTables *>( |
2249 |
|
|
saved_states[i]->state); |
2250 |
|
|
break; |
2251 |
|
|
case loader::kStateOpenChunksV3: |
2252 |
|
|
SendMsg2Socket(fd_progress, "Releasing chunk tables (version 3)\n"); |
2253 |
|
|
delete static_cast<compat::chunk_tables_v3::ChunkTables *>( |
2254 |
|
|
saved_states[i]->state); |
2255 |
|
|
break; |
2256 |
|
|
case loader::kStateOpenChunksV4: |
2257 |
|
|
SendMsg2Socket(fd_progress, "Releasing chunk tables\n"); |
2258 |
|
|
delete static_cast<ChunkTables *>(saved_states[i]->state); |
2259 |
|
|
break; |
2260 |
|
|
case loader::kStateInodeGeneration: |
2261 |
|
|
SendMsg2Socket(fd_progress, "Releasing saved inode generation info\n"); |
2262 |
|
|
delete static_cast<cvmfs::InodeGenerationInfo *>( |
2263 |
|
|
saved_states[i]->state); |
2264 |
|
|
break; |
2265 |
|
|
case loader::kStateOpenFiles: |
2266 |
|
|
cvmfs::file_system_->cache_mgr()->FreeState( |
2267 |
|
|
fd_progress, saved_states[i]->state); |
2268 |
|
|
break; |
2269 |
|
|
case loader::kStateOpenFilesCounter: |
2270 |
|
|
SendMsg2Socket(fd_progress, "Releasing open files counter\n"); |
2271 |
|
|
delete static_cast<uint32_t *>(saved_states[i]->state); |
2272 |
|
|
break; |
2273 |
|
|
default: |
2274 |
|
|
break; |
2275 |
|
|
} |
2276 |
|
|
} |
2277 |
|
|
} |
2278 |
|
|
|
2279 |
|
|
|
2280 |
|
|
static void __attribute__((constructor)) LibraryMain() { |
2281 |
|
|
g_cvmfs_exports = new loader::CvmfsExports(); |
2282 |
|
|
g_cvmfs_exports->so_version = PACKAGE_VERSION; |
2283 |
|
|
g_cvmfs_exports->fnAltProcessFlavor = AltProcessFlavor; |
2284 |
|
|
g_cvmfs_exports->fnInit = Init; |
2285 |
|
|
g_cvmfs_exports->fnSpawn = Spawn; |
2286 |
|
|
g_cvmfs_exports->fnFini = Fini; |
2287 |
|
|
g_cvmfs_exports->fnGetErrorMsg = GetErrorMsg; |
2288 |
|
|
g_cvmfs_exports->fnMaintenanceMode = MaintenanceMode; |
2289 |
|
|
g_cvmfs_exports->fnSaveState = SaveState; |
2290 |
|
|
g_cvmfs_exports->fnRestoreState = RestoreState; |
2291 |
|
|
g_cvmfs_exports->fnFreeSavedState = FreeSavedState; |
2292 |
|
|
cvmfs::SetCvmfsOperations(&g_cvmfs_exports->cvmfs_operations); |
2293 |
|
|
} |
2294 |
|
|
|
2295 |
|
|
|
2296 |
|
|
static void __attribute__((destructor)) LibraryExit() { |
2297 |
|
|
delete g_cvmfs_exports; |
2298 |
|
|
g_cvmfs_exports = NULL; |
2299 |
|
|
} |