Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / test / objectstore / chain_xattr.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/chain_xattr.h"
25 #include "include/Context.h"
26 #include "include/coredumpctl.h"
27 #include "common/errno.h"
28 #include "common/ceph_argparse.h"
29 #include "global/global_init.h"
30 #include <gtest/gtest.h>
31
32 #define LARGE_BLOCK_LEN CHAIN_XATTR_MAX_BLOCK_LEN + 1024
33 #define FILENAME "chain_xattr"
34
35 TEST(chain_xattr, get_and_set) {
36   const char* file = FILENAME;
37   ::unlink(file);
38   int fd = ::open(file, O_CREAT|O_WRONLY|O_TRUNC, 0700);
39   const string user("user.");
40   
41   {
42     const string name = user + string(CHAIN_XATTR_MAX_NAME_LEN - user.size(), '@');
43     const string x(LARGE_BLOCK_LEN, 'X');
44
45     {
46       char y[LARGE_BLOCK_LEN];
47       ASSERT_EQ(LARGE_BLOCK_LEN, chain_setxattr(file, name.c_str(), x.c_str(), LARGE_BLOCK_LEN));
48       ASSERT_EQ(LARGE_BLOCK_LEN, chain_getxattr(file, name.c_str(), 0, 0));
49       ASSERT_EQ(LARGE_BLOCK_LEN, chain_getxattr(file, name.c_str(), y, LARGE_BLOCK_LEN));
50       ASSERT_EQ(0, chain_removexattr(file, name.c_str()));
51       ASSERT_EQ(0, memcmp(x.c_str(), y, LARGE_BLOCK_LEN));
52     }
53
54     {
55       char y[LARGE_BLOCK_LEN];
56       ASSERT_EQ(LARGE_BLOCK_LEN, chain_fsetxattr(fd, name.c_str(), x.c_str(), LARGE_BLOCK_LEN));
57       ASSERT_EQ(LARGE_BLOCK_LEN, chain_fgetxattr(fd, name.c_str(), 0, 0));
58       ASSERT_EQ(LARGE_BLOCK_LEN, chain_fgetxattr(fd, name.c_str(), y, LARGE_BLOCK_LEN));
59       ASSERT_EQ(0, chain_fremovexattr(fd, name.c_str()));
60       ASSERT_EQ(0, memcmp(x.c_str(), y, LARGE_BLOCK_LEN));
61     }
62   }
63
64   //
65   // when chain_setxattr is used to store value that is
66   // CHAIN_XATTR_MAX_BLOCK_LEN * 2 + 10 bytes long it 
67   //
68   // add user.foo => CHAIN_XATTR_MAX_BLOCK_LEN bytes
69   // add user.foo@1 => CHAIN_XATTR_MAX_BLOCK_LEN bytes
70   // add user.foo@2 => 10 bytes
71   //
72   // then ( no chain_removexattr in between ) when it is used to
73   // override with a value that is exactly CHAIN_XATTR_MAX_BLOCK_LEN
74   // bytes long it will
75   //
76   // replace user.foo => CHAIN_XATTR_MAX_BLOCK_LEN bytes
77   // remove user.foo@1 => CHAIN_XATTR_MAX_BLOCK_LEN bytes
78   // leak user.foo@2 => 10 bytes
79   // 
80   // see http://marc.info/?l=ceph-devel&m=136027076615853&w=4 for the 
81   // discussion
82   //
83   {
84     const string name = user + string(CHAIN_XATTR_MAX_NAME_LEN - user.size(), '@');
85     const string x(LARGE_BLOCK_LEN, 'X');
86
87     {
88       char y[CHAIN_XATTR_MAX_BLOCK_LEN];
89       ASSERT_EQ(LARGE_BLOCK_LEN, chain_setxattr(file, name.c_str(), x.c_str(), LARGE_BLOCK_LEN));
90       ASSERT_EQ(CHAIN_XATTR_MAX_BLOCK_LEN, chain_setxattr(file, name.c_str(), x.c_str(), CHAIN_XATTR_MAX_BLOCK_LEN));
91       ASSERT_EQ(CHAIN_XATTR_MAX_BLOCK_LEN, chain_getxattr(file, name.c_str(), 0, 0));
92       ASSERT_EQ(CHAIN_XATTR_MAX_BLOCK_LEN, chain_getxattr(file, name.c_str(), y, CHAIN_XATTR_MAX_BLOCK_LEN));
93       ASSERT_EQ(0, chain_removexattr(file, name.c_str()));
94       ASSERT_EQ(0, memcmp(x.c_str(), y, CHAIN_XATTR_MAX_BLOCK_LEN));
95     }
96
97     {
98       char y[CHAIN_XATTR_MAX_BLOCK_LEN];
99       ASSERT_EQ(LARGE_BLOCK_LEN, chain_fsetxattr(fd, name.c_str(), x.c_str(), LARGE_BLOCK_LEN));
100       ASSERT_EQ(CHAIN_XATTR_MAX_BLOCK_LEN, chain_fsetxattr(fd, name.c_str(), x.c_str(), CHAIN_XATTR_MAX_BLOCK_LEN));
101       ASSERT_EQ(CHAIN_XATTR_MAX_BLOCK_LEN, chain_fgetxattr(fd, name.c_str(), 0, 0));
102       ASSERT_EQ(CHAIN_XATTR_MAX_BLOCK_LEN, chain_fgetxattr(fd, name.c_str(), y, CHAIN_XATTR_MAX_BLOCK_LEN));
103       ASSERT_EQ(0, chain_fremovexattr(fd, name.c_str()));
104       ASSERT_EQ(0, memcmp(x.c_str(), y, CHAIN_XATTR_MAX_BLOCK_LEN));
105     }
106   }
107
108   {
109     int x = 0;
110     ASSERT_EQ(-ENOENT, chain_setxattr("UNLIKELY_TO_EXIST", "NAME", &x, sizeof(x)));
111     ASSERT_EQ(-ENOENT, chain_getxattr("UNLIKELY_TO_EXIST", "NAME", 0, 0));
112     ASSERT_EQ(-ENOENT, chain_getxattr("UNLIKELY_TO_EXIST", "NAME", &x, sizeof(x)));
113     ASSERT_EQ(-ENOENT, chain_removexattr("UNLIKELY_TO_EXIST", "NAME"));
114     int unlikely_to_be_a_valid_fd = 400;
115     ASSERT_EQ(-EBADF, chain_fsetxattr(unlikely_to_be_a_valid_fd, "NAME", &x, sizeof(x)));
116     ASSERT_EQ(-EBADF, chain_fgetxattr(unlikely_to_be_a_valid_fd, "NAME", 0, 0));
117     ASSERT_EQ(-EBADF, chain_fgetxattr(unlikely_to_be_a_valid_fd, "NAME", &x, sizeof(x)));
118     ASSERT_EQ(-EBADF, chain_fremovexattr(unlikely_to_be_a_valid_fd, "NAME"));
119   }
120
121   {
122     int x;
123     const string name = user + string(CHAIN_XATTR_MAX_NAME_LEN * 2, '@');
124     PrCtl unset_dumpable;
125     ASSERT_DEATH(chain_setxattr(file, name.c_str(), &x, sizeof(x)), "");
126     ASSERT_DEATH(chain_fsetxattr(fd, name.c_str(), &x, sizeof(x)), "");
127   }
128
129   {
130     const string name = user + string(CHAIN_XATTR_MAX_NAME_LEN - user.size(), '@');
131     const string x(LARGE_BLOCK_LEN, 'X');
132     {
133       char y[LARGE_BLOCK_LEN];
134       ASSERT_EQ(LARGE_BLOCK_LEN, chain_setxattr(file, name.c_str(), x.c_str(), LARGE_BLOCK_LEN));
135       ASSERT_EQ(-ERANGE, chain_getxattr(file, name.c_str(), y, LARGE_BLOCK_LEN - 1));
136       ASSERT_EQ(-ERANGE, chain_getxattr(file, name.c_str(), y, CHAIN_XATTR_MAX_BLOCK_LEN));
137       ASSERT_EQ(0, chain_removexattr(file, name.c_str()));
138     }
139
140     {
141       char y[LARGE_BLOCK_LEN];
142       ASSERT_EQ(LARGE_BLOCK_LEN, chain_fsetxattr(fd, name.c_str(), x.c_str(), LARGE_BLOCK_LEN));
143       ASSERT_EQ(-ERANGE, chain_fgetxattr(fd, name.c_str(), y, LARGE_BLOCK_LEN - 1));
144       ASSERT_EQ(-ERANGE, chain_fgetxattr(fd, name.c_str(), y, CHAIN_XATTR_MAX_BLOCK_LEN));
145       ASSERT_EQ(0, chain_fremovexattr(fd, name.c_str()));
146     }
147   }
148
149   ::close(fd);
150   ::unlink(file);
151 }
152
153 TEST(chain_xattr, chunk_aligned) {
154   const char* file = FILENAME;
155   ::unlink(file);
156   int fd = ::open(file, O_CREAT|O_WRONLY|O_TRUNC, 0700);
157   const string user("user.");
158
159   // set N* chunk size
160   const string name = "user.foo";
161   const string name2 = "user.bar";
162
163   for (int len = CHAIN_XATTR_MAX_BLOCK_LEN - 10;
164        len < CHAIN_XATTR_MAX_BLOCK_LEN + 10;
165        ++len) {
166     cout << len << std::endl;
167     const string x(len, 'x');
168     char buf[len*2];
169     ASSERT_EQ(len, chain_setxattr(file, name.c_str(), x.c_str(), len));
170     char attrbuf[4096];
171     int l = ceph_os_listxattr(file, attrbuf, sizeof(attrbuf));
172     for (char *p = attrbuf; p - attrbuf < l; p += strlen(p) + 1) {
173       cout << "  attr " << p << std::endl;
174     }
175     ASSERT_EQ(len, chain_getxattr(file, name.c_str(), buf, len*2));
176     ASSERT_EQ(0, chain_removexattr(file, name.c_str()));
177
178     ASSERT_EQ(len, chain_fsetxattr(fd, name2.c_str(), x.c_str(), len));
179     l = ceph_os_flistxattr(fd, attrbuf, sizeof(attrbuf));
180     for (char *p = attrbuf; p - attrbuf < l; p += strlen(p) + 1) {
181       cout << "  attr " << p << std::endl;
182     }
183     ASSERT_EQ(len, chain_fgetxattr(fd, name2.c_str(), buf, len*2));
184     ASSERT_EQ(0, chain_fremovexattr(fd, name2.c_str()));
185   }
186
187   for (int len = CHAIN_XATTR_SHORT_BLOCK_LEN - 10;
188        len < CHAIN_XATTR_SHORT_BLOCK_LEN + 10;
189        ++len) {
190     cout << len << std::endl;
191     const string x(len, 'x');
192     char buf[len*2];
193     ASSERT_EQ(len, chain_setxattr(file, name.c_str(), x.c_str(), len));
194     char attrbuf[4096];
195     int l = ceph_os_listxattr(file, attrbuf, sizeof(attrbuf));
196     for (char *p = attrbuf; p - attrbuf < l; p += strlen(p) + 1) {
197       cout << "  attr " << p << std::endl;
198     }
199     ASSERT_EQ(len, chain_getxattr(file, name.c_str(), buf, len*2));
200   }
201
202   {
203     // test tail path in chain_getxattr
204     const char *aname = "user.baz";
205     char buf[CHAIN_XATTR_SHORT_BLOCK_LEN*3];
206     memset(buf, 'x', sizeof(buf));
207     ASSERT_EQ((int)sizeof(buf), chain_setxattr(file, aname, buf, sizeof(buf)));
208     ASSERT_EQ(-ERANGE, chain_getxattr(file, aname, buf,
209                                       CHAIN_XATTR_SHORT_BLOCK_LEN*2));
210   }
211   {
212     // test tail path in chain_fgetxattr
213     const char *aname = "user.biz";
214     char buf[CHAIN_XATTR_SHORT_BLOCK_LEN*3];
215     memset(buf, 'x', sizeof(buf));
216     ASSERT_EQ((int)sizeof(buf), chain_fsetxattr(fd, aname, buf, sizeof(buf)));
217     ASSERT_EQ(-ERANGE, chain_fgetxattr(fd, aname, buf,
218                                        CHAIN_XATTR_SHORT_BLOCK_LEN*2));
219   }
220
221   ::close(fd);
222   ::unlink(file);
223 }
224
225 void get_vector_from_xattr(vector<string> &xattrs, char* xattr, int size) {
226   char *end = xattr + size;
227   while (xattr < end) {
228     if (*xattr == '\0' )
229       break;
230     xattrs.push_back(xattr);
231     xattr += strlen(xattr) + 1;
232   }
233 }
234
235 bool listxattr_cmp(char* xattr1, char* xattr2, int size) {
236   vector<string> xattrs1;
237   vector<string> xattrs2;
238   get_vector_from_xattr(xattrs1, xattr1, size);
239   get_vector_from_xattr(xattrs2, xattr2, size);
240
241   if (xattrs1.size() != xattrs2.size())
242     return false;
243
244   std::sort(xattrs1.begin(), xattrs1.end());
245   std::sort(xattrs2.begin(), xattrs2.end());
246   std::vector<string> diff;
247   std::set_difference(xattrs1.begin(), xattrs1.end(),
248                           xattrs2.begin(), xattrs2.end(),
249                           diff.begin());
250
251   return diff.empty();
252 }
253
254 TEST(chain_xattr, listxattr) {
255   const char* file = FILENAME;
256   ::unlink(file);
257   int fd = ::open(file, O_CREAT|O_WRONLY|O_TRUNC, 0700);
258   const string user("user.");
259   const string name1 = user + string(CHAIN_XATTR_MAX_NAME_LEN - user.size(), '1');
260   const string name2 = user + string(CHAIN_XATTR_MAX_NAME_LEN - user.size(), '@');
261   const string x(LARGE_BLOCK_LEN, 'X');
262   const int y = 1234;
263
264   int orig_size = chain_listxattr(file, NULL, 0);
265   char *orig_buffer = NULL;
266   string orig_str;
267   if (orig_size) {
268     orig_buffer = (char*)malloc(orig_size);
269     chain_flistxattr(fd, orig_buffer, orig_size);
270     orig_str = string(orig_buffer);
271     orig_size = orig_str.size();
272   }
273
274   ASSERT_EQ(LARGE_BLOCK_LEN, chain_setxattr(file, name1.c_str(), x.c_str(), LARGE_BLOCK_LEN));
275   ASSERT_EQ((int)sizeof(y), chain_setxattr(file, name2.c_str(), &y, sizeof(y)));
276
277   int buffer_size = 0;
278   if (orig_size)
279     buffer_size += orig_size + sizeof(char);
280   buffer_size += name1.size() + sizeof(char) + name2.size() + sizeof(char);
281
282   int index = 0;
283   char* expected = (char*)malloc(buffer_size);
284   ::memset(expected, '\0', buffer_size);
285   if (orig_size) {
286     ::strcpy(expected, orig_str.c_str());
287     index = orig_size + 1;
288   }
289   ::strcpy(expected + index, name1.c_str());
290   ::strcpy(expected + index + name1.size() + 1, name2.c_str());
291   char* actual = (char*)malloc(buffer_size);
292   ::memset(actual, '\0', buffer_size);
293   ASSERT_LT(buffer_size, chain_listxattr(file, NULL, 0)); // size evaluation is conservative
294   chain_listxattr(file, actual, buffer_size);
295   ASSERT_TRUE(listxattr_cmp(expected, actual, buffer_size));
296   ::memset(actual, '\0', buffer_size);
297   chain_flistxattr(fd, actual, buffer_size);
298   ASSERT_TRUE(listxattr_cmp(expected, actual, buffer_size));
299
300   int unlikely_to_be_a_valid_fd = 400;
301   ASSERT_GT(0, chain_listxattr("UNLIKELY_TO_EXIST", actual, 0));
302   ASSERT_GT(0, chain_listxattr("UNLIKELY_TO_EXIST", actual, buffer_size));
303   ASSERT_GT(0, chain_flistxattr(unlikely_to_be_a_valid_fd, actual, 0));
304   ASSERT_GT(0, chain_flistxattr(unlikely_to_be_a_valid_fd, actual, buffer_size));
305   ASSERT_EQ(-ERANGE, chain_listxattr(file, actual, 1));
306   ASSERT_EQ(-ERANGE, chain_flistxattr(fd, actual, 1));
307
308   ASSERT_EQ(0, chain_removexattr(file, name1.c_str()));
309   ASSERT_EQ(0, chain_removexattr(file, name2.c_str()));
310
311   free(orig_buffer);
312   free(actual);
313   free(expected);
314   ::unlink(file);
315 }
316
317 list<string> get_xattrs(int fd)
318 {
319   char _buf[1024];
320   char *buf = _buf;
321   int len = sys_flistxattr(fd, _buf, sizeof(_buf));
322   if (len < 0)
323     return list<string>();
324   list<string> ret;
325   while (len > 0) {
326     size_t next_len = strlen(buf);
327     ret.push_back(string(buf, buf + next_len));
328     assert(len >= (int)(next_len + 1));
329     buf += (next_len + 1);
330     len -= (next_len + 1);
331   }
332   return ret;
333 }
334
335 list<string> get_xattrs(string fn)
336 {
337   int fd = ::open(fn.c_str(), O_RDONLY);
338   if (fd < 0)
339     return list<string>();
340   auto ret = get_xattrs(fd);
341   ::close(fd);
342   return ret;
343 }
344
345 TEST(chain_xattr, fskip_chain_cleanup_and_ensure_single_attr)
346 {
347   const char *name = "user.foo";
348   const char *file = FILENAME;
349   ::unlink(file);
350   int fd = ::open(file, O_CREAT|O_RDWR|O_TRUNC, 0700);
351
352   std::size_t existing_xattrs = get_xattrs(fd).size();
353   char buf[800];
354   memset(buf, 0x1F, sizeof(buf));
355   // set chunked without either
356   {
357     std::size_t r = chain_fsetxattr(fd, name, buf, sizeof(buf));
358     ASSERT_EQ(sizeof(buf), r);
359     ASSERT_GT(get_xattrs(fd).size(), existing_xattrs + 1UL);
360   }
361
362   // verify
363   {
364     char buf2[sizeof(buf)*2];
365     std::size_t r = chain_fgetxattr(fd, name, buf2, sizeof(buf2));
366     ASSERT_EQ(sizeof(buf), r);
367     ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
368   }
369
370   // overwrite
371   {
372     std::size_t r = chain_fsetxattr<false, true>(fd, name, buf, sizeof(buf));
373     ASSERT_EQ(sizeof(buf), r);
374     ASSERT_EQ(existing_xattrs + 1UL, get_xattrs(fd).size());
375   }
376
377   // verify
378   {
379     char buf2[sizeof(buf)*2];
380     std::size_t r = chain_fgetxattr(fd, name, buf2, sizeof(buf2));
381     ASSERT_EQ(sizeof(buf), r);
382     ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
383   }
384
385   ::close(fd);
386   ::unlink(file);
387 }
388
389 TEST(chain_xattr, skip_chain_cleanup_and_ensure_single_attr)
390 {
391   const char *name = "user.foo";
392   const char *file = FILENAME;
393   ::unlink(file);
394   int fd = ::open(file, O_CREAT|O_RDWR|O_TRUNC, 0700);
395   std::size_t existing_xattrs = get_xattrs(fd).size();
396   ::close(fd);
397
398   char buf[3000];
399   memset(buf, 0x1F, sizeof(buf));
400   // set chunked without either
401   {
402     std::size_t r = chain_setxattr(file, name, buf, sizeof(buf));
403     ASSERT_EQ(sizeof(buf), r);
404     ASSERT_GT(get_xattrs(file).size(), existing_xattrs + 1UL);
405   }
406
407   // verify
408   {
409     char buf2[sizeof(buf)*2];
410     std::size_t r = chain_getxattr(file, name, buf2, sizeof(buf2));
411     ASSERT_EQ(sizeof(buf), r);
412     ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
413   }
414
415   // overwrite
416   {
417     std::size_t r = chain_setxattr<false, true>(file, name, buf, sizeof(buf));
418     ASSERT_EQ(sizeof(buf), r);
419     ASSERT_EQ(existing_xattrs + 1UL, get_xattrs(file).size());
420   }
421
422   // verify
423   {
424     char buf2[sizeof(buf)*2];
425     std::size_t r = chain_getxattr(file, name, buf2, sizeof(buf2));
426     ASSERT_EQ(sizeof(buf), r);
427     ASSERT_EQ(0, memcmp(buf, buf2, sizeof(buf)));
428   }
429
430   ::unlink(file);
431 }
432
433 int main(int argc, char **argv) {
434   vector<const char*> args;
435   argv_to_vec(argc, (const char **)argv, args);
436
437   auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
438                          CODE_ENVIRONMENT_UTILITY, 0);
439   common_init_finish(g_ceph_context);
440   g_ceph_context->_conf->set_val("err_to_stderr", "false");
441   g_ceph_context->_conf->set_val("log_to_stderr", "false");
442   g_ceph_context->_conf->apply_changes(NULL);
443
444   const char* file = FILENAME;
445   int x = 1234;
446   int y = 0;
447   int tmpfd = ::open(file, O_CREAT|O_WRONLY|O_TRUNC, 0700);
448   int ret = ::ceph_os_fsetxattr(tmpfd, "user.test", &x, sizeof(x));
449   if (ret >= 0)
450     ret = ::ceph_os_fgetxattr(tmpfd, "user.test", &y, sizeof(y));
451   ::close(tmpfd);
452   ::unlink(file);
453   if ((ret < 0) || (x != y)) {
454     cerr << "SKIP all tests because extended attributes don't appear to work in the file system in which the tests are run: " << cpp_strerror(ret) << std::endl;
455   } else {
456     ::testing::InitGoogleTest(&argc, argv);
457     return RUN_ALL_TESTS();
458   }
459 }
460
461 // Local Variables:
462 // compile-command: "cd ../.. ; make unittest_chain_xattr ; valgrind --tool=memcheck ./unittest_chain_xattr # --gtest_filter=chain_xattr.get_and_set"
463 // End: