Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / test / os / TestLFNIndex.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4  * Ceph - scalable distributed file system
5  *
6  * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
7  *
8  * Author: Loic Dachary <loic@dachary.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU Library Public License as published by
12  * the Free Software Foundation; either version 2, or (at your option)
13  * any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Library Public License for more details.
19  *
20  */
21
22 #include <stdio.h>
23 #include <signal.h>
24 #include "os/filestore/LFNIndex.h"
25 #include "os/filestore/chain_xattr.h"
26 #include "common/ceph_argparse.h"
27 #include "global/global_init.h"
28 #include <gtest/gtest.h>
29
30 class TestWrapLFNIndex : public LFNIndex {
31 public:
32   TestWrapLFNIndex(CephContext* cct,
33                    coll_t collection,
34                    const char *base_path,
35                    uint32_t index_version)
36     : LFNIndex(cct, collection, base_path, index_version) {}
37
38   uint32_t collection_version() override {
39     return index_version;
40   }
41
42   int cleanup() override { return 0; }
43
44   int _split(
45                      uint32_t match,                           
46                      uint32_t bits,                            
47                      CollectionIndex* dest
48                      ) override { return 0; }
49
50   void test_generate_and_parse(const ghobject_t &hoid, const std::string &mangled_expected) {
51     const std::string mangled_name = lfn_generate_object_name(hoid);
52     EXPECT_EQ(mangled_expected, mangled_name);
53     ghobject_t hoid_parsed;
54     EXPECT_EQ(0, lfn_parse_object_name(mangled_name, &hoid_parsed));
55     EXPECT_EQ(hoid, hoid_parsed);
56   }
57
58 protected:
59   int _init() override { return 0; }
60
61   int _created(
62                        const vector<string> &path,
63                        const ghobject_t &hoid,
64                        const string &mangled_name 
65                        ) override { return 0; }
66
67   int _remove(
68                       const vector<string> &path,
69                       const ghobject_t &hoid,
70                       const string &mangled_name
71                       ) override { return 0; }
72
73   int _lookup(
74                       const ghobject_t &hoid,
75                       vector<string> *path,
76                       string *mangled_name,
77                       int *exists                
78                       ) override { return 0; }
79
80   int _collection_list_partial(
81                                        const ghobject_t &start,
82                                        const ghobject_t &end,
83                                        int max_count,
84                                        vector<ghobject_t> *ls,
85                                        ghobject_t *next
86                                        ) override { return 0; }
87   int _pre_hash_collection(
88                                    uint32_t pg_num,
89                                    uint64_t expected_num_objs
90                                   ) override { return 0; }
91
92 };
93
94 class TestHASH_INDEX_TAG : public TestWrapLFNIndex, public ::testing::Test {
95 public:
96   TestHASH_INDEX_TAG()
97     : TestWrapLFNIndex(g_ceph_context, coll_t(), "PATH_1",
98                        CollectionIndex::HASH_INDEX_TAG) {
99   }
100 };
101
102 TEST_F(TestHASH_INDEX_TAG, generate_and_parse_name) {
103   const vector<string> path;
104   const std::string key;
105   uint64_t hash = 0xABABABAB;
106   uint64_t pool = -1;
107
108   test_generate_and_parse(ghobject_t(hobject_t(object_t(".A/B_\\C.D"), key, CEPH_NOSNAP, hash, pool, "")),
109                           "\\.A\\sB_\\\\C.D_head_ABABABAB");
110   test_generate_and_parse(ghobject_t(hobject_t(object_t("DIR_A"), key, CEPH_NOSNAP, hash, pool, "")),
111                           "\\dA_head_ABABABAB");
112 }
113
114 class TestHASH_INDEX_TAG_2 : public TestWrapLFNIndex, public ::testing::Test {
115 public:
116   TestHASH_INDEX_TAG_2()
117     : TestWrapLFNIndex(g_ceph_context,
118                        coll_t(), "PATH_1", CollectionIndex::HASH_INDEX_TAG_2) {
119   }
120 };
121
122 TEST_F(TestHASH_INDEX_TAG_2, generate_and_parse_name) {
123   const vector<string> path;
124   const std::string key("KEY");
125   uint64_t hash = 0xABABABAB;
126   uint64_t pool = -1;
127
128   {
129     std::string name(".XA/B_\\C.D");
130     name[1] = '\0';
131     ghobject_t hoid(hobject_t(object_t(name), key, CEPH_NOSNAP, hash, pool, ""));
132
133     test_generate_and_parse(hoid, "\\.\\nA\\sB\\u\\\\C.D_KEY_head_ABABABAB");
134   }
135   test_generate_and_parse(ghobject_t(hobject_t(object_t("DIR_A"), key, CEPH_NOSNAP, hash, pool, "")),
136                           "\\dA_KEY_head_ABABABAB");
137 }
138
139 class TestHOBJECT_WITH_POOL : public TestWrapLFNIndex, public ::testing::Test {
140 public:
141   TestHOBJECT_WITH_POOL()
142     : TestWrapLFNIndex(g_ceph_context, coll_t(),
143                        "PATH_1", CollectionIndex::HOBJECT_WITH_POOL) {
144   }
145 };
146
147 TEST_F(TestHOBJECT_WITH_POOL, generate_and_parse_name) {
148   const vector<string> path;
149   const std::string key("KEY");
150   uint64_t hash = 0xABABABAB;
151   uint64_t pool = 0xCDCDCDCD;
152   int64_t gen = 0xefefefefef;
153   shard_id_t shard_id(0xb);
154
155   {
156     std::string name(".XA/B_\\C.D");
157     name[1] = '\0';
158     ghobject_t hoid(hobject_t(object_t(name), key, CEPH_NOSNAP, hash, pool, ""));
159     hoid.hobj.nspace = "NSPACE";
160
161     test_generate_and_parse(hoid, "\\.\\nA\\sB\\u\\\\C.D_KEY_head_ABABABAB_NSPACE_cdcdcdcd");
162   }
163   {
164     ghobject_t hoid(hobject_t(object_t("DIR_A"), key, CEPH_NOSNAP, hash, pool, ""));
165     hoid.hobj.nspace = "NSPACE";
166
167     test_generate_and_parse(hoid, "\\dA_KEY_head_ABABABAB_NSPACE_cdcdcdcd");
168   }
169   {
170     std::string name(".XA/B_\\C.D");
171     name[1] = '\0';
172     ghobject_t hoid(hobject_t(object_t(name), key, CEPH_NOSNAP, hash, pool, ""), gen, shard_id);
173     hoid.hobj.nspace = "NSPACE";
174
175     test_generate_and_parse(hoid, "\\.\\nA\\sB\\u\\\\C.D_KEY_head_ABABABAB_NSPACE_cdcdcdcd_efefefefef_b");
176   }
177   {
178     ghobject_t hoid(hobject_t(object_t("DIR_A"), key, CEPH_NOSNAP, hash, pool, ""), gen, shard_id);
179     hoid.hobj.nspace = "NSPACE";
180
181     test_generate_and_parse(hoid, "\\dA_KEY_head_ABABABAB_NSPACE_cdcdcdcd_efefefefef_b");
182   }
183 }
184
185 class TestLFNIndex : public TestWrapLFNIndex, public ::testing::Test {
186 public:
187   TestLFNIndex()
188     : TestWrapLFNIndex(g_ceph_context, coll_t(), "PATH_1",
189                        CollectionIndex::HOBJECT_WITH_POOL) {
190   }
191
192   void SetUp() override {
193     ::chmod("PATH_1", 0700);
194     ASSERT_EQ(0, ::system("rm -fr PATH_1"));
195     ASSERT_EQ(0, ::mkdir("PATH_1", 0700));
196   }
197
198   void TearDown() override {
199     ASSERT_EQ(0, ::system("rm -fr PATH_1"));
200   }
201 };
202
203 TEST_F(TestLFNIndex, remove_object) {
204   const vector<string> path;
205
206   //
207   // small object name removal
208   //
209   {
210     std::string mangled_name;
211     int exists = 666;
212     ghobject_t hoid(hobject_t(sobject_t("ABC", CEPH_NOSNAP)));
213
214     EXPECT_EQ(0, ::chmod("PATH_1", 0000));
215     if (getuid() != 0) {
216       EXPECT_EQ(-EACCES, remove_object(path, hoid));
217     }
218     EXPECT_EQ(0, ::chmod("PATH_1", 0700));
219     EXPECT_EQ(-ENOENT, remove_object(path, hoid));
220     EXPECT_EQ(0, get_mangled_name(path, hoid, &mangled_name, &exists));
221     const std::string pathname("PATH_1/" + mangled_name);
222     EXPECT_EQ(0, ::close(::creat(pathname.c_str(), 0600)));
223     EXPECT_EQ(0, remove_object(path, hoid));
224     EXPECT_EQ(-1, ::access(pathname.c_str(), 0));
225     EXPECT_EQ(ENOENT, errno);
226   }
227   //
228   // long object name removal of a single file
229   //
230   {
231     std::string mangled_name;
232     int exists;
233     const std::string object_name(1024, 'A');
234     ghobject_t hoid(hobject_t(sobject_t(object_name, CEPH_NOSNAP)));
235
236     EXPECT_EQ(0, get_mangled_name(path, hoid, &mangled_name, &exists));
237     EXPECT_EQ(0, exists);
238     EXPECT_NE(std::string::npos, mangled_name.find("0_long"));
239     std::string pathname("PATH_1/" + mangled_name);
240     EXPECT_EQ(0, ::close(::creat(pathname.c_str(), 0600)));
241     EXPECT_EQ(0, created(hoid, pathname.c_str()));
242
243     EXPECT_EQ(0, remove_object(path, hoid));
244     EXPECT_EQ(-1, ::access(pathname.c_str(), 0));
245     EXPECT_EQ(ENOENT, errno);
246   }
247
248   //
249   // long object name removal of the last file 
250   //
251   {
252     std::string mangled_name;
253     int exists;
254     const std::string object_name(1024, 'A');
255     ghobject_t hoid(hobject_t(sobject_t(object_name, CEPH_NOSNAP)));
256
257     //
258     //   PATH_1/AAA..._0_long => does not match long object name
259     //
260     EXPECT_EQ(0, get_mangled_name(path, hoid, &mangled_name, &exists));
261     EXPECT_EQ(0, exists);
262     EXPECT_NE(std::string::npos, mangled_name.find("0_long"));
263     std::string pathname("PATH_1/" + mangled_name);
264     EXPECT_EQ(0, ::close(::creat(pathname.c_str(), 0600)));
265     EXPECT_EQ(0, created(hoid, pathname.c_str()));
266     string LFN_ATTR = "user.cephos.lfn";
267     if (index_version != HASH_INDEX_TAG) {
268       char buf[100];
269       snprintf(buf, sizeof(buf), "%d", index_version);
270       LFN_ATTR += string(buf);
271     }
272     const std::string object_name_1 = object_name + "SUFFIX";
273     EXPECT_EQ(object_name_1.size(), (unsigned)chain_setxattr(pathname.c_str(), LFN_ATTR.c_str(), object_name_1.c_str(), object_name_1.size()));
274
275     //
276     //   PATH_1/AAA..._1_long => matches long object name
277     //
278     std::string mangled_name_1;
279     exists = 666;
280     EXPECT_EQ(0, get_mangled_name(path, hoid, &mangled_name_1, &exists));
281     EXPECT_NE(std::string::npos, mangled_name_1.find("1_long"));
282     EXPECT_EQ(0, exists);
283     std::string pathname_1("PATH_1/" + mangled_name_1);
284     EXPECT_EQ(0, ::close(::creat(pathname_1.c_str(), 0600)));
285     EXPECT_EQ(0, created(hoid, pathname_1.c_str()));
286
287     //
288     // remove_object skips PATH_1/AAA..._0_long and removes PATH_1/AAA..._1_long
289     //
290     EXPECT_EQ(0, remove_object(path, hoid));
291     EXPECT_EQ(0, ::access(pathname.c_str(), 0));
292     EXPECT_EQ(-1, ::access(pathname_1.c_str(), 0));
293     EXPECT_EQ(ENOENT, errno);
294     EXPECT_EQ(0, ::unlink(pathname.c_str()));
295   }
296
297   //
298   // long object name removal of a file in the middle of the list
299   //
300   {
301     std::string mangled_name;
302     int exists;
303     const std::string object_name(1024, 'A');
304     ghobject_t hoid(hobject_t(sobject_t(object_name, CEPH_NOSNAP)));
305
306     //
307     //   PATH_1/AAA..._0_long => matches long object name
308     //
309     EXPECT_EQ(0, get_mangled_name(path, hoid, &mangled_name, &exists));
310     EXPECT_EQ(0, exists);
311     EXPECT_NE(std::string::npos, mangled_name.find("0_long"));
312     std::string pathname("PATH_1/" + mangled_name);
313     EXPECT_EQ(0, ::close(::creat(pathname.c_str(), 0600)));
314     EXPECT_EQ(0, created(hoid, pathname.c_str()));
315     //
316     //   PATH_1/AAA..._1_long => matches long object name
317     //
318     std::string mangled_name_1 = mangled_name;
319     mangled_name_1.replace(mangled_name_1.find("0_long"), 6, "1_long");
320     const std::string pathname_1("PATH_1/" + mangled_name_1);
321     const std::string cmd("cp -a " + pathname + " " + pathname_1);
322     EXPECT_EQ(0, ::system(cmd.c_str()));
323     const string ATTR = "user.MARK";
324     EXPECT_EQ((unsigned)1, (unsigned)chain_setxattr(pathname_1.c_str(), ATTR.c_str(), "Y", 1));
325
326     //
327     // remove_object replaces the file to be removed with the last from the
328     // collision list. In this case it replaces
329     //    PATH_1/AAA..._0_long
330     // with
331     //    PATH_1/AAA..._1_long
332     //
333     EXPECT_EQ(0, remove_object(path, hoid));
334     EXPECT_EQ(0, ::access(pathname.c_str(), 0));
335     char buffer[1] = { 0, };
336     EXPECT_EQ((unsigned)1, (unsigned)chain_getxattr(pathname.c_str(), ATTR.c_str(), buffer, 1));
337     EXPECT_EQ('Y', buffer[0]);
338     EXPECT_EQ(-1, ::access(pathname_1.c_str(), 0));
339     EXPECT_EQ(ENOENT, errno);
340   }
341 }
342
343 TEST_F(TestLFNIndex, get_mangled_name) {
344   const vector<string> path;
345
346   //
347   // small object name
348   //
349   {
350     std::string mangled_name;
351     int exists = 666;
352     ghobject_t hoid(hobject_t(sobject_t("ABC", CEPH_NOSNAP)));
353
354     EXPECT_EQ(0, get_mangled_name(path, hoid, &mangled_name, &exists));
355     EXPECT_NE(std::string::npos, mangled_name.find("ABC__head"));
356     EXPECT_EQ(std::string::npos, mangled_name.find("0_long"));
357     EXPECT_EQ(0, exists);
358     const std::string pathname("PATH_1/" + mangled_name);
359     EXPECT_EQ(0, ::close(::creat(pathname.c_str(), 0600)));
360     EXPECT_EQ(0, get_mangled_name(path, hoid, &mangled_name, &exists));
361     EXPECT_NE(std::string::npos, mangled_name.find("ABC__head"));
362     EXPECT_EQ(1, exists);
363     EXPECT_EQ(0, ::unlink(pathname.c_str()));
364   }
365   //
366   // long object name
367   //
368   {
369     std::string mangled_name;
370     int exists;
371     const std::string object_name(1024, 'A');
372     ghobject_t hoid(hobject_t(sobject_t(object_name, CEPH_NOSNAP)));
373
374     //
375     // long version of the mangled name and no matching
376     // file exists 
377     //
378     mangled_name.clear();
379     exists = 666;
380     EXPECT_EQ(0, get_mangled_name(path, hoid, &mangled_name, &exists));
381     EXPECT_NE(std::string::npos, mangled_name.find("0_long"));
382     EXPECT_EQ(0, exists);
383
384     const std::string pathname("PATH_1/" + mangled_name);
385
386     //
387     // if a file by the same name exists but does not have the
388     // expected extended attribute, it is silently removed 
389     //
390     mangled_name.clear();
391     exists = 666;
392     EXPECT_EQ(0, ::close(::creat(pathname.c_str(), 0600)));
393     EXPECT_EQ(0, get_mangled_name(path, hoid, &mangled_name, &exists));
394     EXPECT_NE(std::string::npos, mangled_name.find("0_long"));
395     EXPECT_EQ(0, exists);
396     EXPECT_EQ(-1, ::access(pathname.c_str(), 0));
397     EXPECT_EQ(ENOENT, errno);
398
399     //
400     // if a file by the same name exists but does not have the
401     // expected extended attribute, and cannot be removed, 
402     // return on error
403     //
404     mangled_name.clear();
405     exists = 666;
406     EXPECT_EQ(0, ::close(::creat(pathname.c_str(), 0600)));
407     EXPECT_EQ(0, ::chmod("PATH_1", 0500));
408     if (getuid() != 0) {
409       EXPECT_EQ(-EACCES, get_mangled_name(path, hoid, &mangled_name, &exists));
410     }
411     EXPECT_EQ("", mangled_name);
412     EXPECT_EQ(666, exists);
413     EXPECT_EQ(0, ::chmod("PATH_1", 0700));
414     EXPECT_EQ(0, ::unlink(pathname.c_str()));
415
416     //
417     // long version of the mangled name and a file
418     // exists by that name and contains the long object name
419     //
420     mangled_name.clear();
421     exists = 666;
422     EXPECT_EQ(0, ::close(::creat(pathname.c_str(), 0600)));
423     EXPECT_EQ(0, created(hoid, pathname.c_str()));
424     EXPECT_EQ(0, get_mangled_name(path, hoid, &mangled_name, &exists));
425     EXPECT_NE(std::string::npos, mangled_name.find("0_long"));
426     EXPECT_EQ(1, exists);
427     EXPECT_EQ(0, ::access(pathname.c_str(), 0));
428
429     //
430     // long version of the mangled name and a file exists by that name
431     // and contains a long object name with the same prefix but they
432     // are not identical and it so happens that their SHA1 is
433     // identical : a collision number is used to differentiate them
434     //
435     string LFN_ATTR = "user.cephos.lfn";
436     if (index_version != HASH_INDEX_TAG) {
437       char buf[100];
438       snprintf(buf, sizeof(buf), "%d", index_version);
439       LFN_ATTR += string(buf);
440     }
441     const std::string object_name_same_prefix = object_name + "SUFFIX";
442     EXPECT_EQ(object_name_same_prefix.size(), (unsigned)chain_setxattr(pathname.c_str(), LFN_ATTR.c_str(), object_name_same_prefix.c_str(), object_name_same_prefix.size()));
443     std::string mangled_name_same_prefix;
444     exists = 666;
445     EXPECT_EQ(0, get_mangled_name(path, hoid, &mangled_name_same_prefix, &exists));
446     EXPECT_NE(std::string::npos, mangled_name_same_prefix.find("1_long"));
447     EXPECT_EQ(0, exists);
448     
449     EXPECT_EQ(0, ::unlink(pathname.c_str()));
450   }
451 }
452
453 int main(int argc, char **argv) {
454   int fd = ::creat("detect", 0600);
455   if (fd < 0){
456     cerr << "failed to create file detect" << std::endl;
457     return EXIT_FAILURE;
458   }
459   int ret = chain_fsetxattr(fd, "user.test", "A", 1);
460   ::close(fd);
461   ::unlink("detect");
462   if (ret < 0) {
463     cerr << "SKIP LFNIndex because unable to test for xattr" << std::endl;
464   } else {
465     vector<const char*> args;
466     argv_to_vec(argc, (const char **)argv, args);
467
468     auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
469                            CODE_ENVIRONMENT_UTILITY, 0);
470     common_init_finish(g_ceph_context);
471
472     ::testing::InitGoogleTest(&argc, argv);
473     return RUN_ALL_TESTS();
474   }
475 }
476
477 /* 
478  * Local Variables:
479  * compile-command: "cd ../.. ; 
480  *   make unittest_lfnindex && 
481  *   valgrind --tool=memcheck ./unittest_lfnindex \
482  *   # --gtest_filter=TestLFNIndex.* --log-to-stderr=true --debug-filestore=20"
483  * End:
484  */