1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
22 #if defined(__FreeBSD__)
23 #include <sys/param.h>
26 #include "osd/osd_types.h"
27 #include "include/object.h"
28 #include "common/config.h"
29 #include "common/debug.h"
30 #include "include/buffer.h"
31 #include "common/ceph_crypto.h"
32 #include "common/errno.h"
33 #include "include/compat.h"
34 #include "chain_xattr.h"
38 #define dout_context cct
39 #define dout_subsys ceph_subsys_filestore
41 #define dout_prefix *_dout << "LFNIndex(" << get_base_path() << ") "
49 using ceph::crypto::SHA1
;
51 using ceph::bufferlist
;
52 using ceph::bufferptr
;
54 const string
LFNIndex::LFN_ATTR
= "user.cephos.lfn";
55 const string
LFNIndex::PHASH_ATTR_PREFIX
= "user.cephos.phash.";
56 const string
LFNIndex::SUBDIR_PREFIX
= "DIR_";
57 const string
LFNIndex::FILENAME_COOKIE
= "long";
58 const int LFNIndex::FILENAME_PREFIX_LEN
= FILENAME_SHORT_LEN
- FILENAME_HASH_LEN
-
59 FILENAME_COOKIE
.size() -
61 void LFNIndex::maybe_inject_failure()
63 if (error_injection_enabled
) {
64 if (current_failure
> last_failure
&&
65 (((double)(rand() % 10000))/((double)(10000))
66 < error_injection_probability
)) {
67 last_failure
= current_failure
;
69 throw RetryException();
75 // Helper to close fd's when we leave scope. This is useful when used
76 // in combination with RetryException, thrown by the above.
79 explicit FDCloser(int f
) : fd(f
) {}
81 VOID_TEMP_FAILURE_RETRY(::close(fd
));
88 uint64_t LFNIndex::get_max_escaped_name_len(const hobject_t
&obj
)
90 ghobject_t
ghobj(obj
);
91 ghobj
.shard_id
= shard_id_t(0);
94 return lfn_generate_object_name_current(ghobj
).size();
102 int LFNIndex::created(const ghobject_t
&oid
, const char *path
)
105 vector
<string
> path_comp
;
107 r
= decompose_full_path(path
, &path_comp
, 0, &short_name
);
110 r
= lfn_created(path_comp
, oid
, short_name
);
113 /* This is hacky, but the only way we get ENOENT from lfn_created here is
114 * if we did a failure injection in _created below AND actually started the
115 * split or merge. In that case, lfn_created already suceeded, and
116 * WRAP_RETRY already cleaned it up and we are actually done. In a real
117 * failure, the filestore itself would have ended up calling this with
118 * the new path, not the old one, so we'd find it.
124 r
= _created(path_comp
, oid
, short_name
);
130 int LFNIndex::unlink(const ghobject_t
&oid
)
135 r
= _lookup(oid
, &path
, &short_name
, NULL
);
139 r
= _remove(path
, oid
, short_name
);
146 int LFNIndex::lookup(const ghobject_t
&oid
,
147 IndexedPath
*out_path
,
153 r
= _lookup(oid
, &path
, &short_name
, hardlink
);
156 string full_path
= get_full_path(path
, short_name
);
157 *out_path
= std::make_shared
<Path
>(full_path
, this);
162 int LFNIndex::pre_hash_collection(uint32_t pg_num
, uint64_t expected_num_objs
)
164 return _pre_hash_collection(pg_num
, expected_num_objs
);
168 int LFNIndex::collection_list_partial(const ghobject_t
&start
,
169 const ghobject_t
&end
,
171 vector
<ghobject_t
> *ls
,
174 return _collection_list_partial(start
, end
, max_count
, ls
, next
);
177 /* Derived class utility methods */
179 int LFNIndex::fsync_dir(const vector
<string
> &path
)
181 maybe_inject_failure();
182 int fd
= ::open(get_full_path_subdir(path
).c_str(), O_RDONLY
|O_CLOEXEC
);
186 maybe_inject_failure();
188 maybe_inject_failure();
190 derr
<< __func__
<< " fsync failed: " << cpp_strerror(errno
) << dendl
;
196 int LFNIndex::link_object(const vector
<string
> &from
,
197 const vector
<string
> &to
,
198 const ghobject_t
&oid
,
199 const string
&from_short_name
)
202 string from_path
= get_full_path(from
, from_short_name
);
204 maybe_inject_failure();
205 r
= lfn_get_name(to
, oid
, 0, &to_path
, 0);
208 maybe_inject_failure();
209 r
= ::link(from_path
.c_str(), to_path
.c_str());
210 maybe_inject_failure();
217 int LFNIndex::remove_objects(const vector
<string
> &dir
,
218 const map
<string
, ghobject_t
> &to_remove
,
219 map
<string
, ghobject_t
> *remaining
)
221 set
<string
> clean_chains
;
222 for (map
<string
, ghobject_t
>::const_iterator to_clean
= to_remove
.begin();
223 to_clean
!= to_remove
.end();
225 if (!lfn_is_hashed_filename(to_clean
->first
)) {
226 maybe_inject_failure();
227 int r
= ::unlink(get_full_path(dir
, to_clean
->first
).c_str());
228 maybe_inject_failure();
233 if (clean_chains
.count(lfn_get_short_name(to_clean
->second
, 0)))
236 map
<int, pair
<string
, ghobject_t
> > chain
;
237 for (int i
= 0; ; ++i
) {
238 string short_name
= lfn_get_short_name(to_clean
->second
, i
);
239 if (remaining
->count(short_name
)) {
240 chain
[i
] = *(remaining
->find(short_name
));
241 } else if (to_remove
.count(short_name
)) {
248 map
<int, pair
<string
, ghobject_t
> >::reverse_iterator candidate
= chain
.rbegin();
249 for (set
<int>::iterator i
= holes
.begin();
252 if (candidate
== chain
.rend() || *i
> candidate
->first
) {
253 string remove_path_name
=
254 get_full_path(dir
, lfn_get_short_name(to_clean
->second
, *i
));
255 maybe_inject_failure();
256 int r
= ::unlink(remove_path_name
.c_str());
257 maybe_inject_failure();
262 string from
= get_full_path(dir
, candidate
->second
.first
);
263 string to
= get_full_path(dir
, lfn_get_short_name(candidate
->second
.second
, *i
));
264 maybe_inject_failure();
265 int r
= ::rename(from
.c_str(), to
.c_str());
266 maybe_inject_failure();
269 remaining
->erase(candidate
->second
.first
);
270 remaining
->insert(pair
<string
, ghobject_t
>(
271 lfn_get_short_name(candidate
->second
.second
, *i
),
272 candidate
->second
.second
));
276 clean_chains
.insert(lfn_get_short_name(to_clean
->second
, 0));
281 int LFNIndex::move_objects(const vector
<string
> &from
,
282 const vector
<string
> &to
)
284 map
<string
, ghobject_t
> to_move
;
286 r
= list_objects(from
, 0, NULL
, &to_move
);
289 for (map
<string
,ghobject_t
>::iterator i
= to_move
.begin();
292 string from_path
= get_full_path(from
, i
->first
);
293 string to_path
, to_name
;
294 r
= lfn_get_name(to
, i
->second
, &to_name
, &to_path
, 0);
297 maybe_inject_failure();
298 r
= ::link(from_path
.c_str(), to_path
.c_str());
299 if (r
< 0 && errno
!= EEXIST
)
301 maybe_inject_failure();
302 r
= lfn_created(to
, i
->second
, to_name
);
303 maybe_inject_failure();
310 for (map
<string
,ghobject_t
>::iterator i
= to_move
.begin();
313 maybe_inject_failure();
314 r
= ::unlink(get_full_path(from
, i
->first
).c_str());
315 maybe_inject_failure();
319 return fsync_dir(from
);
322 int LFNIndex::remove_object(const vector
<string
> &from
,
323 const ghobject_t
&oid
)
327 maybe_inject_failure();
328 r
= get_mangled_name(from
, oid
, &short_name
, &exist
);
329 maybe_inject_failure();
334 return lfn_unlink(from
, oid
, short_name
);
337 int LFNIndex::get_mangled_name(const vector
<string
> &from
,
338 const ghobject_t
&oid
,
339 string
*mangled_name
, int *hardlink
)
341 return lfn_get_name(from
, oid
, mangled_name
, 0, hardlink
);
344 int LFNIndex::move_subdir(
347 const vector
<string
> &path
,
351 vector
<string
> sub_path(path
.begin(), path
.end());
352 sub_path
.push_back(dir
);
353 string
from_path(from
.get_full_path_subdir(sub_path
));
354 string
to_path(dest
.get_full_path_subdir(sub_path
));
355 int r
= ::rename(from_path
.c_str(), to_path
.c_str());
361 int LFNIndex::move_object(
364 const vector
<string
> &path
,
365 const pair
<string
, ghobject_t
> &obj
368 string
from_path(from
.get_full_path(path
, obj
.first
));
372 int r
= dest
.lfn_get_name(path
, obj
.second
, &to_name
, &to_path
, &exists
);
376 r
= ::link(from_path
.c_str(), to_path
.c_str());
380 r
= dest
.lfn_created(path
, obj
.second
, to_name
);
383 r
= dest
.fsync_dir(path
);
386 r
= from
.remove_object(path
, obj
.second
);
389 return from
.fsync_dir(path
);
393 static int get_hobject_from_oinfo(const char *dir
, const char *file
,
397 snprintf(path
, sizeof(path
), "%s/%s", dir
, file
);
398 // Hack, user.ceph._ is the attribute used to store the object info
400 int r
= chain_getxattr_buf(
409 object_info_t
oi(bl
);
410 *o
= ghobject_t(oi
.soid
);
415 int LFNIndex::list_objects(const vector
<string
> &to_list
, int max_objs
,
416 long *handle
, map
<string
, ghobject_t
> *out
)
418 string to_list_path
= get_full_path_subdir(to_list
);
419 DIR *dir
= ::opendir(to_list_path
.c_str());
424 if (handle
&& *handle
) {
425 seekdir(dir
, *handle
);
428 struct dirent
*de
= nullptr;
438 dout(0) << "readdir failed " << to_list_path
<< ": "
439 << cpp_strerror(-r
) << dendl
;
445 if (max_objs
> 0 && listed
>= max_objs
) {
448 if (de
->d_name
[0] == '.')
450 string
short_name(de
->d_name
);
452 if (lfn_is_object(short_name
)) {
453 r
= lfn_translate(to_list
, short_name
, &obj
);
459 string long_name
= lfn_generate_object_name(obj
);
460 if (!lfn_must_hash(long_name
)) {
461 ceph_assert(long_name
== short_name
);
463 if (index_version
== HASH_INDEX_TAG
)
464 get_hobject_from_oinfo(to_list_path
.c_str(), short_name
.c_str(), &obj
);
466 out
->insert(pair
<string
, ghobject_t
>(short_name
, obj
));
472 if (handle
&& !end
) {
473 *handle
= telldir(dir
);
482 int LFNIndex::list_subdirs(const vector
<string
> &to_list
,
485 string to_list_path
= get_full_path_subdir(to_list
);
486 DIR *dir
= ::opendir(to_list_path
.c_str());
490 struct dirent
*de
= nullptr;
498 dout(0) << "readdir failed " << to_list_path
<< ": "
499 << cpp_strerror(-r
) << dendl
;
503 string
short_name(de
->d_name
);
504 string demangled_name
;
505 if (lfn_is_subdir(short_name
, &demangled_name
)) {
506 out
->push_back(demangled_name
);
514 int LFNIndex::create_path(const vector
<string
> &to_create
)
516 maybe_inject_failure();
517 int r
= ::mkdir(get_full_path_subdir(to_create
).c_str(), 0777);
518 maybe_inject_failure();
525 int LFNIndex::remove_path(const vector
<string
> &to_remove
)
527 maybe_inject_failure();
528 int r
= ::rmdir(get_full_path_subdir(to_remove
).c_str());
529 maybe_inject_failure();
536 int LFNIndex::path_exists(const vector
<string
> &to_check
, int *exists
)
538 string full_path
= get_full_path_subdir(to_check
);
540 if (::stat(full_path
.c_str(), &buf
)) {
554 int LFNIndex::add_attr_path(const vector
<string
> &path
,
555 const string
&attr_name
,
556 bufferlist
&attr_value
)
558 string full_path
= get_full_path_subdir(path
);
559 maybe_inject_failure();
560 return chain_setxattr
<false, true>(
561 full_path
.c_str(), mangle_attr_name(attr_name
).c_str(),
562 reinterpret_cast<void *>(attr_value
.c_str()),
563 attr_value
.length());
566 int LFNIndex::get_attr_path(const vector
<string
> &path
,
567 const string
&attr_name
,
568 bufferlist
&attr_value
)
570 string full_path
= get_full_path_subdir(path
);
572 int r
= chain_getxattr_buf(
574 mangle_attr_name(attr_name
).c_str(),
577 attr_value
.push_back(bp
);
581 int LFNIndex::remove_attr_path(const vector
<string
> &path
,
582 const string
&attr_name
)
584 string full_path
= get_full_path_subdir(path
);
585 string mangled_attr_name
= mangle_attr_name(attr_name
);
586 maybe_inject_failure();
587 return chain_removexattr(full_path
.c_str(), mangled_attr_name
.c_str());
590 string
LFNIndex::lfn_generate_object_name_keyless(const ghobject_t
&oid
)
592 char s
[FILENAME_MAX_LEN
];
593 char *end
= s
+ sizeof(s
);
596 ceph_assert(oid
.generation
== ghobject_t::NO_GEN
);
597 const char *i
= oid
.hobj
.oid
.name
.c_str();
598 // Escape subdir prefix
599 if (oid
.hobj
.oid
.name
.substr(0, 4) == "DIR_") {
604 while (*i
&& t
< end
) {
608 } else if (*i
== '.' && i
== oid
.hobj
.oid
.name
.c_str()) { // only escape leading .
611 } else if (*i
== '/') {
619 if (oid
.hobj
.snap
== CEPH_NOSNAP
)
620 t
+= snprintf(t
, end
- t
, "_head");
621 else if (oid
.hobj
.snap
== CEPH_SNAPDIR
)
622 t
+= snprintf(t
, end
- t
, "_snapdir");
624 t
+= snprintf(t
, end
- t
, "_%llx", (long long unsigned)oid
.hobj
.snap
);
625 snprintf(t
, end
- t
, "_%.*X", (int)(sizeof(oid
.hobj
.get_hash())*2), oid
.hobj
.get_hash());
630 static void append_escaped(string::const_iterator begin
,
631 string::const_iterator end
,
634 for (string::const_iterator i
= begin
; i
!= end
; ++i
) {
637 } else if (*i
== '/') {
639 } else if (*i
== '_') {
641 } else if (*i
== '\0') {
649 string
LFNIndex::lfn_generate_object_name_current(const ghobject_t
&oid
)
652 string::const_iterator i
= oid
.hobj
.oid
.name
.begin();
653 if (oid
.hobj
.oid
.name
.substr(0, 4) == "DIR_") {
654 full_name
.append("\\d");
656 } else if (oid
.hobj
.oid
.name
[0] == '.') {
657 full_name
.append("\\.");
660 append_escaped(i
, oid
.hobj
.oid
.name
.end(), &full_name
);
661 full_name
.append("_");
662 append_escaped(oid
.hobj
.get_key().begin(), oid
.hobj
.get_key().end(), &full_name
);
663 full_name
.append("_");
667 const char *end
= t
+ sizeof(buf
);
668 if (oid
.hobj
.snap
== CEPH_NOSNAP
)
669 t
+= snprintf(t
, end
- t
, "head");
670 else if (oid
.hobj
.snap
== CEPH_SNAPDIR
)
671 t
+= snprintf(t
, end
- t
, "snapdir");
673 t
+= snprintf(t
, end
- t
, "%llx", (long long unsigned)oid
.hobj
.snap
);
674 t
+= snprintf(t
, end
- t
, "_%.*X", (int)(sizeof(oid
.hobj
.get_hash())*2), oid
.hobj
.get_hash());
675 full_name
.append(buf
, t
);
676 full_name
.append("_");
678 append_escaped(oid
.hobj
.nspace
.begin(), oid
.hobj
.nspace
.end(), &full_name
);
679 full_name
.append("_");
682 if (oid
.hobj
.pool
== -1)
683 t
+= snprintf(t
, end
- t
, "none");
685 t
+= snprintf(t
, end
- t
, "%llx", (long long unsigned)oid
.hobj
.pool
);
686 full_name
.append(buf
, t
);
688 if (oid
.generation
!= ghobject_t::NO_GEN
||
689 oid
.shard_id
!= shard_id_t::NO_SHARD
) {
690 full_name
.append("_");
693 t
+= snprintf(t
, end
- buf
, "%llx", (long long unsigned)oid
.generation
);
694 full_name
.append(buf
, t
);
696 full_name
.append("_");
699 t
+= snprintf(t
, end
- buf
, "%x", (int)oid
.shard_id
);
700 full_name
.append(buf
, t
);
706 string
LFNIndex::lfn_generate_object_name_poolless(const ghobject_t
&oid
)
708 if (index_version
== HASH_INDEX_TAG
)
709 return lfn_generate_object_name_keyless(oid
);
711 ceph_assert(oid
.generation
== ghobject_t::NO_GEN
);
713 string::const_iterator i
= oid
.hobj
.oid
.name
.begin();
714 if (oid
.hobj
.oid
.name
.substr(0, 4) == "DIR_") {
715 full_name
.append("\\d");
717 } else if (oid
.hobj
.oid
.name
[0] == '.') {
718 full_name
.append("\\.");
721 append_escaped(i
, oid
.hobj
.oid
.name
.end(), &full_name
);
722 full_name
.append("_");
723 append_escaped(oid
.hobj
.get_key().begin(), oid
.hobj
.get_key().end(), &full_name
);
724 full_name
.append("_");
726 char snap_with_hash
[PATH_MAX
];
727 char *t
= snap_with_hash
;
728 char *end
= t
+ sizeof(snap_with_hash
);
729 if (oid
.hobj
.snap
== CEPH_NOSNAP
)
730 t
+= snprintf(t
, end
- t
, "head");
731 else if (oid
.hobj
.snap
== CEPH_SNAPDIR
)
732 t
+= snprintf(t
, end
- t
, "snapdir");
734 t
+= snprintf(t
, end
- t
, "%llx", (long long unsigned)oid
.hobj
.snap
);
735 snprintf(t
, end
- t
, "_%.*X", (int)(sizeof(oid
.hobj
.get_hash())*2), oid
.hobj
.get_hash());
736 full_name
+= string(snap_with_hash
);
740 int LFNIndex::lfn_get_name(const vector
<string
> &path
,
741 const ghobject_t
&oid
,
742 string
*mangled_name
, string
*out_path
,
745 string full_name
= lfn_generate_object_name(oid
);
748 if (!lfn_must_hash(full_name
)) {
750 *mangled_name
= full_name
;
752 *out_path
= get_full_path(path
, full_name
);
755 string full_path
= get_full_path(path
, full_name
);
756 maybe_inject_failure();
757 r
= ::stat(full_path
.c_str(), &buf
);
764 *hardlink
= buf
.st_nlink
;
772 string candidate_path
;
774 candidate
= lfn_get_short_name(oid
, i
);
775 candidate_path
= get_full_path(path
, candidate
);
777 r
= chain_getxattr_buf(
778 candidate_path
.c_str(),
779 get_lfn_attr().c_str(),
782 if (errno
!= ENODATA
&& errno
!= ENOENT
)
784 if (errno
== ENODATA
) {
785 // Left over from incomplete transaction, it'll be replayed
786 maybe_inject_failure();
787 r
= ::unlink(candidate_path
.c_str());
788 maybe_inject_failure();
793 *mangled_name
= candidate
;
795 *out_path
= candidate_path
;
801 string
lfn(bp
.c_str(), bp
.length());
802 if (lfn
== full_name
) {
804 *mangled_name
= candidate
;
806 *out_path
= candidate_path
;
809 r
= ::stat(candidate_path
.c_str(), &st
);
816 *hardlink
= st
.st_nlink
;
822 r
= chain_getxattr_buf(
823 candidate_path
.c_str(),
824 get_alt_lfn_attr().c_str(),
827 // only consider alt name if nlink > 1
829 int rc
= ::stat(candidate_path
.c_str(), &st
);
832 if (st
.st_nlink
<= 1) {
833 // left over from incomplete unlink, remove
834 maybe_inject_failure();
835 dout(20) << __func__
<< " found extra alt attr for " << candidate_path
836 << ", long name " << string(bp
.c_str(), bp
.length()) << dendl
;
837 rc
= chain_removexattr(candidate_path
.c_str(),
838 get_alt_lfn_attr().c_str());
839 maybe_inject_failure();
844 string
lfn(bp
.c_str(), bp
.length());
845 if (lfn
== full_name
) {
846 dout(20) << __func__
<< " used alt attr for " << full_name
<< dendl
;
848 *mangled_name
= candidate
;
850 *out_path
= candidate_path
;
852 *hardlink
= st
.st_nlink
;
857 ceph_abort(); // Unreachable
861 int LFNIndex::lfn_created(const vector
<string
> &path
,
862 const ghobject_t
&oid
,
863 const string
&mangled_name
)
865 if (!lfn_is_hashed_filename(mangled_name
))
867 string full_path
= get_full_path(path
, mangled_name
);
868 string full_name
= lfn_generate_object_name(oid
);
869 maybe_inject_failure();
871 // if the main attr exists and is different, move it to the alt attr.
873 int r
= chain_getxattr_buf(
875 get_lfn_attr().c_str(),
878 string
lfn(bp
.c_str(), bp
.length());
879 if (lfn
!= full_name
) {
880 dout(20) << __func__
<< " " << mangled_name
881 << " moving old name to alt attr "
883 << ", new name is " << full_name
<< dendl
;
884 r
= chain_setxattr
<false, true>(
885 full_path
.c_str(), get_alt_lfn_attr().c_str(),
886 bp
.c_str(), bp
.length());
892 return chain_setxattr
<false, true>(
893 full_path
.c_str(), get_lfn_attr().c_str(),
894 full_name
.c_str(), full_name
.size());
897 int LFNIndex::lfn_unlink(const vector
<string
> &path
,
898 const ghobject_t
&oid
,
899 const string
&mangled_name
)
901 if (!lfn_is_hashed_filename(mangled_name
)) {
902 string full_path
= get_full_path(path
, mangled_name
);
903 maybe_inject_failure();
904 int r
= ::unlink(full_path
.c_str());
905 maybe_inject_failure();
913 string candidate
= lfn_get_short_name(oid
, i
);
914 if (candidate
== mangled_name
)
917 int removed_index
= i
;
921 string to_check
= lfn_get_short_name(oid
, i
);
922 string to_check_path
= get_full_path(path
, to_check
);
923 int r
= ::stat(to_check_path
.c_str(), &buf
);
925 if (errno
== ENOENT
) {
932 string full_path
= get_full_path(path
, mangled_name
);
933 int fd
= ::open(full_path
.c_str(), O_RDONLY
|O_CLOEXEC
);
937 if (i
== removed_index
+ 1) {
938 maybe_inject_failure();
939 int r
= ::unlink(full_path
.c_str());
940 maybe_inject_failure();
944 string
& rename_to
= full_path
;
945 string rename_from
= get_full_path(path
, lfn_get_short_name(oid
, i
- 1));
946 maybe_inject_failure();
947 int r
= ::rename(rename_from
.c_str(), rename_to
.c_str());
948 maybe_inject_failure();
953 int r
= ::fstat(fd
, &st
);
954 if (r
== 0 && st
.st_nlink
> 0) {
956 dout(20) << __func__
<< " removing alt attr from " << full_path
<< dendl
;
958 chain_fremovexattr(fd
, get_alt_lfn_attr().c_str());
963 int LFNIndex::lfn_translate(const vector
<string
> &path
,
964 const string
&short_name
,
967 if (!lfn_is_hashed_filename(short_name
)) {
968 return lfn_parse_object_name(short_name
, out
);
970 string full_path
= get_full_path(path
, short_name
);
971 // First, check alt attr
973 int r
= chain_getxattr_buf(
975 get_alt_lfn_attr().c_str(),
978 // There is an alt attr, does it match?
979 string
lfn(bp
.c_str(), bp
.length());
980 if (short_name_matches(short_name
.c_str(), lfn
.c_str())) {
981 return lfn_parse_object_name(lfn
, out
);
987 r
= chain_getxattr_buf(
989 get_lfn_attr().c_str(),
996 string
long_name(bp
.c_str(), bp
.length());
997 return lfn_parse_object_name(long_name
, out
);
1000 bool LFNIndex::lfn_is_object(const string
&short_name
)
1002 return lfn_is_hashed_filename(short_name
) || !lfn_is_subdir(short_name
, 0);
1005 bool LFNIndex::lfn_is_subdir(const string
&name
, string
*demangled
)
1007 if (name
.substr(0, SUBDIR_PREFIX
.size()) == SUBDIR_PREFIX
) {
1009 *demangled
= demangle_path_component(name
);
1015 static int parse_object(const char *s
, ghobject_t
& o
)
1017 const char *hash
= s
+ strlen(s
) - 1;
1018 while (*hash
!= '_' &&
1021 const char *bar
= hash
- 1;
1022 while (*bar
!= '_' &&
1026 char buf
[bar
-s
+ 1];
1033 case '\\': *t
++ = '\\'; break;
1034 case '.': *t
++ = '.'; break;
1035 case 's': *t
++ = '/'; break;
1043 default: ceph_abort();
1051 o
.hobj
.oid
.name
= string(buf
, t
-buf
);
1052 if (strncmp(bar
+1, "head", 4) == 0)
1053 o
.hobj
.snap
= CEPH_NOSNAP
;
1054 else if (strncmp(bar
+1, "snapdir", 7) == 0)
1055 o
.hobj
.snap
= CEPH_SNAPDIR
;
1057 o
.hobj
.snap
= strtoull(bar
+1, NULL
, 16);
1059 uint32_t hobject_hash_input
;
1060 sscanf(hash
, "_%X", &hobject_hash_input
);
1061 o
.hobj
.set_hash(hobject_hash_input
);
1068 int LFNIndex::lfn_parse_object_name_keyless(const string
&long_name
, ghobject_t
*out
)
1070 int r
= parse_object(long_name
.c_str(), *out
);
1073 if (coll().is_pg_prefix(&pg
))
1074 pool
= (int64_t)pg
.pgid
.pool();
1075 out
->hobj
.pool
= pool
;
1076 if (!r
) return -EINVAL
;
1077 string temp
= lfn_generate_object_name(*out
);
1081 static bool append_unescaped(string::const_iterator begin
,
1082 string::const_iterator end
,
1085 for (string::const_iterator i
= begin
; i
!= end
; ++i
) {
1099 out
->append(i
, i
+1);
1105 int LFNIndex::lfn_parse_object_name_poolless(const string
&long_name
,
1113 string::const_iterator current
= long_name
.begin();
1114 if (*current
== '\\') {
1116 if (current
== long_name
.end()) {
1118 } else if (*current
== 'd') {
1119 name
.append("DIR_");
1121 } else if (*current
== '.') {
1129 string::const_iterator end
= current
;
1130 for ( ; end
!= long_name
.end() && *end
!= '_'; ++end
) ;
1131 if (end
== long_name
.end())
1133 if (!append_unescaped(current
, end
, &name
))
1137 for ( ; end
!= long_name
.end() && *end
!= '_'; ++end
) ;
1138 if (end
== long_name
.end())
1140 if (!append_unescaped(current
, end
, &key
))
1144 for ( ; end
!= long_name
.end() && *end
!= '_'; ++end
) ;
1145 if (end
== long_name
.end())
1147 string
snap_str(current
, end
);
1150 for ( ; end
!= long_name
.end() && *end
!= '_'; ++end
) ;
1151 if (end
!= long_name
.end())
1153 string
hash_str(current
, end
);
1155 if (snap_str
== "head")
1157 else if (snap_str
== "snapdir")
1158 snap
= CEPH_SNAPDIR
;
1160 snap
= strtoull(snap_str
.c_str(), NULL
, 16);
1161 sscanf(hash_str
.c_str(), "%X", &hash
);
1166 if (coll().is_pg_prefix(&pg
))
1167 pool
= (int64_t)pg
.pgid
.pool();
1168 (*out
) = ghobject_t(hobject_t(name
, key
, snap
, hash
, pool
, ""));
1173 int LFNIndex::lfn_parse_object_name(const string
&long_name
, ghobject_t
*out
)
1181 gen_t generation
= ghobject_t::NO_GEN
;
1182 shard_id_t shard_id
= shard_id_t::NO_SHARD
;
1184 if (index_version
== HASH_INDEX_TAG
)
1185 return lfn_parse_object_name_keyless(long_name
, out
);
1186 if (index_version
== HASH_INDEX_TAG_2
)
1187 return lfn_parse_object_name_poolless(long_name
, out
);
1189 string::const_iterator current
= long_name
.begin();
1190 if (*current
== '\\') {
1192 if (current
== long_name
.end()) {
1194 } else if (*current
== 'd') {
1195 name
.append("DIR_");
1197 } else if (*current
== '.') {
1205 string::const_iterator end
= current
;
1206 for ( ; end
!= long_name
.end() && *end
!= '_'; ++end
) ;
1207 if (end
== long_name
.end())
1209 if (!append_unescaped(current
, end
, &name
))
1213 for ( ; end
!= long_name
.end() && *end
!= '_'; ++end
) ;
1214 if (end
== long_name
.end())
1216 if (!append_unescaped(current
, end
, &key
))
1220 for ( ; end
!= long_name
.end() && *end
!= '_'; ++end
) ;
1221 if (end
== long_name
.end())
1223 string
snap_str(current
, end
);
1226 for ( ; end
!= long_name
.end() && *end
!= '_'; ++end
) ;
1227 if (end
== long_name
.end())
1229 string
hash_str(current
, end
);
1232 for ( ; end
!= long_name
.end() && *end
!= '_'; ++end
) ;
1233 if (end
== long_name
.end())
1235 if (!append_unescaped(current
, end
, &ns
))
1239 for ( ; end
!= long_name
.end() && *end
!= '_'; ++end
) ;
1240 string
pstring(current
, end
);
1242 // Optional generation/shard_id
1243 string genstring
, shardstring
;
1244 if (end
!= long_name
.end()) {
1246 for ( ; end
!= long_name
.end() && *end
!= '_'; ++end
) ;
1247 if (end
== long_name
.end())
1249 genstring
= string(current
, end
);
1251 generation
= (gen_t
)strtoull(genstring
.c_str(), NULL
, 16);
1254 for ( ; end
!= long_name
.end() && *end
!= '_'; ++end
) ;
1255 if (end
!= long_name
.end())
1257 shardstring
= string(current
, end
);
1259 shard_id
= (shard_id_t
)strtoul(shardstring
.c_str(), NULL
, 16);
1262 if (snap_str
== "head")
1264 else if (snap_str
== "snapdir")
1265 snap
= CEPH_SNAPDIR
;
1267 snap
= strtoull(snap_str
.c_str(), NULL
, 16);
1268 sscanf(hash_str
.c_str(), "%X", &hash
);
1270 if (pstring
== "none")
1271 pool
= (uint64_t)-1;
1273 pool
= strtoull(pstring
.c_str(), NULL
, 16);
1275 (*out
) = ghobject_t(hobject_t(name
, key
, snap
, hash
, (int64_t)pool
, ns
), generation
, shard_id
);
1279 bool LFNIndex::lfn_is_hashed_filename(const string
&name
)
1281 if (name
.size() < (unsigned)FILENAME_SHORT_LEN
) {
1284 if (name
.substr(name
.size() - FILENAME_COOKIE
.size(), FILENAME_COOKIE
.size())
1285 == FILENAME_COOKIE
) {
1292 bool LFNIndex::lfn_must_hash(const string
&long_name
)
1294 return (int)long_name
.size() >= FILENAME_SHORT_LEN
;
1297 static inline void buf_to_hex(const unsigned char *buf
, int len
, char *str
)
1301 for (i
= 0; i
< len
; i
++) {
1302 sprintf(&str
[i
*2], "%02x", (int)buf
[i
]);
1306 int LFNIndex::hash_filename(const char *filename
, char *hash
, int buf_len
)
1308 if (buf_len
< FILENAME_HASH_LEN
+ 1)
1311 char buf
[FILENAME_LFN_DIGEST_SIZE
];
1312 char hex
[FILENAME_LFN_DIGEST_SIZE
* 2];
1315 h
.Update((const unsigned char *)filename
, strlen(filename
));
1316 h
.Final((unsigned char *)buf
);
1318 buf_to_hex((unsigned char *)buf
, (FILENAME_HASH_LEN
+ 1) / 2, hex
);
1319 strncpy(hash
, hex
, FILENAME_HASH_LEN
);
1320 hash
[FILENAME_HASH_LEN
] = '\0';
1324 void LFNIndex::build_filename(const char *old_filename
, int i
, char *filename
, int len
)
1326 char hash
[FILENAME_HASH_LEN
+ 1];
1328 ceph_assert(len
>= FILENAME_SHORT_LEN
+ 4);
1330 strncpy(filename
, old_filename
, FILENAME_PREFIX_LEN
);
1331 filename
[FILENAME_PREFIX_LEN
] = '\0';
1332 if ((int)strlen(filename
) < FILENAME_PREFIX_LEN
)
1334 if (old_filename
[FILENAME_PREFIX_LEN
] == '\0')
1337 hash_filename(old_filename
, hash
, sizeof(hash
));
1338 int ofs
= FILENAME_PREFIX_LEN
;
1340 int suffix_len
= sprintf(filename
+ ofs
, "_%s_%d_%s", hash
, i
, FILENAME_COOKIE
.c_str());
1341 if (ofs
+ suffix_len
<= FILENAME_SHORT_LEN
|| !ofs
)
1347 bool LFNIndex::short_name_matches(const char *short_name
, const char *cand_long_name
)
1349 const char *end
= short_name
;
1351 const char *suffix
= end
;
1352 if (suffix
> short_name
) --suffix
; // last char
1353 while (suffix
> short_name
&& *suffix
!= '_') --suffix
; // back to first _
1354 if (suffix
> short_name
) --suffix
; // one behind that
1355 while (suffix
> short_name
&& *suffix
!= '_') --suffix
; // back to second _
1358 char buf
[FILENAME_SHORT_LEN
+ 4];
1359 ceph_assert((end
- suffix
) < (int)sizeof(buf
));
1360 int r
= sscanf(suffix
, "_%d_%s", &index
, buf
);
1363 if (strcmp(buf
, FILENAME_COOKIE
.c_str()) != 0)
1365 build_filename(cand_long_name
, index
, buf
, sizeof(buf
));
1366 return strcmp(short_name
, buf
) == 0;
1369 string
LFNIndex::lfn_get_short_name(const ghobject_t
&oid
, int i
)
1371 string long_name
= lfn_generate_object_name(oid
);
1372 ceph_assert(lfn_must_hash(long_name
));
1373 char buf
[FILENAME_SHORT_LEN
+ 4];
1374 build_filename(long_name
.c_str(), i
, buf
, sizeof(buf
));
1378 const string
&LFNIndex::get_base_path()
1383 string
LFNIndex::get_full_path_subdir(const vector
<string
> &rel
)
1385 string retval
= get_base_path();
1386 for (vector
<string
>::const_iterator i
= rel
.begin();
1390 retval
+= mangle_path_component(*i
);
1395 string
LFNIndex::get_full_path(const vector
<string
> &rel
, const string
&name
)
1397 return get_full_path_subdir(rel
) + "/" + name
;
1400 string
LFNIndex::mangle_path_component(const string
&component
)
1402 return SUBDIR_PREFIX
+ component
;
1405 string
LFNIndex::demangle_path_component(const string
&component
)
1407 return component
.substr(SUBDIR_PREFIX
.size(), component
.size() - SUBDIR_PREFIX
.size());
1410 int LFNIndex::decompose_full_path(const char *in
, vector
<string
> *out
,
1411 ghobject_t
*oid
, string
*shortname
)
1413 const char *beginning
= in
+ get_base_path().size();
1414 const char *end
= beginning
;
1418 for ( ; *end
!= '\0' && *end
!= '/'; ++end
) ;
1420 out
->push_back(demangle_path_component(string(beginning
, end
- beginning
)));
1426 *shortname
= string(beginning
, end
- beginning
);
1428 int r
= lfn_translate(*out
, *shortname
, oid
);
1435 string
LFNIndex::mangle_attr_name(const string
&attr
)
1437 return PHASH_ATTR_PREFIX
+ attr
;