Line data Source code
1 : /*
2 : Copyright 2018-2024, Barcelona Supercomputing Center (BSC), Spain
3 : Copyright 2015-2024, Johannes Gutenberg Universitaet Mainz, Germany
4 :
5 : This software was partially supported by the
6 : EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu).
7 :
8 : This software was partially supported by the
9 : ADA-FS project under the SPPEXA project funded by the DFG.
10 :
11 : This file is part of GekkoFS' POSIX interface.
12 :
13 : GekkoFS' POSIX interface is free software: you can redistribute it and/or
14 : modify it under the terms of the GNU Lesser General Public License as
15 : published by the Free Software Foundation, either version 3 of the License,
16 : or (at your option) any later version.
17 :
18 : GekkoFS' POSIX interface is distributed in the hope that it will be useful,
19 : but WITHOUT ANY WARRANTY; without even the implied warranty of
20 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 : GNU Lesser General Public License for more details.
22 :
23 : You should have received a copy of the GNU Lesser General Public License
24 : along with GekkoFS' POSIX interface. If not, see
25 : <https://www.gnu.org/licenses/>.
26 :
27 : SPDX-License-Identifier: LGPL-3.0-or-later
28 : */
29 :
30 : #include <config.hpp>
31 : #include <client/preload.hpp>
32 : #include <client/preload_util.hpp>
33 : #include <client/logging.hpp>
34 : #include <client/gkfs_functions.hpp>
35 : #include <client/rpc/forward_metadata.hpp>
36 : #include <client/rpc/forward_data.hpp>
37 : #include <client/open_dir.hpp>
38 :
39 : #include <common/path_util.hpp>
40 :
41 : extern "C" {
42 : #include <dirent.h> // used for file types in the getdents{,64}() functions
43 : #include <linux/kernel.h> // used for definition of alignment macros
44 : #include <sys/statfs.h>
45 : #include <sys/statvfs.h>
46 : }
47 :
48 : using namespace std;
49 :
50 : /*
51 : * Macro used within getdents{,64} functions.
52 : * __ALIGN_KERNEL defined in linux/kernel.h
53 : */
54 : #define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
55 :
56 : /*
57 : * linux_dirent is used in getdents() but is privately defined in the linux
58 : * kernel: fs/readdir.c.
59 : */
60 : struct linux_dirent {
61 : unsigned long d_ino;
62 : unsigned long d_off;
63 : unsigned short d_reclen;
64 : char d_name[1];
65 : };
66 : /*
67 : * linux_dirent64 is used in getdents64() and defined in the linux kernel:
68 : * include/linux/dirent.h. However, it is not part of the kernel-headers and
69 : * cannot be imported.
70 : */
71 : struct linux_dirent64 {
72 : uint64_t d_ino;
73 : int64_t d_off;
74 : unsigned short d_reclen;
75 : unsigned char d_type;
76 : char d_name[1]; // originally `char d_name[0]` in kernel, but ISO C++
77 : // forbids zero-size array 'd_name'
78 : };
79 :
80 : struct dirent_extended {
81 : size_t size;
82 : time_t ctime;
83 : unsigned short d_reclen;
84 : unsigned char d_type;
85 : char d_name[1]; // originally `char d_name[0]` in kernel, but ISO C++
86 : // forbids zero-size array 'd_name'
87 : };
88 :
89 :
90 : namespace {
91 :
92 : /**
93 : * Checks if metadata for parent directory exists (can be disabled with
94 : * GKFS_CREATE_CHECK_PARENTS). errno may be set
95 : * @param path
96 : * @return 0 on success, -1 on failure
97 : */
98 : int
99 1059 : check_parent_dir(const std::string& path) {
100 : #if GKFS_CREATE_CHECK_PARENTS
101 2118 : auto p_comp = gkfs::path::dirname(path);
102 2118 : auto md = gkfs::utils::get_metadata(p_comp);
103 1059 : if(!md) {
104 1 : if(errno == ENOENT) {
105 1 : LOG(DEBUG, "Parent component does not exist: '{}'", p_comp);
106 : } else {
107 0 : LOG(ERROR, "Failed to get metadata for parent component '{}': {}",
108 1 : path, strerror(errno));
109 : }
110 1 : return -1;
111 : }
112 1058 : if(!S_ISDIR(md->mode())) {
113 1 : LOG(DEBUG, "Parent component is not a directory: '{}'", p_comp);
114 1 : errno = ENOTDIR;
115 1 : return -1;
116 : }
117 : #endif // GKFS_CREATE_CHECK_PARENTS
118 : return 0;
119 : }
120 : } // namespace
121 :
122 : namespace gkfs::syscall {
123 :
124 : /**
125 : * gkfs wrapper for open() system calls
126 : * errno may be set
127 : * @param path
128 : * @param mode
129 : * @param flags
130 : * @return 0 on success, -1 on failure
131 : */
132 : int
133 1146 : gkfs_open(const std::string& path, mode_t mode, int flags) {
134 :
135 1146 : if(flags & O_PATH) {
136 1 : LOG(ERROR, "`O_PATH` flag is not supported");
137 1 : errno = ENOTSUP;
138 1 : return -1;
139 : }
140 :
141 : // metadata object filled during create or stat
142 2291 : gkfs::metadata::Metadata md{};
143 1145 : if(flags & O_CREAT) {
144 1043 : if(flags & O_DIRECTORY) {
145 1 : LOG(ERROR, "O_DIRECTORY use with O_CREAT. NOT SUPPORTED");
146 1 : errno = ENOTSUP;
147 1 : return -1;
148 : }
149 : // no access check required here. If one is using our FS they have the
150 : // permissions.
151 1042 : auto err = gkfs_create(path, mode | S_IFREG);
152 1042 : if(err) {
153 5 : if(errno == EEXIST) {
154 : // file exists, O_CREAT was set
155 3 : if(flags & O_EXCL) {
156 : // File exists and O_EXCL & O_CREAT was set
157 1 : return -1;
158 : }
159 : // file exists, O_CREAT was set O_EXCL wasnt, so function does
160 : // not fail this case is actually undefined as per `man 2 open`
161 4 : auto md_ = gkfs::utils::get_metadata(path);
162 2 : if(!md_) {
163 0 : LOG(ERROR,
164 : "Could not get metadata after creating file '{}': '{}'",
165 0 : path, strerror(errno));
166 1 : return -1;
167 : }
168 2 : md = *md_;
169 : #ifdef HAS_RENAME
170 : // This is an old file that was renamed which we do not open
171 2 : if(md.blocks() == -1) {
172 0 : LOG(DEBUG,
173 : "This file was renamed and we do not open. path '{}'",
174 0 : path);
175 0 : return -1;
176 : }
177 : #endif // HAS_RENAME
178 : } else {
179 2 : LOG(ERROR, "Error creating file: '{}'", strerror(errno));
180 2 : return -1;
181 : }
182 : } else {
183 : // file was successfully created. Add to filemap
184 2074 : return CTX->file_map()->add(
185 2074 : std::make_shared<gkfs::filemap::OpenFile>(path, flags));
186 : }
187 : } else {
188 203 : auto md_ = gkfs::utils::get_metadata(path);
189 102 : if(!md_) {
190 1 : if(errno != ENOENT) {
191 0 : LOG(ERROR, "Error stating existing file '{}'", path);
192 : }
193 : // file doesn't exist and O_CREAT was not set
194 1 : return -1;
195 : }
196 101 : md = *md_;
197 : }
198 :
199 :
200 : #ifdef HAS_SYMLINKS
201 103 : if(md.is_link()) {
202 0 : if(flags & O_NOFOLLOW) {
203 0 : LOG(WARNING, "Symlink found and O_NOFOLLOW flag was specified");
204 0 : errno = ELOOP;
205 0 : return -1;
206 : }
207 0 : return gkfs_open(md.target_path(), mode, flags);
208 : }
209 : #ifdef HAS_RENAME
210 1248 : auto new_path = path;
211 103 : if(md.blocks() == -1) {
212 : // This is an old file that was renamed and essentially no longer exists
213 0 : errno = ENOENT;
214 0 : return -1;
215 : } else {
216 103 : if(!md.target_path().empty()) {
217 : // get renamed path from target and retrieve metadata from it
218 24 : auto md_ = gkfs::utils::get_metadata(md.target_path());
219 12 : new_path = md.target_path();
220 18 : while(!md_.value().target_path().empty()) {
221 6 : new_path = md_.value().target_path();
222 12 : md_ = gkfs::utils::get_metadata(md_.value().target_path(),
223 12 : false);
224 6 : if(!md_) {
225 : return -1;
226 : }
227 : }
228 12 : md = *md_;
229 12 : if(S_ISDIR(md.mode())) {
230 0 : return gkfs_opendir(new_path);
231 : }
232 :
233 : /*** Regular file exists ***/
234 12 : assert(S_ISREG(md.mode()));
235 :
236 12 : if((flags & O_TRUNC) && ((flags & O_RDWR) || (flags & O_WRONLY))) {
237 0 : if(gkfs_truncate(new_path, md.size(), 0)) {
238 0 : LOG(ERROR, "Error truncating file");
239 0 : return -1;
240 : }
241 : }
242 :
243 24 : return CTX->file_map()->add(
244 24 : std::make_shared<gkfs::filemap::OpenFile>(new_path, flags));
245 : }
246 : }
247 : #endif // HAS_RENAME
248 : #endif // HAS_SYMLINKS
249 91 : if(S_ISDIR(md.mode())) {
250 22 : return gkfs_opendir(path);
251 : }
252 :
253 : /*** Regular file exists ***/
254 69 : assert(S_ISREG(md.mode()));
255 :
256 69 : if((flags & O_TRUNC) && ((flags & O_RDWR) || (flags & O_WRONLY))) {
257 2 : if(gkfs_truncate(path, md.size(), 0)) {
258 0 : LOG(ERROR, "Error truncating file");
259 0 : return -1;
260 : }
261 : }
262 :
263 138 : return CTX->file_map()->add(
264 138 : std::make_shared<gkfs::filemap::OpenFile>(path, flags));
265 : }
266 :
267 : /**
268 : * Wrapper function for file/directory creation
269 : * errno may be set
270 : * @param path
271 : * @param mode
272 : * @return 0 on success, -1 on failure
273 : */
274 : int
275 1059 : gkfs_create(const std::string& path, mode_t mode) {
276 :
277 : // file type must be set
278 1059 : switch(mode & S_IFMT) {
279 0 : case 0:
280 0 : mode |= S_IFREG;
281 0 : break;
282 : case S_IFREG: // intentionally fall-through
283 : case S_IFDIR:
284 : break;
285 0 : case S_IFCHR: // intentionally fall-through
286 0 : case S_IFBLK:
287 0 : case S_IFIFO:
288 0 : case S_IFSOCK:
289 0 : LOG(WARNING, "Unsupported node type");
290 0 : errno = ENOTSUP;
291 0 : return -1;
292 0 : default:
293 0 : LOG(WARNING, "Unrecognized node type");
294 0 : errno = EINVAL;
295 0 : return -1;
296 : }
297 :
298 1059 : if(check_parent_dir(path)) {
299 : return -1;
300 : }
301 : // Write to all replicas, at least one need to success
302 : bool success = false;
303 2114 : for(auto copy = 0; copy < CTX->get_replicas() + 1; copy++) {
304 1057 : auto err = gkfs::rpc::forward_create(path, mode, copy);
305 1057 : if(err) {
306 3 : errno = err;
307 : } else {
308 1054 : success = true;
309 1054 : errno = 0;
310 : }
311 : }
312 1057 : if(!success) {
313 3 : return -1;
314 : }
315 :
316 : return 0;
317 : }
318 :
319 : /**
320 : * gkfs wrapper for unlink() system calls
321 : * errno may be set
322 : * @param path
323 : * @return 0 on success, -1 on failure
324 : */
325 : int
326 7 : gkfs_remove(const std::string& path) {
327 14 : auto md = gkfs::utils::get_metadata(path);
328 7 : if(!md) {
329 : return -1;
330 : }
331 :
332 6 : if(S_ISDIR(md->mode())) {
333 1 : LOG(ERROR, "Cannot remove directory '{}'", path);
334 1 : errno = EISDIR;
335 1 : return -1;
336 : }
337 : #ifdef HAS_SYMLINKS
338 : #ifdef HAS_RENAME
339 5 : if(md.value().blocks() == -1) {
340 1 : errno = ENOENT;
341 1 : return -1;
342 : } else {
343 4 : if(!md.value().target_path().empty()) {
344 2 : auto md_ = gkfs::utils::get_metadata(md.value().target_path());
345 2 : std::string new_path = md.value().target_path();
346 2 : while(!md.value().target_path().empty()) {
347 1 : new_path = md.value().target_path();
348 3 : md = gkfs::utils::get_metadata(md.value().target_path(), false);
349 1 : if(!md) {
350 0 : return -1;
351 : }
352 : }
353 1 : auto err = gkfs::rpc::forward_remove(new_path, CTX->get_replicas());
354 1 : if(err) {
355 0 : errno = err;
356 0 : return -1;
357 : }
358 : }
359 : }
360 : #endif // HAS_RENAME
361 : #endif // HAS_SYMLINKS
362 :
363 4 : auto err = gkfs::rpc::forward_remove(path, CTX->get_replicas());
364 4 : if(err) {
365 0 : errno = err;
366 0 : return -1;
367 : }
368 : return 0;
369 : }
370 :
371 : /**
372 : * gkfs wrapper for access() system calls
373 : * errno may be set
374 : * @param path
375 : * @param mask
376 : * @param follow_links
377 : * @return 0 on success, -1 on failure
378 : */
379 : int
380 7 : gkfs_access(const std::string& path, const int mask, bool follow_links) {
381 14 : auto md = gkfs::utils::get_metadata(path, follow_links);
382 7 : if(!md) {
383 1 : LOG(DEBUG, "File does not exist '{}'", path);
384 1 : return -1;
385 : }
386 : #ifdef HAS_SYMLINKS
387 : #ifdef HAS_RENAME
388 6 : LOG(DEBUG, "Checking for renamed file '{}'", path);
389 6 : if(md.value().blocks() == -1) {
390 1 : errno = ENOENT;
391 1 : LOG(DEBUG, "File exist but it is renamed '{}'", path);
392 1 : return -1;
393 :
394 : } else {
395 6 : while(!md.value().target_path().empty()) {
396 2 : LOG(DEBUG, "File exist but it is renamed '{} -> {}'", path,
397 1 : md.value().target_path());
398 3 : md = gkfs::utils::get_metadata(md.value().target_path(), false);
399 1 : if(!md) {
400 0 : LOG(DEBUG, "File does not exist but it is renamed '{} -> {}'",
401 0 : path, md.value().target_path());
402 0 : return -1;
403 : }
404 : }
405 : }
406 : #endif // HAS_RENAME
407 : #endif // HAS_SYMLINKS
408 : return 0;
409 : }
410 :
411 : #ifdef HAS_SYMLINKS
412 : #ifdef HAS_RENAME
413 : /**
414 : * gkfs wrapper for rename() system calls
415 : * errno may be set
416 : * We use blocks to determine if the file is a renamed file.
417 : * If the file is re-renamed (a->b->a) a recovers the block of b
418 : * and we delete b.
419 : * There is no support for replication in rename
420 : * @param old_path
421 : * @param new_path
422 : * @return 0 on success, -1 on failure
423 : */
424 : int
425 12 : gkfs_rename(const string& old_path, const string& new_path) {
426 24 : auto md_old = gkfs::utils::get_metadata(old_path, false);
427 :
428 : // if the file is not found, or it is a renamed one cancel.
429 12 : if(!md_old || md_old.value().blocks() == -1) {
430 1 : return -1;
431 : }
432 23 : auto md_new = gkfs::utils::get_metadata(new_path, false);
433 11 : if(md_new) {
434 : // the new file exists... check for circular...
435 1 : if(md_new.value().blocks() == -1 &&
436 2 : md_old.value().target_path() == new_path) {
437 : // the new file is a renamed file, so we need to get the metadata of
438 : // the original file.
439 1 : LOG(DEBUG, "Destroying Circular Rename '{}' --> '{}'", old_path,
440 1 : new_path);
441 1 : gkfs::metadata::MetadentryUpdateFlags flags{};
442 1 : flags.atime = false;
443 1 : flags.mtime = false;
444 1 : flags.ctime = false;
445 1 : flags.blocks = true;
446 1 : flags.mode = false;
447 1 : flags.size = false;
448 1 : flags.uid = false;
449 1 : flags.gid = false;
450 1 : flags.link_count = false;
451 1 : md_old.value().blocks(0);
452 2 : md_old.value().target_path("");
453 :
454 1 : auto err = gkfs::rpc::forward_update_metadentry(
455 1 : new_path, md_old.value(), flags, 0);
456 :
457 1 : if(err) {
458 0 : errno = err;
459 0 : return -1;
460 : }
461 : // Delete old file
462 1 : err = gkfs::rpc::forward_remove(old_path, CTX->get_replicas());
463 1 : if(err) {
464 0 : errno = err;
465 0 : return -1;
466 : }
467 : return 0;
468 : }
469 : return -1;
470 : }
471 :
472 10 : auto err = gkfs::rpc::forward_rename(old_path, new_path, md_old.value());
473 10 : if(err) {
474 0 : errno = err;
475 0 : return -1;
476 : }
477 :
478 : return 0;
479 : }
480 : #endif
481 : #endif
482 :
483 : /**
484 : * gkfs wrapper for stat() system calls
485 : * errno may be set
486 : * @param path
487 : * @param buf
488 : * @param follow_links
489 : * @return 0 on success, -1 on failure
490 : */
491 : int
492 65 : gkfs_stat(const string& path, struct stat* buf, bool follow_links) {
493 130 : auto md = gkfs::utils::get_metadata(path, follow_links);
494 65 : if(!md) {
495 : return -1;
496 : }
497 : #ifdef HAS_SYMLINKS
498 : #ifdef HAS_RENAME
499 57 : if(md.value().blocks() == -1) {
500 7 : errno = ENOENT;
501 7 : return -1;
502 : } else {
503 62 : while(!md.value().target_path().empty()) {
504 36 : md = gkfs::utils::get_metadata(md.value().target_path(), false);
505 12 : if(!md) {
506 : return -1;
507 : }
508 : }
509 : }
510 : #endif
511 : #endif
512 50 : gkfs::utils::metadata_to_stat(path, *md, *buf);
513 : return 0;
514 : }
515 :
516 : #ifdef STATX_TYPE
517 :
518 : /**
519 : * gkfs wrapper for statx() system calls
520 : * errno may be set
521 : * @param dirfs
522 : * @param path
523 : * @param flags
524 : * @param mask
525 : * @param buf
526 : * @param follow_links
527 : * @return 0 on success, -1 on failure
528 : */
529 : int
530 4 : gkfs_statx(int dirfs, const std::string& path, int flags, unsigned int mask,
531 : struct statx* buf, bool follow_links) {
532 8 : auto md = gkfs::utils::get_metadata(path, follow_links);
533 :
534 4 : if(!md) {
535 : return -1;
536 : }
537 : #ifdef HAS_SYMLINKS
538 : #ifdef HAS_RENAME
539 3 : if(md.value().blocks() == -1) {
540 0 : errno = ENOENT;
541 0 : return -1;
542 : } else {
543 3 : while(!md.value().target_path().empty()) {
544 0 : md = gkfs::utils::get_metadata(md.value().target_path(), false);
545 0 : if(!md) {
546 : return -1;
547 : }
548 : }
549 : }
550 : #endif
551 : #endif
552 3 : struct stat tmp {};
553 :
554 3 : gkfs::utils::metadata_to_stat(path, *md, tmp);
555 :
556 3 : buf->stx_mask = 0;
557 3 : buf->stx_blksize = tmp.st_blksize;
558 3 : buf->stx_attributes = 0;
559 3 : buf->stx_nlink = tmp.st_nlink;
560 3 : buf->stx_uid = tmp.st_uid;
561 3 : buf->stx_gid = tmp.st_gid;
562 3 : buf->stx_mode = tmp.st_mode;
563 3 : buf->stx_ino = tmp.st_ino;
564 3 : buf->stx_size = tmp.st_size;
565 3 : buf->stx_blocks = tmp.st_blocks;
566 3 : buf->stx_attributes_mask = 0;
567 :
568 3 : buf->stx_atime.tv_sec = tmp.st_atim.tv_sec;
569 3 : buf->stx_atime.tv_nsec = tmp.st_atim.tv_nsec;
570 :
571 3 : buf->stx_mtime.tv_sec = tmp.st_mtim.tv_sec;
572 3 : buf->stx_mtime.tv_nsec = tmp.st_mtim.tv_nsec;
573 :
574 3 : buf->stx_ctime.tv_sec = tmp.st_ctim.tv_sec;
575 3 : buf->stx_ctime.tv_nsec = tmp.st_ctim.tv_nsec;
576 :
577 3 : buf->stx_btime = buf->stx_atime;
578 :
579 3 : return 0;
580 : }
581 :
582 : #endif
583 :
584 : /**
585 : * gkfs wrapper for statfs() system calls
586 : * errno may be set
587 : * @param buf
588 : * @return 0 on success, -1 on failure
589 : */
590 : int
591 2 : gkfs_statfs(struct statfs* buf) {
592 :
593 2 : auto ret = gkfs::rpc::forward_get_chunk_stat();
594 2 : auto err = ret.first;
595 2 : if(err) {
596 0 : LOG(ERROR, "{}() Failure with error: '{}'", err);
597 0 : errno = err;
598 0 : return -1;
599 : }
600 2 : auto blk_stat = ret.second;
601 2 : buf->f_type = 0;
602 2 : buf->f_bsize = blk_stat.chunk_size;
603 2 : buf->f_blocks = blk_stat.chunk_total;
604 2 : buf->f_bfree = blk_stat.chunk_free;
605 2 : buf->f_bavail = blk_stat.chunk_free;
606 2 : buf->f_files = 0;
607 2 : buf->f_ffree = 0;
608 2 : buf->f_fsid = {0, 0};
609 2 : buf->f_namelen = path::max_length;
610 2 : buf->f_frsize = 0;
611 2 : buf->f_flags =
612 : ST_NOATIME | ST_NODIRATIME | ST_NOSUID | ST_NODEV | ST_SYNCHRONOUS;
613 2 : return 0;
614 : }
615 :
616 : #ifdef GKFS_ENABLE_UNUSED_FUNCTIONS
617 : /**
618 : * gkfs wrapper for statvfs() system calls
619 : * errno may be set
620 : *
621 : * NOTE: Currently unused.
622 : *
623 : * @param buf
624 : * @return 0 on success, -1 on failure
625 : */
626 : int
627 : gkfs_statvfs(struct statvfs* buf) {
628 : auto ret = gkfs::rpc::forward_get_chunk_stat();
629 : auto err = ret.first;
630 : if(err) {
631 : LOG(ERROR, "{}() Failure with error: '{}'", err);
632 : errno = err;
633 : return -1;
634 : }
635 : auto blk_stat = ret.second;
636 : buf->f_bsize = blk_stat.chunk_size;
637 : buf->f_blocks = blk_stat.chunk_total;
638 : buf->f_bfree = blk_stat.chunk_free;
639 : buf->f_bavail = blk_stat.chunk_free;
640 : buf->f_files = 0;
641 : buf->f_ffree = 0;
642 : buf->f_favail = 0;
643 : buf->f_fsid = 0;
644 : buf->f_namemax = path::max_length;
645 : buf->f_frsize = 0;
646 : buf->f_flag =
647 : ST_NOATIME | ST_NODIRATIME | ST_NOSUID | ST_NODEV | ST_SYNCHRONOUS;
648 : return 0;
649 : }
650 : #endif
651 :
652 : /**
653 : * gkfs wrapper for lseek() system calls with available file descriptor
654 : * errno may be set
655 : * @param fd
656 : * @param offset
657 : * @param whence
658 : * @return 0 on success, -1 on failure
659 : */
660 : off_t
661 12 : gkfs_lseek(unsigned int fd, off_t offset, unsigned int whence) {
662 24 : return gkfs_lseek(CTX->file_map()->get(fd), offset, whence);
663 : }
664 :
665 : /**
666 : * gkfs wrapper for lseek() system calls with available shared ptr to gkfs
667 : * FileMap errno may be set
668 : * @param gkfs_fd
669 : * @param offset
670 : * @param whence
671 : * @return 0 on success, -1 on failure
672 : */
673 : off_t
674 12 : gkfs_lseek(shared_ptr<gkfs::filemap::OpenFile> gkfs_fd, off_t offset,
675 : unsigned int whence) {
676 12 : switch(whence) {
677 3 : case SEEK_SET:
678 3 : if(offset < 0) {
679 1 : errno = EINVAL;
680 1 : return -1;
681 : }
682 2 : gkfs_fd->pos(offset);
683 2 : break;
684 1 : case SEEK_CUR:
685 1 : gkfs_fd->pos(gkfs_fd->pos() + offset);
686 1 : break;
687 5 : case SEEK_END: {
688 : // TODO: handle replicas
689 5 : auto ret =
690 5 : gkfs::rpc::forward_get_metadentry_size(gkfs_fd->path(), 0);
691 5 : auto err = ret.first;
692 5 : if(err) {
693 0 : errno = err;
694 1 : return -1;
695 : }
696 :
697 5 : auto file_size = ret.second;
698 5 : if(offset < 0 && file_size < -offset) {
699 1 : errno = EINVAL;
700 1 : return -1;
701 : }
702 4 : gkfs_fd->pos(file_size + offset);
703 4 : break;
704 : }
705 1 : case SEEK_DATA:
706 1 : LOG(WARNING, "SEEK_DATA whence is not supported");
707 : // We do not support this whence yet
708 1 : errno = EINVAL;
709 1 : return -1;
710 1 : case SEEK_HOLE:
711 1 : LOG(WARNING, "SEEK_HOLE whence is not supported");
712 : // We do not support this whence yet
713 1 : errno = EINVAL;
714 1 : return -1;
715 1 : default:
716 1 : LOG(WARNING, "Unknown whence value {:#x}", whence);
717 1 : errno = EINVAL;
718 1 : return -1;
719 : }
720 7 : return gkfs_fd->pos();
721 : }
722 :
723 : /**
724 : * wrapper function for gkfs_truncate
725 : * errno may be set
726 : * @param path
727 : * @param old_size
728 : * @param new_size
729 : * @return 0 on success, -1 on failure
730 : */
731 : int
732 7 : gkfs_truncate(const std::string& path, off_t old_size, off_t new_size) {
733 7 : assert(new_size >= 0);
734 7 : assert(new_size <= old_size);
735 :
736 7 : if(new_size == old_size) {
737 : return 0;
738 : }
739 6 : for(auto copy = 0; copy < (CTX->get_replicas() + 1); copy++) {
740 3 : auto err = gkfs::rpc::forward_decr_size(path, new_size, copy);
741 3 : if(err) {
742 0 : LOG(DEBUG, "Failed to decrease size");
743 0 : errno = err;
744 0 : return -1;
745 : }
746 : }
747 :
748 3 : auto err = gkfs::rpc::forward_truncate(path, old_size, new_size,
749 3 : CTX->get_replicas());
750 3 : if(err) {
751 0 : LOG(DEBUG, "Failed to truncate data");
752 0 : errno = err;
753 0 : return -1;
754 : }
755 : return 0;
756 : }
757 :
758 : /**
759 : * gkfs wrapper for truncate() system calls
760 : * errno may be set
761 : * @param path
762 : * @param length
763 : * @return 0 on success, -1 on failure
764 : */
765 : int
766 7 : gkfs_truncate(const std::string& path, off_t length) {
767 : /* TODO CONCURRENCY:
768 : * At the moment we first ask the length to the metadata-server in order to
769 : * know which data-server have data to be deleted.
770 : *
771 : * From the moment we issue the gkfs_stat and the moment we issue the
772 : * gkfs_trunc_data, some more data could have been added to the file and the
773 : * length increased.
774 : */
775 7 : if(length < 0) {
776 1 : LOG(DEBUG, "Length is negative: {}", length);
777 1 : errno = EINVAL;
778 1 : return -1;
779 : }
780 :
781 13 : auto md = gkfs::utils::get_metadata(path, true);
782 6 : if(!md) {
783 : return -1;
784 : }
785 :
786 : // If rename is enabled we need to check if the file is renamed
787 : #ifdef HAS_SYMLINKS
788 : #ifdef HAS_RENAME
789 6 : if(md.value().blocks() == -1) {
790 0 : errno = ENOENT;
791 0 : return -1;
792 6 : } else if(!md.value().target_path().empty()) {
793 2 : std::string new_path;
794 2 : while(!md.value().target_path().empty()) {
795 1 : new_path = md.value().target_path();
796 3 : md = gkfs::utils::get_metadata(md.value().target_path());
797 : }
798 : // This could be optimized
799 1 : auto size = md->size();
800 1 : if(static_cast<unsigned long>(length) > size) {
801 0 : LOG(DEBUG, "Length is greater then file size: {} > {}", length,
802 0 : size);
803 0 : errno = EINVAL;
804 0 : return -1;
805 : }
806 1 : return gkfs_truncate(new_path, size, length);
807 : }
808 : #endif
809 : #endif
810 :
811 5 : auto size = md->size();
812 5 : if(static_cast<unsigned long>(length) > size) {
813 1 : LOG(DEBUG, "Length is greater then file size: '{}' > '{}'", length,
814 1 : size);
815 1 : auto output_fd = gkfs_open(path, md->mode(), O_WRONLY);
816 1 : if(output_fd == -1) {
817 0 : errno = EINVAL;
818 0 : return -1;
819 : }
820 1 : gkfs_lseek(output_fd, 0, SEEK_END);
821 1 : ssize_t n = static_cast<unsigned long>(length) - size;
822 : // Zeroes the buffer. All make_* are value initialized
823 2 : auto buf = std::make_unique<char[]>(n);
824 1 : if(!buf) {
825 0 : errno = ENOMEM;
826 0 : return -1;
827 : }
828 1 : if(gkfs_write(output_fd, buf.get(), (size_t) n) != n) {
829 0 : errno = EINVAL;
830 0 : return -1;
831 : }
832 1 : CTX->file_map()->remove(output_fd);
833 : return 0;
834 : }
835 4 : return gkfs_truncate(path, size, length);
836 : }
837 :
838 : /**
839 : * gkfs wrapper for dup() system calls
840 : * errno may be set
841 : * @param oldfd
842 : * @return file descriptor int or -1 on error
843 : */
844 : int
845 3 : gkfs_dup(const int oldfd) {
846 3 : return CTX->file_map()->dup(oldfd);
847 : }
848 :
849 : /**
850 : * gkfs wrapper for dup2() system calls
851 : * errno may be set
852 : * @param oldfd
853 : * @param newfd
854 : * @return file descriptor int or -1 on error
855 : */
856 : int
857 1 : gkfs_dup2(const int oldfd, const int newfd) {
858 1 : return CTX->file_map()->dup2(oldfd, newfd);
859 : }
860 :
861 : /**
862 : * Wrapper function for all gkfs write operations
863 : * errno may be set
864 : * @param file
865 : * @param buf
866 : * @param count
867 : * @param offset
868 : * @param update_pos pos should only be updated for some write operations (see
869 : * man 2 pwrite)
870 : * @return written size or -1 on error
871 : */
872 : ssize_t
873 42 : gkfs_pwrite(std::shared_ptr<gkfs::filemap::OpenFile> file, const char* buf,
874 : size_t count, off64_t offset, bool update_pos) {
875 42 : if(file->type() != gkfs::filemap::FileType::regular) {
876 1 : assert(file->type() == gkfs::filemap::FileType::directory);
877 1 : LOG(WARNING, "Cannot write to directory");
878 1 : errno = EISDIR;
879 1 : return -1;
880 : }
881 82 : auto path = make_unique<string>(file->path());
882 41 : auto is_append = file->get_flag(gkfs::filemap::OpenFile_flags::append);
883 41 : auto write_size = 0;
884 41 : auto num_replicas = CTX->get_replicas();
885 :
886 41 : auto ret_offset = gkfs::rpc::forward_update_metadentry_size(
887 41 : *path, count, offset, is_append, num_replicas);
888 41 : auto err = ret_offset.first;
889 41 : if(err) {
890 0 : LOG(ERROR, "update_metadentry_size() failed with err '{}'", err);
891 0 : errno = err;
892 0 : return -1;
893 : }
894 41 : if(is_append) {
895 : // When append is set the EOF is set to the offset
896 : // forward_update_metadentry_size returns. This is because it is an
897 : // atomic operation on the server and reserves the space for this append
898 3 : if(ret_offset.second == -1) {
899 0 : LOG(ERROR,
900 : "update_metadentry_size() received -1 as starting offset. "
901 : "This occurs when the staring offset could not be extracted "
902 0 : "from RocksDB's merge operations. Inform GekkoFS devs.");
903 0 : errno = EIO;
904 0 : return -1;
905 : }
906 : offset = ret_offset.second;
907 : }
908 :
909 41 : auto ret_write = gkfs::rpc::forward_write(*path, buf, offset, count, 0);
910 41 : err = ret_write.first;
911 41 : write_size = ret_write.second;
912 :
913 41 : if(num_replicas > 0) {
914 0 : auto ret_write_repl = gkfs::rpc::forward_write(*path, buf, offset,
915 0 : count, num_replicas);
916 :
917 0 : if(err and ret_write_repl.first == 0) {
918 : // We succesfully write the data to some replica
919 0 : err = ret_write_repl.first;
920 : // Write size will be wrong
921 0 : write_size = ret_write_repl.second;
922 : }
923 : }
924 :
925 41 : if(err) {
926 0 : LOG(WARNING, "gkfs::rpc::forward_write() failed with err '{}'", err);
927 0 : errno = err;
928 0 : return -1;
929 : }
930 41 : if(update_pos) {
931 : // Update offset in file descriptor in the file map
932 31 : file->pos(offset + write_size);
933 : }
934 41 : if(static_cast<size_t>(write_size) != count) {
935 0 : LOG(WARNING,
936 : "gkfs::rpc::forward_write() wrote '{}' bytes instead of '{}'",
937 : write_size, count);
938 : }
939 41 : return write_size; // return written size
940 : }
941 :
942 : /**
943 : * gkfs wrapper for pwrite() system calls
944 : * errno may be set
945 : * @param fd
946 : * @param buf
947 : * @param count
948 : * @param offset
949 : * @return written size or -1 on error
950 : */
951 : ssize_t
952 2 : gkfs_pwrite_ws(int fd, const void* buf, size_t count, off64_t offset) {
953 2 : auto file = CTX->file_map()->get(fd);
954 8 : return gkfs_pwrite(file, reinterpret_cast<const char*>(buf), count, offset);
955 : }
956 :
957 : /**
958 : * gkfs wrapper for write() system calls
959 : * errno may be set
960 : * @param fd
961 : * @param buf
962 : * @param count
963 : * @return written size or -1 on error
964 : */
965 : ssize_t
966 32 : gkfs_write(int fd, const void* buf, size_t count) {
967 32 : auto gkfs_fd = CTX->file_map()->get(fd);
968 : // call pwrite and update pos
969 64 : auto ret = gkfs_pwrite(gkfs_fd, reinterpret_cast<const char*>(buf), count,
970 64 : gkfs_fd->pos(), true);
971 64 : return ret;
972 : }
973 :
974 : /**
975 : * gkfs wrapper for pwritev() system calls
976 : * errno may be set
977 : * @param fd
978 : * @param iov
979 : * @param iovcnt
980 : * @param offset
981 : * @return written size or -1 on error
982 : */
983 : ssize_t
984 4 : gkfs_pwritev(int fd, const struct iovec* iov, int iovcnt, off_t offset) {
985 :
986 8 : auto file = CTX->file_map()->get(fd);
987 : auto pos = offset; // keep track of current position
988 : ssize_t written = 0;
989 : ssize_t ret;
990 12 : for(int i = 0; i < iovcnt; ++i) {
991 8 : auto count = (iov + i)->iov_len;
992 8 : if(count == 0) {
993 0 : continue;
994 : }
995 8 : auto buf = (iov + i)->iov_base;
996 16 : ret = gkfs_pwrite(file, reinterpret_cast<char*>(buf), count, pos);
997 8 : if(ret == -1) {
998 : break;
999 : }
1000 8 : written += ret;
1001 8 : pos += ret;
1002 :
1003 8 : if(static_cast<size_t>(ret) < count) {
1004 : break;
1005 : }
1006 : }
1007 :
1008 4 : if(written == 0) {
1009 0 : return -1;
1010 : }
1011 : return written;
1012 : }
1013 :
1014 : /**
1015 : * gkfs wrapper for writev() system calls
1016 : * errno may be set
1017 : * @param fd
1018 : * @param iov
1019 : * @param iovcnt
1020 : * @return written size or -1 on error
1021 : */
1022 : ssize_t
1023 2 : gkfs_writev(int fd, const struct iovec* iov, int iovcnt) {
1024 :
1025 4 : auto gkfs_fd = CTX->file_map()->get(fd);
1026 2 : auto pos = gkfs_fd->pos(); // retrieve the current offset
1027 2 : auto ret = gkfs_pwritev(fd, iov, iovcnt, pos);
1028 2 : assert(ret != 0);
1029 2 : if(ret < 0) {
1030 : return -1;
1031 : }
1032 2 : gkfs_fd->pos(pos + ret);
1033 : return ret;
1034 : }
1035 :
1036 : /**
1037 : * Wrapper function for all gkfs read operations
1038 : * @param file
1039 : * @param buf
1040 : * @param count
1041 : * @param offset
1042 : * @return read size or -1 on error
1043 : */
1044 : ssize_t
1045 29 : gkfs_pread(std::shared_ptr<gkfs::filemap::OpenFile> file, char* buf,
1046 : size_t count, off64_t offset) {
1047 29 : if(file->type() != gkfs::filemap::FileType::regular) {
1048 1 : assert(file->type() == gkfs::filemap::FileType::directory);
1049 1 : LOG(WARNING, "Cannot read from directory");
1050 1 : errno = EISDIR;
1051 1 : return -1;
1052 : }
1053 :
1054 : // Zeroing buffer before read is only relevant for sparse files. Otherwise
1055 : // sparse regions contain invalid data.
1056 28 : if constexpr(gkfs::config::io::zero_buffer_before_read) {
1057 : memset(buf, 0, sizeof(char) * count);
1058 : }
1059 28 : std::pair<int, off_t> ret;
1060 57 : std::set<int8_t> failed; // set with failed targets.
1061 28 : if(CTX->get_replicas() != 0) {
1062 :
1063 0 : ret = gkfs::rpc::forward_read(file->path(), buf, offset, count,
1064 0 : CTX->get_replicas(), failed);
1065 0 : while(ret.first == EIO) {
1066 0 : ret = gkfs::rpc::forward_read(file->path(), buf, offset, count,
1067 0 : CTX->get_replicas(), failed);
1068 0 : LOG(WARNING, "gkfs::rpc::forward_read() failed with ret '{}'",
1069 : ret.first);
1070 : }
1071 :
1072 : } else {
1073 56 : ret = gkfs::rpc::forward_read(file->path(), buf, offset, count, 0,
1074 28 : failed);
1075 : }
1076 :
1077 28 : auto err = ret.first;
1078 28 : if(err) {
1079 0 : LOG(WARNING, "gkfs::rpc::forward_read() failed with ret '{}'", err);
1080 0 : errno = err;
1081 0 : return -1;
1082 : }
1083 : // XXX check that we don't try to read past end of the file
1084 28 : return ret.second; // return read size
1085 : }
1086 :
1087 : /**
1088 : * gkfs wrapper for read() system calls
1089 : * errno may be set
1090 : * @param fd
1091 : * @param buf
1092 : * @param count
1093 : * @return read size or -1 on error
1094 : */
1095 : ssize_t
1096 24 : gkfs_read(int fd, void* buf, size_t count) {
1097 24 : auto gkfs_fd = CTX->file_map()->get(fd);
1098 24 : auto pos = gkfs_fd->pos(); // retrieve the current offset
1099 48 : auto ret = gkfs_pread(gkfs_fd, reinterpret_cast<char*>(buf), count, pos);
1100 : // Update offset in file descriptor in the file map
1101 24 : if(ret > 0) {
1102 13 : gkfs_fd->pos(pos + ret);
1103 : }
1104 48 : return ret;
1105 : }
1106 :
1107 : /**
1108 : * gkfs wrapper for preadv() system calls
1109 : * errno may be set
1110 : * @param fd
1111 : * @param iov
1112 : * @param iovcnt
1113 : * @param offset
1114 : * @return read size or -1 on error
1115 : */
1116 : ssize_t
1117 2 : gkfs_preadv(int fd, const struct iovec* iov, int iovcnt, off_t offset) {
1118 :
1119 4 : auto file = CTX->file_map()->get(fd);
1120 : auto pos = offset; // keep track of current position
1121 : ssize_t read = 0;
1122 : ssize_t ret;
1123 6 : for(int i = 0; i < iovcnt; ++i) {
1124 4 : auto count = (iov + i)->iov_len;
1125 4 : if(count == 0) {
1126 0 : continue;
1127 : }
1128 4 : auto buf = (iov + i)->iov_base;
1129 8 : ret = gkfs_pread(file, reinterpret_cast<char*>(buf), count, pos);
1130 4 : if(ret == -1) {
1131 : break;
1132 : }
1133 4 : read += ret;
1134 4 : pos += ret;
1135 :
1136 4 : if(static_cast<size_t>(ret) < count) {
1137 : break;
1138 : }
1139 : }
1140 :
1141 2 : if(read == 0) {
1142 0 : return -1;
1143 : }
1144 : return read;
1145 : }
1146 :
1147 : /**
1148 : * gkfs wrapper for readv() system calls
1149 : * errno may be set
1150 : * @param fd
1151 : * @param iov
1152 : * @param iovcnt
1153 : * @return read size or -1 on error
1154 : */
1155 : ssize_t
1156 1 : gkfs_readv(int fd, const struct iovec* iov, int iovcnt) {
1157 :
1158 2 : auto gkfs_fd = CTX->file_map()->get(fd);
1159 1 : auto pos = gkfs_fd->pos(); // retrieve the current offset
1160 1 : auto ret = gkfs_preadv(fd, iov, iovcnt, pos);
1161 1 : assert(ret != 0);
1162 1 : if(ret < 0) {
1163 : return -1;
1164 : }
1165 1 : gkfs_fd->pos(pos + ret);
1166 : return ret;
1167 : }
1168 :
1169 : /**
1170 : * gkfs wrapper for pread() system calls
1171 : * errno may be set
1172 : * @param fd
1173 : * @param buf
1174 : * @param count
1175 : * @param offset
1176 : * @return read size or -1 on error
1177 : */
1178 : ssize_t
1179 1 : gkfs_pread_ws(int fd, void* buf, size_t count, off64_t offset) {
1180 1 : auto gkfs_fd = CTX->file_map()->get(fd);
1181 4 : return gkfs_pread(gkfs_fd, reinterpret_cast<char*>(buf), count, offset);
1182 : }
1183 :
1184 : /**
1185 : * wrapper function for opening directories
1186 : * errno may be set
1187 : * @param path
1188 : * @return 0 on success or -1 on error
1189 : */
1190 : int
1191 22 : gkfs_opendir(const std::string& path) {
1192 44 : auto md = gkfs::utils::get_metadata(path);
1193 22 : if(!md) {
1194 : return -1;
1195 : }
1196 :
1197 22 : if(!S_ISDIR(md->mode())) {
1198 0 : LOG(DEBUG, "Path is not a directory");
1199 0 : errno = ENOTDIR;
1200 0 : return -1;
1201 : }
1202 :
1203 44 : auto ret = gkfs::rpc::forward_get_dirents(path);
1204 22 : auto err = ret.first;
1205 22 : if(err) {
1206 0 : errno = err;
1207 0 : return -1;
1208 : }
1209 22 : assert(ret.second);
1210 66 : return CTX->file_map()->add(ret.second);
1211 : }
1212 :
1213 : /**
1214 : * gkfs wrapper for rmdir() system calls
1215 : * errno may be set
1216 : * @param path
1217 : * @return 0 on success or -1 on error
1218 : */
1219 : int
1220 4 : gkfs_rmdir(const std::string& path) {
1221 8 : auto md = gkfs::utils::get_metadata(path);
1222 4 : if(!md) {
1223 0 : LOG(DEBUG, "Error: Path '{}' err code '{}' ", path, strerror(errno));
1224 0 : return -1;
1225 : }
1226 4 : if(!S_ISDIR(md->mode())) {
1227 1 : LOG(DEBUG, "Path '{}' is not a directory", path);
1228 1 : errno = ENOTDIR;
1229 1 : return -1;
1230 : }
1231 :
1232 7 : auto ret = gkfs::rpc::forward_get_dirents(path);
1233 3 : auto err = ret.first;
1234 3 : if(err) {
1235 0 : errno = err;
1236 0 : return -1;
1237 : }
1238 3 : assert(ret.second);
1239 6 : auto open_dir = ret.second;
1240 3 : if(open_dir->size() != 0) {
1241 1 : errno = ENOTEMPTY;
1242 1 : return -1;
1243 : }
1244 2 : err = gkfs::rpc::forward_remove(path, CTX->get_replicas());
1245 2 : if(err) {
1246 0 : errno = err;
1247 0 : return -1;
1248 : }
1249 : return 0;
1250 : }
1251 :
1252 : /**
1253 : * gkfs wrapper for getdents() system calls
1254 : * errno may be set
1255 : * @param fd
1256 : * @param dirp
1257 : * @param count
1258 : * @return 0 on success or -1 on error
1259 : */
1260 : int
1261 0 : gkfs_getdents(unsigned int fd, struct linux_dirent* dirp, unsigned int count) {
1262 :
1263 : // Get opendir object (content was downloaded with opendir() call)
1264 0 : auto open_dir = CTX->file_map()->get_dir(fd);
1265 0 : if(open_dir == nullptr) {
1266 : // Cast did not succeeded: open_file is a regular file
1267 0 : errno = EBADF;
1268 0 : return -1;
1269 : }
1270 :
1271 : // get directory position of which entries to return
1272 0 : auto pos = open_dir->pos();
1273 0 : if(pos >= open_dir->size()) {
1274 : return 0;
1275 : }
1276 :
1277 : unsigned int written = 0;
1278 0 : struct linux_dirent* current_dirp = nullptr;
1279 0 : while(pos < open_dir->size()) {
1280 : // get dentry fir current position
1281 0 : auto de = open_dir->getdent(pos);
1282 : /*
1283 : * Calculate the total dentry size within the kernel struct
1284 : * `linux_dirent` depending on the file name size. The size is then
1285 : * aligned to the size of `long` boundary. This line was originally
1286 : * defined in the linux kernel: fs/readdir.c in function filldir(): int
1287 : * reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
1288 : * sizeof(long)); However, since d_name is null-terminated and
1289 : * de.name().size() does not include space for the null-terminator, we
1290 : * add 1. Thus, + 3 in total.
1291 : */
1292 0 : auto total_size = ALIGN(offsetof(struct linux_dirent, d_name) +
1293 : de.name().size() + 3,
1294 : sizeof(long));
1295 0 : if(total_size > (count - written)) {
1296 : // no enough space left on user buffer to insert next dirent
1297 : break;
1298 : }
1299 0 : current_dirp = reinterpret_cast<struct linux_dirent*>(
1300 0 : reinterpret_cast<char*>(dirp) + written);
1301 0 : current_dirp->d_ino =
1302 0 : std::hash<std::string>()(open_dir->path() + "/" + de.name());
1303 :
1304 0 : current_dirp->d_reclen = total_size;
1305 :
1306 0 : *(reinterpret_cast<char*>(current_dirp) + total_size - 1) =
1307 0 : ((de.type() == gkfs::filemap::FileType::regular) ? DT_REG
1308 : : DT_DIR);
1309 :
1310 0 : LOG(DEBUG, "name {}: {}", pos, de.name());
1311 0 : std::strcpy(&(current_dirp->d_name[0]), de.name().c_str());
1312 0 : ++pos;
1313 0 : current_dirp->d_off = pos;
1314 0 : written += total_size;
1315 : }
1316 :
1317 0 : if(written == 0) {
1318 0 : errno = EINVAL;
1319 0 : return -1;
1320 : }
1321 : // set directory position for next getdents() call
1322 0 : open_dir->pos(pos);
1323 0 : return written;
1324 : }
1325 :
1326 : /**
1327 : * gkfs wrapper for getdents64() system calls
1328 : * errno may be set
1329 : * @param fd
1330 : * @param dirp
1331 : * @param count
1332 : * @return 0 on success or -1 on error
1333 : */
1334 : int
1335 30 : gkfs_getdents64(unsigned int fd, struct linux_dirent64* dirp,
1336 : unsigned int count) {
1337 :
1338 60 : auto open_dir = CTX->file_map()->get_dir(fd);
1339 30 : if(open_dir == nullptr) {
1340 : // Cast did not succeeded: open_file is a regular file
1341 0 : errno = EBADF;
1342 0 : return -1;
1343 : }
1344 30 : auto pos = open_dir->pos();
1345 30 : if(pos >= open_dir->size()) {
1346 : return 0;
1347 : }
1348 : unsigned int written = 0;
1349 1038 : struct linux_dirent64* current_dirp = nullptr;
1350 1038 : while(pos < open_dir->size()) {
1351 2050 : auto de = open_dir->getdent(pos);
1352 : /*
1353 : * Calculate the total dentry size within the kernel struct
1354 : * `linux_dirent` depending on the file name size. The size is then
1355 : * aligned to the size of `long` boundary.
1356 : *
1357 : * This line was originally defined in the linux kernel: fs/readdir.c in
1358 : * function filldir64(): int reclen = ALIGN(offsetof(struct
1359 : * linux_dirent64, d_name) + namlen + 1, sizeof(u64)); We keep + 1
1360 : * because: Since d_name is null-terminated and de.name().size() does
1361 : * not include space for the null-terminator, we add 1. Since d_name in
1362 : * our `struct linux_dirent64` definition is not a zero-size array (as
1363 : * opposed to the kernel version), we subtract 1. Thus, it stays + 1.
1364 : */
1365 1025 : auto total_size = ALIGN(offsetof(struct linux_dirent64, d_name) +
1366 : de.name().size() + 1,
1367 : sizeof(uint64_t));
1368 1025 : if(total_size > (count - written)) {
1369 : // no enough space left on user buffer to insert next dirent
1370 : break;
1371 : }
1372 1025 : current_dirp = reinterpret_cast<struct linux_dirent64*>(
1373 1025 : reinterpret_cast<char*>(dirp) + written);
1374 2050 : current_dirp->d_ino =
1375 2050 : std::hash<std::string>()(open_dir->path() + "/" + de.name());
1376 :
1377 1025 : current_dirp->d_reclen = total_size;
1378 1025 : current_dirp->d_type =
1379 1025 : ((de.type() == gkfs::filemap::FileType::regular) ? DT_REG
1380 : : DT_DIR);
1381 :
1382 1025 : LOG(DEBUG, "name {}: {}", pos, de.name());
1383 1025 : std::strcpy(&(current_dirp->d_name[0]), de.name().c_str());
1384 1025 : ++pos;
1385 1025 : current_dirp->d_off = pos;
1386 1025 : written += total_size;
1387 : }
1388 :
1389 13 : if(written == 0) {
1390 0 : errno = EINVAL;
1391 0 : return -1;
1392 : }
1393 13 : open_dir->pos(pos);
1394 13 : return written;
1395 : }
1396 :
1397 :
1398 : #ifdef HAS_SYMLINKS
1399 : #ifdef GKFS_ENABLE_UNUSED_FUNCTIONS
1400 : /**
1401 : * gkfs wrapper for make symlink() system calls
1402 : * errno may be set
1403 : *
1404 : * * NOTE: Currently unused
1405 : *
1406 : * @param path
1407 : * @param target_path
1408 : * @return 0 on success or -1 on error
1409 : */
1410 : int
1411 : gkfs_mk_symlink(const std::string& path, const std::string& target_path) {
1412 : /* The following check is not POSIX compliant.
1413 : * In POSIX the target is not checked at all.
1414 : * Here if the target is a directory we raise a NOTSUP error.
1415 : * So that application know we don't support link to directory.
1416 : */
1417 : auto target_md = gkfs::utils::get_metadata(target_path, false);
1418 : if(target_md) {
1419 : auto trg_mode = target_md->mode();
1420 : if(!(S_ISREG(trg_mode) || S_ISLNK(trg_mode))) {
1421 : assert(S_ISDIR(trg_mode));
1422 : LOG(DEBUG, "Target path is a directory. Not supported");
1423 : errno = ENOTSUP;
1424 : return -1;
1425 : }
1426 : }
1427 :
1428 : if(check_parent_dir(path)) {
1429 : return -1;
1430 : }
1431 :
1432 : auto link_md = gkfs::utils::get_metadata(path, false);
1433 : if(link_md) {
1434 : LOG(DEBUG, "Link exists: '{}'", path);
1435 : errno = EEXIST;
1436 : return -1;
1437 : }
1438 :
1439 : auto err = gkfs::rpc::forward_mk_symlink(path, target_path);
1440 : if(err) {
1441 : errno = err;
1442 : return -1;
1443 : }
1444 : return 0;
1445 : }
1446 :
1447 : /**
1448 : * gkfs wrapper for reading symlinks
1449 : * errno may be set
1450 : *
1451 : * NOTE: Currently unused
1452 : *
1453 : * @param path
1454 : * @param buf
1455 : * @param bufsize
1456 : * @return 0 on success or -1 on error
1457 : */
1458 : int
1459 : gkfs_readlink(const std::string& path, char* buf, int bufsize) {
1460 : auto md = gkfs::utils::get_metadata(path, false);
1461 : if(!md) {
1462 : LOG(DEBUG, "Named link doesn't exist");
1463 : return -1;
1464 : }
1465 : if(!(md->is_link())) {
1466 : LOG(DEBUG, "The named file is not a symbolic link");
1467 : errno = EINVAL;
1468 : return -1;
1469 : }
1470 : int path_size = md->target_path().size() + CTX->mountdir().size();
1471 : if(path_size >= bufsize) {
1472 : LOG(WARNING, "Destination buffer size is too short: {} < {}, {} ",
1473 : bufsize, path_size, md->target_path());
1474 : errno = ENAMETOOLONG;
1475 : return -1;
1476 : }
1477 :
1478 : CTX->mountdir().copy(buf, CTX->mountdir().size());
1479 : std::strcpy(buf + CTX->mountdir().size(), md->target_path().c_str());
1480 : return path_size;
1481 : }
1482 : #endif
1483 : #endif
1484 :
1485 : } // namespace gkfs::syscall
1486 :
1487 :
1488 : /* This function defines an extension of the dirents prepared to do a find-like
1489 : * function The function only sends the request to the specified server
1490 : */
1491 : extern "C" int
1492 4 : gkfs_getsingleserverdir(const char* path, struct dirent_extended* dirp,
1493 : unsigned int count, int server) {
1494 :
1495 12 : auto ret = gkfs::rpc::forward_get_dirents_single(path, server);
1496 4 : auto err = ret.first;
1497 4 : if(err) {
1498 0 : errno = err;
1499 0 : return -1;
1500 : }
1501 :
1502 8 : auto open_dir = ret.second;
1503 4 : unsigned int pos = 0;
1504 4 : unsigned int written = 0;
1505 4 : struct dirent_extended* current_dirp = nullptr;
1506 8 : while(pos < open_dir.size()) {
1507 8 : auto de = open_dir[pos];
1508 : /*
1509 : * Calculate the total dentry size within the 'dirent_extended`
1510 : * depending on the file name size. The size is then aligned to the size
1511 : * of `long` boundary.
1512 : */
1513 4 : auto total_size = ALIGN(offsetof(struct dirent_extended, d_name) +
1514 : (get<0>(de)).size() + 1,
1515 : sizeof(uint64_t));
1516 4 : if(total_size > (count - written)) {
1517 : // no enough space left on user buffer to insert next dirent
1518 : break;
1519 : }
1520 4 : current_dirp = reinterpret_cast<struct dirent_extended*>(
1521 4 : reinterpret_cast<char*>(dirp) + written);
1522 :
1523 4 : current_dirp->d_reclen = total_size;
1524 4 : current_dirp->d_type = get<1>(de);
1525 4 : current_dirp->size = get<2>(de);
1526 4 : current_dirp->ctime = get<3>(de);
1527 :
1528 4 : LOG(DEBUG, "name {}: {} {} {} {} / size {}", pos, get<0>(de),
1529 4 : get<1>(de), get<2>(de), get<3>(de), total_size);
1530 4 : std::strcpy(&(current_dirp->d_name[0]), (get<0>(de)).c_str());
1531 4 : ++pos;
1532 4 : written += total_size;
1533 : }
1534 :
1535 4 : if(written == 0) {
1536 2 : errno = EINVAL;
1537 2 : return -1;
1538 : }
1539 2 : return written;
1540 : }
|