Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / test / libcephfs / test.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) 2011 New Dream Network
7  *
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.
12  *
13  */
14
15 #include "gtest/gtest.h"
16 #include "include/cephfs/libcephfs.h"
17 #include "include/stat.h"
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <unistd.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <dirent.h>
24 #include <sys/xattr.h>
25 #include <sys/uio.h>
26
27 #ifdef __linux__
28 #include <limits.h>
29 #endif
30
31 #include <map>
32 #include <vector>
33
34 TEST(LibCephFS, OpenEmptyComponent) {
35
36   pid_t mypid = getpid();
37   struct ceph_mount_info *cmount;
38   ASSERT_EQ(0, ceph_create(&cmount, NULL));
39   ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
40   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
41   ASSERT_EQ(0, ceph_mount(cmount, "/"));
42
43   char c_dir[1024];
44   sprintf(c_dir, "/open_test_%d", mypid);
45   struct ceph_dir_result *dirp;
46
47   ASSERT_EQ(0, ceph_mkdirs(cmount, c_dir, 0777));
48
49   ASSERT_EQ(0, ceph_opendir(cmount, c_dir, &dirp));
50
51   char c_path[1024];
52   sprintf(c_path, "/open_test_%d//created_file_%d", mypid, mypid);
53   int fd = ceph_open(cmount, c_path, O_RDONLY|O_CREAT, 0666);
54   ASSERT_LT(0, fd);
55
56   ASSERT_EQ(0, ceph_close(cmount, fd));
57   ASSERT_EQ(0, ceph_closedir(cmount, dirp));
58   ceph_shutdown(cmount);
59
60   ASSERT_EQ(0, ceph_create(&cmount, NULL));
61   ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
62   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
63
64   ASSERT_EQ(0, ceph_mount(cmount, "/"));
65
66   fd = ceph_open(cmount, c_path, O_RDONLY, 0666);
67   ASSERT_LT(0, fd);
68
69   ASSERT_EQ(0, ceph_close(cmount, fd));
70   ceph_shutdown(cmount);
71 }
72
73 TEST(LibCephFS, OpenReadWrite) {
74   struct ceph_mount_info *cmount;
75   ASSERT_EQ(0, ceph_create(&cmount, NULL));
76   ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
77   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
78   ASSERT_EQ(0, ceph_mount(cmount, "/"));
79
80   char c_path[1024];
81   sprintf(c_path, "test_open_rdwr_%d", getpid());
82   int fd = ceph_open(cmount, c_path, O_WRONLY|O_CREAT, 0666);
83   ASSERT_LT(0, fd);
84
85   const char *out_buf = "hello world";
86   size_t size = strlen(out_buf);
87   char in_buf[100];
88   ASSERT_EQ(ceph_write(cmount, fd, out_buf, size, 0), (int)size);
89   ASSERT_EQ(ceph_read(cmount, fd, in_buf, sizeof(in_buf), 0), -EBADF);
90   ASSERT_EQ(0, ceph_close(cmount, fd));
91
92   fd = ceph_open(cmount, c_path, O_RDONLY, 0);
93   ASSERT_LT(0, fd);
94   ASSERT_EQ(ceph_write(cmount, fd, out_buf, size, 0), -EBADF);
95   ASSERT_EQ(ceph_read(cmount, fd, in_buf, sizeof(in_buf), 0), (int)size);
96   ASSERT_EQ(0, ceph_close(cmount, fd));
97
98   fd = ceph_open(cmount, c_path, O_RDWR, 0);
99   ASSERT_LT(0, fd);
100   ASSERT_EQ(ceph_write(cmount, fd, out_buf, size, 0), (int)size);
101   ASSERT_EQ(ceph_read(cmount, fd, in_buf, sizeof(in_buf), 0), (int)size);
102   ASSERT_EQ(0, ceph_close(cmount, fd));
103
104   ceph_shutdown(cmount);
105 }
106
107 TEST(LibCephFS, MountNonExist) {
108
109   struct ceph_mount_info *cmount;
110
111   ASSERT_EQ(0, ceph_create(&cmount, NULL));
112   ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
113   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
114   ASSERT_NE(0, ceph_mount(cmount, "/non-exist"));
115   ceph_shutdown(cmount);
116 }
117
118 TEST(LibCephFS, MountDouble) {
119
120   struct ceph_mount_info *cmount;
121
122   ASSERT_EQ(0, ceph_create(&cmount, NULL));
123   ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
124   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
125   ASSERT_EQ(0, ceph_mount(cmount, "/"));
126   ASSERT_EQ(-EISCONN, ceph_mount(cmount, "/"));
127   ceph_shutdown(cmount);
128 }
129
130 TEST(LibCephFS, MountRemount) {
131
132   struct ceph_mount_info *cmount;
133
134   ASSERT_EQ(0, ceph_create(&cmount, NULL));
135   ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
136   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
137
138   CephContext *cct = ceph_get_mount_context(cmount);
139   ASSERT_EQ(0, ceph_mount(cmount, "/"));
140   ASSERT_EQ(0, ceph_unmount(cmount));
141
142   ASSERT_EQ(0, ceph_mount(cmount, "/"));
143   ASSERT_EQ(cct, ceph_get_mount_context(cmount));
144
145   ceph_shutdown(cmount);
146 }
147
148 TEST(LibCephFS, UnmountUnmounted) {
149
150   struct ceph_mount_info *cmount;
151
152   ASSERT_EQ(0, ceph_create(&cmount, NULL));
153   ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
154   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
155   ASSERT_EQ(-ENOTCONN, ceph_unmount(cmount));
156   ceph_shutdown(cmount);
157 }
158
159 TEST(LibCephFS, ReleaseUnmounted) {
160
161   struct ceph_mount_info *cmount;
162
163   ASSERT_EQ(0, ceph_create(&cmount, NULL));
164   ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
165   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
166   ASSERT_EQ(0, ceph_release(cmount));
167 }
168
169 TEST(LibCephFS, ReleaseMounted) {
170
171   struct ceph_mount_info *cmount;
172
173   ASSERT_EQ(0, ceph_create(&cmount, NULL));
174   ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
175   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
176   ASSERT_EQ(0, ceph_mount(cmount, "/"));
177   ASSERT_EQ(-EISCONN, ceph_release(cmount));
178   ASSERT_EQ(0, ceph_unmount(cmount));
179   ASSERT_EQ(0, ceph_release(cmount));
180 }
181
182 TEST(LibCephFS, UnmountRelease) {
183
184   struct ceph_mount_info *cmount;
185
186   ASSERT_EQ(0, ceph_create(&cmount, NULL));
187   ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
188   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
189   ASSERT_EQ(0, ceph_mount(cmount, "/"));
190   ASSERT_EQ(0, ceph_unmount(cmount));
191   ASSERT_EQ(0, ceph_release(cmount));
192 }
193
194 TEST(LibCephFS, Mount) {
195   struct ceph_mount_info *cmount;
196   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
197   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
198   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
199   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
200   ceph_shutdown(cmount);
201
202   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
203   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
204   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
205   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
206   ceph_shutdown(cmount);
207 }
208
209 TEST(LibCephFS, OpenLayout) {
210   struct ceph_mount_info *cmount;
211   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
212   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
213   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
214   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
215
216   /* valid layout */
217   char test_layout_file[256];
218   sprintf(test_layout_file, "test_layout_%d_b", getpid());
219   int fd = ceph_open_layout(cmount, test_layout_file, O_CREAT|O_WRONLY, 0666, (1<<20), 7, (1<<20), NULL);
220   ASSERT_GT(fd, 0);
221   char poolname[80];
222   ASSERT_LT(0, ceph_get_file_pool_name(cmount, fd, poolname, sizeof(poolname)));
223   ASSERT_LT(0, ceph_get_file_pool_name(cmount, fd, poolname, 0));
224
225   /* on already-written file (ENOTEMPTY) */
226   ceph_write(cmount, fd, "hello world", 11, 0);
227   ceph_close(cmount, fd);
228
229   char xattrk[128];
230   char xattrv[128];
231   sprintf(xattrk, "ceph.file.layout.stripe_unit");
232   sprintf(xattrv, "65536");
233   ASSERT_EQ(-ENOTEMPTY, ceph_setxattr(cmount, test_layout_file, xattrk, (void *)xattrv, 5, 0));
234
235   /* invalid layout */
236   sprintf(test_layout_file, "test_layout_%d_c", getpid());
237   fd = ceph_open_layout(cmount, test_layout_file, O_CREAT, 0666, (1<<20), 1, 19, NULL);
238   ASSERT_EQ(fd, -EINVAL);
239
240   /* with data pool */
241   sprintf(test_layout_file, "test_layout_%d_d", getpid());
242   fd = ceph_open_layout(cmount, test_layout_file, O_CREAT, 0666, (1<<20), 7, (1<<20), poolname);
243   ASSERT_GT(fd, 0);
244   ceph_close(cmount, fd);
245
246   /* with metadata pool (invalid) */
247   sprintf(test_layout_file, "test_layout_%d_e", getpid());
248   fd = ceph_open_layout(cmount, test_layout_file, O_CREAT, 0666, (1<<20), 7, (1<<20), "metadata");
249   ASSERT_EQ(fd, -EINVAL);
250
251   /* with metadata pool (does not exist) */
252   sprintf(test_layout_file, "test_layout_%d_f", getpid());
253   fd = ceph_open_layout(cmount, test_layout_file, O_CREAT, 0666, (1<<20), 7, (1<<20), "asdfjasdfjasdf");
254   ASSERT_EQ(fd, -EINVAL);
255
256   ceph_shutdown(cmount);
257 }
258
259 TEST(LibCephFS, DirLs) {
260
261   pid_t mypid = getpid();
262
263   struct ceph_mount_info *cmount;
264   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
265   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
266   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
267   ASSERT_EQ(ceph_mount(cmount, "/"), 0);
268
269   struct ceph_dir_result *ls_dir = NULL;
270   char foostr[256];
271   sprintf(foostr, "dir_ls%d", mypid);
272   ASSERT_EQ(ceph_opendir(cmount, foostr, &ls_dir), -ENOENT);
273
274   ASSERT_EQ(ceph_mkdir(cmount, foostr, 0777), 0);
275   struct ceph_statx stx;
276   ASSERT_EQ(ceph_statx(cmount, foostr, &stx, 0, 0), 0);
277   ASSERT_NE(S_ISDIR(stx.stx_mode), 0);
278
279   char barstr[256];
280   sprintf(barstr, "dir_ls2%d", mypid);
281   ASSERT_EQ(ceph_statx(cmount, barstr, &stx, 0, AT_SYMLINK_NOFOLLOW), -ENOENT);
282
283   // insert files into directory and test open
284   char bazstr[256];
285   int i = 0, r = rand() % 4096;
286   if (getenv("LIBCEPHFS_RAND")) {
287     r = atoi(getenv("LIBCEPHFS_RAND"));
288   }
289   printf("rand: %d\n", r);
290   for(; i < r; ++i) {
291
292     sprintf(bazstr, "dir_ls%d/dirf%d", mypid, i);
293     int fd  = ceph_open(cmount, bazstr, O_CREAT|O_RDONLY, 0666);
294     ASSERT_GT(fd, 0);
295     ASSERT_EQ(ceph_close(cmount, fd), 0);
296
297     // set file sizes for readdirplus
298     ceph_truncate(cmount, bazstr, i);
299   }
300
301   ASSERT_EQ(ceph_opendir(cmount, foostr, &ls_dir), 0);
302
303   // not guaranteed to get . and .. first, but its a safe assumption in this case
304   struct dirent *result = ceph_readdir(cmount, ls_dir);
305   ASSERT_TRUE(result != NULL);
306   ASSERT_STREQ(result->d_name, ".");
307   result = ceph_readdir(cmount, ls_dir);
308   ASSERT_TRUE(result != NULL);
309   ASSERT_STREQ(result->d_name, "..");
310
311   std::vector<std::string> entries;
312   std::map<std::string, int64_t> offset_map;
313   int64_t offset = ceph_telldir(cmount, ls_dir);
314   for(i = 0; i < r; ++i) {
315     result = ceph_readdir(cmount, ls_dir);
316     ASSERT_TRUE(result != NULL);
317     entries.push_back(result->d_name);
318     offset_map[result->d_name] = offset;
319     offset = ceph_telldir(cmount, ls_dir);
320   }
321
322   ASSERT_TRUE(ceph_readdir(cmount, ls_dir) == NULL);
323   offset = ceph_telldir(cmount, ls_dir);
324
325   ASSERT_EQ(offset_map.size(), entries.size());
326   for(i = 0; i < r; ++i) {
327     sprintf(bazstr, "dirf%d", i);
328     ASSERT_TRUE(offset_map.count(bazstr) == 1);
329   }
330
331   // test seekdir
332   ceph_seekdir(cmount, ls_dir, offset);
333   ASSERT_TRUE(ceph_readdir(cmount, ls_dir) == NULL);
334
335   for (auto p = offset_map.begin(); p != offset_map.end(); ++p) {
336     ceph_seekdir(cmount, ls_dir, p->second);
337     result = ceph_readdir(cmount, ls_dir);
338     ASSERT_TRUE(result != NULL);
339     std::string d_name(result->d_name);
340     ASSERT_EQ(p->first, d_name);
341   }
342
343   // test rewinddir
344   ceph_rewinddir(cmount, ls_dir);
345
346   result = ceph_readdir(cmount, ls_dir);
347   ASSERT_TRUE(result != NULL);
348   ASSERT_STREQ(result->d_name, ".");
349   result = ceph_readdir(cmount, ls_dir);
350   ASSERT_TRUE(result != NULL);
351   ASSERT_STREQ(result->d_name, "..");
352
353   ceph_rewinddir(cmount, ls_dir);
354
355   int t = ceph_telldir(cmount, ls_dir);
356   ASSERT_GT(t, -1);
357
358   ASSERT_TRUE(ceph_readdir(cmount, ls_dir) != NULL);
359
360   // test seekdir - move back to the beginning
361   ceph_seekdir(cmount, ls_dir, t);
362
363   // test getdents
364   struct dirent *getdents_entries;
365   getdents_entries = (struct dirent *)malloc((r + 2) * sizeof(*getdents_entries));
366
367   int count = 0;
368   std::vector<std::string> found;
369   while (true) {
370     int len = ceph_getdents(cmount, ls_dir, (char *)getdents_entries, r * sizeof(*getdents_entries));
371     if (len == 0)
372       break;
373     ASSERT_GT(len, 0);
374     ASSERT_TRUE((len % sizeof(*getdents_entries)) == 0);
375     int n = len / sizeof(*getdents_entries);
376     int j;
377     if (count == 0) {
378       ASSERT_STREQ(getdents_entries[0].d_name, ".");
379       ASSERT_STREQ(getdents_entries[1].d_name, "..");
380       j = 2;
381     } else {
382       j = 0;
383     }
384     count += n;
385     for(; j < n; ++i, ++j) {
386       const char *name = getdents_entries[j].d_name;
387       found.push_back(name);
388     }
389   }
390   ASSERT_EQ(found, entries);
391   free(getdents_entries);
392
393   // test readdir_r
394   ceph_rewinddir(cmount, ls_dir);
395
396   result = ceph_readdir(cmount, ls_dir);
397   ASSERT_TRUE(result != NULL);
398   ASSERT_STREQ(result->d_name, ".");
399   result = ceph_readdir(cmount, ls_dir);
400   ASSERT_TRUE(result != NULL);
401   ASSERT_STREQ(result->d_name, "..");
402
403   found.clear();
404   while (true) {
405     struct dirent rdent;
406     int len = ceph_readdir_r(cmount, ls_dir, &rdent);
407     if (len == 0)
408       break;
409     ASSERT_EQ(len, 1);
410     found.push_back(rdent.d_name);
411   }
412   ASSERT_EQ(found, entries);
413
414   // test readdirplus
415   ceph_rewinddir(cmount, ls_dir);
416
417   result = ceph_readdir(cmount, ls_dir);
418   ASSERT_TRUE(result != NULL);
419   ASSERT_STREQ(result->d_name, ".");
420   result = ceph_readdir(cmount, ls_dir);
421   ASSERT_TRUE(result != NULL);
422   ASSERT_STREQ(result->d_name, "..");
423
424   found.clear();
425   while (true) {
426     struct dirent rdent;
427     struct ceph_statx stx;
428     int len = ceph_readdirplus_r(cmount, ls_dir, &rdent, &stx,
429                                  CEPH_STATX_SIZE, AT_NO_ATTR_SYNC, NULL);
430     if (len == 0)
431       break;
432     ASSERT_EQ(len, 1);
433     const char *name = rdent.d_name;
434     found.push_back(name);
435     int size;
436     sscanf(name, "dirf%d", &size);
437     ASSERT_TRUE(stx.stx_mask & CEPH_STATX_SIZE);
438     ASSERT_EQ(stx.stx_size, (size_t)size);
439     ASSERT_EQ(stx.stx_ino, rdent.d_ino);
440     //ASSERT_EQ(st.st_mode, (mode_t)0666);
441   }
442   ASSERT_EQ(found, entries);
443
444   ASSERT_EQ(ceph_closedir(cmount, ls_dir), 0);
445
446   ceph_shutdown(cmount);
447 }
448
449 TEST(LibCephFS, ManyNestedDirs) {
450   struct ceph_mount_info *cmount;
451   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
452   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
453   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
454   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
455
456   const char *many_path = "a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a";
457   ASSERT_EQ(ceph_mkdirs(cmount, many_path, 0755), 0);
458
459   int i = 0;
460
461   for(; i < 39; ++i) {
462     ASSERT_EQ(ceph_chdir(cmount, "a"), 0);
463
464     struct ceph_dir_result *dirp;
465     ASSERT_EQ(ceph_opendir(cmount, "a", &dirp), 0);
466     struct dirent *dent = ceph_readdir(cmount, dirp);
467     ASSERT_TRUE(dent != NULL);
468     ASSERT_STREQ(dent->d_name, ".");
469     dent = ceph_readdir(cmount, dirp);
470     ASSERT_TRUE(dent != NULL);
471     ASSERT_STREQ(dent->d_name, "..");
472     dent = ceph_readdir(cmount, dirp);
473     ASSERT_TRUE(dent != NULL);
474     ASSERT_STREQ(dent->d_name, "a");
475     ASSERT_EQ(ceph_closedir(cmount, dirp), 0);
476   }
477
478   ASSERT_STREQ(ceph_getcwd(cmount), "/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a");
479
480   ASSERT_EQ(ceph_chdir(cmount, "a/a/a"), 0);
481
482   for(i = 0; i < 39; ++i) {
483     ASSERT_EQ(ceph_chdir(cmount, ".."), 0);
484     ASSERT_EQ(ceph_rmdir(cmount, "a"), 0);
485   }
486
487   ASSERT_EQ(ceph_chdir(cmount, "/"), 0);
488
489   ASSERT_EQ(ceph_rmdir(cmount, "a/a/a"), 0);
490
491   ceph_shutdown(cmount);
492 }
493
494 TEST(LibCephFS, Xattrs) {
495   struct ceph_mount_info *cmount;
496   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
497   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
498   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
499   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
500
501   char test_xattr_file[256];
502   sprintf(test_xattr_file, "test_xattr_%d", getpid());
503   int fd = ceph_open(cmount, test_xattr_file, O_CREAT, 0666);
504   ASSERT_GT(fd, 0);
505
506   char i = 'a';
507   char xattrk[128];
508   char xattrv[128];
509   for(; i < 'a'+26; ++i) {
510     sprintf(xattrk, "user.test_xattr_%c", i);
511     int len = sprintf(xattrv, "testxattr%c", i);
512     ASSERT_EQ(ceph_setxattr(cmount, test_xattr_file, xattrk, (void *) xattrv, len, XATTR_CREATE), 0);
513   }
514
515   char xattrlist[128*26];
516   int len = ceph_listxattr(cmount, test_xattr_file, xattrlist, sizeof(xattrlist));
517   char *p = xattrlist;
518   char *n;
519   i = 'a';
520   while (len > 0) {
521     // skip/ignore the dir layout
522     if (strcmp(p, "ceph.dir.layout") == 0 ||
523         strcmp(p, "ceph.file.layout") == 0) {
524       len -= strlen(p) + 1;
525       p += strlen(p) + 1;
526       continue;
527     }
528
529     sprintf(xattrk, "user.test_xattr_%c", i);
530     ASSERT_STREQ(p, xattrk);
531
532     char gxattrv[128];
533     std::cout << "getting attr " << p << std::endl;
534     int alen = ceph_getxattr(cmount, test_xattr_file, p, (void *) gxattrv, 128);
535     ASSERT_GT(alen, 0);
536     sprintf(xattrv, "testxattr%c", i);
537     ASSERT_TRUE(!strncmp(xattrv, gxattrv, alen));
538
539     n = index(p, '\0');
540     n++;
541     len -= (n - p);
542     p = n;
543     ++i;
544   }
545
546   i = 'a';
547   for(i = 'a'; i < 'a'+26; ++i) {
548     sprintf(xattrk, "user.test_xattr_%c", i);
549     ASSERT_EQ(ceph_removexattr(cmount, test_xattr_file, xattrk), 0);
550   }
551
552   ceph_close(cmount, fd);
553   ceph_shutdown(cmount);
554
555 }
556
557 TEST(LibCephFS, Xattrs_ll) {
558   struct ceph_mount_info *cmount;
559   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
560   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
561   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
562   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
563
564   char test_xattr_file[256];
565   sprintf(test_xattr_file, "test_xattr_%d", getpid());
566   int fd = ceph_open(cmount, test_xattr_file, O_CREAT, 0666);
567   ASSERT_GT(fd, 0);
568   ceph_close(cmount, fd);
569
570   Inode *root = NULL;
571   Inode *existent_file_handle = NULL;
572
573   int res = ceph_ll_lookup_root(cmount, &root);
574   ASSERT_EQ(res, 0);
575
576   UserPerm *perms = ceph_mount_perms(cmount);
577   struct ceph_statx stx;
578
579   res = ceph_ll_lookup(cmount, root, test_xattr_file, &existent_file_handle,
580                        &stx, 0, 0, perms);
581   ASSERT_EQ(res, 0);
582
583   const char *valid_name = "user.attrname";
584   const char *value = "attrvalue";
585   char value_buf[256] = { 0 };
586
587   res = ceph_ll_setxattr(cmount, existent_file_handle, valid_name, value, strlen(value), 0, perms);
588   ASSERT_EQ(res, 0);
589
590   res = ceph_ll_getxattr(cmount, existent_file_handle, valid_name, value_buf, 256, perms);
591   ASSERT_EQ(res, (int)strlen(value));
592
593   value_buf[res] = '\0';
594   ASSERT_STREQ(value_buf, value);
595
596   ceph_shutdown(cmount);
597 }
598
599 TEST(LibCephFS, LstatSlashdot) {
600   struct ceph_mount_info *cmount;
601   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
602   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
603   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
604   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
605
606   struct ceph_statx stx;
607   ASSERT_EQ(ceph_statx(cmount, "/.", &stx, 0, AT_SYMLINK_NOFOLLOW), 0);
608   ASSERT_EQ(ceph_statx(cmount, ".", &stx, 0, AT_SYMLINK_NOFOLLOW), 0);
609
610   ceph_shutdown(cmount);
611 }
612
613 TEST(LibCephFS, DoubleChmod) {
614
615   struct ceph_mount_info *cmount;
616   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
617   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
618   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
619   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
620
621   char test_file[256];
622   sprintf(test_file, "test_perms_%d", getpid());
623
624   int fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0666);
625   ASSERT_GT(fd, 0);
626
627   // write some stuff
628   const char *bytes = "foobarbaz";
629   ASSERT_EQ(ceph_write(cmount, fd, bytes, strlen(bytes), 0), (int)strlen(bytes));
630
631   ceph_close(cmount, fd);
632
633   // set perms to read but can't write
634   ASSERT_EQ(ceph_chmod(cmount, test_file, 0400), 0);
635
636   fd = ceph_open(cmount, test_file, O_RDWR, 0);
637   ASSERT_EQ(fd, -EACCES);
638
639   fd = ceph_open(cmount, test_file, O_RDONLY, 0);
640   ASSERT_GT(fd, -1);
641
642   char buf[100];
643   int ret = ceph_read(cmount, fd, buf, 100, 0);
644   ASSERT_EQ(ret, (int)strlen(bytes));
645   buf[ret] = '\0';
646   ASSERT_STREQ(buf, bytes);
647
648   ASSERT_EQ(ceph_write(cmount, fd, bytes, strlen(bytes), 0), -EBADF);
649
650   ceph_close(cmount, fd);
651
652   // reset back to writeable
653   ASSERT_EQ(ceph_chmod(cmount, test_file, 0600), 0);
654
655   // ensure perms are correct
656   struct ceph_statx stx;
657   ASSERT_EQ(ceph_statx(cmount, test_file, &stx, CEPH_STATX_MODE, AT_SYMLINK_NOFOLLOW), 0);
658   ASSERT_EQ(stx.stx_mode, 0100600U);
659
660   fd = ceph_open(cmount, test_file, O_RDWR, 0);
661   ASSERT_GT(fd, 0);
662
663   ASSERT_EQ(ceph_write(cmount, fd, bytes, strlen(bytes), 0), (int)strlen(bytes));
664   ceph_close(cmount, fd);
665
666   ceph_shutdown(cmount);
667 }
668
669 TEST(LibCephFS, Fchmod) {
670   struct ceph_mount_info *cmount;
671   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
672   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
673   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
674   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
675
676   char test_file[256];
677   sprintf(test_file, "test_perms_%d", getpid());
678
679   int fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0666);
680   ASSERT_GT(fd, 0);
681
682   // write some stuff
683   const char *bytes = "foobarbaz";
684   ASSERT_EQ(ceph_write(cmount, fd, bytes, strlen(bytes), 0), (int)strlen(bytes));
685
686   // set perms to read but can't write
687   ASSERT_EQ(ceph_fchmod(cmount, fd, 0400), 0);
688
689   char buf[100];
690   int ret = ceph_read(cmount, fd, buf, 100, 0);
691   ASSERT_EQ(ret, (int)strlen(bytes));
692   buf[ret] = '\0';
693   ASSERT_STREQ(buf, bytes);
694
695   ASSERT_EQ(ceph_write(cmount, fd, bytes, strlen(bytes), 0), (int)strlen(bytes));
696
697   ceph_close(cmount, fd);
698
699   ASSERT_EQ(ceph_open(cmount, test_file, O_RDWR, 0), -EACCES);
700
701   // reset back to writeable
702   ASSERT_EQ(ceph_chmod(cmount, test_file, 0600), 0);
703
704   fd = ceph_open(cmount, test_file, O_RDWR, 0);
705   ASSERT_GT(fd, 0);
706
707   ASSERT_EQ(ceph_write(cmount, fd, bytes, strlen(bytes), 0), (int)strlen(bytes));
708   ceph_close(cmount, fd);
709
710   ceph_shutdown(cmount);
711 }
712
713 TEST(LibCephFS, Fchown) {
714   struct ceph_mount_info *cmount;
715   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
716   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
717   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
718   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
719
720   char test_file[256];
721   sprintf(test_file, "test_fchown_%d", getpid());
722
723   int fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0666);
724   ASSERT_GT(fd, 0);
725
726   // set perms to readable and writeable only by owner
727   ASSERT_EQ(ceph_fchmod(cmount, fd, 0600), 0);
728
729   // change ownership to nobody -- we assume nobody exists and id is always 65534
730   ASSERT_EQ(ceph_conf_set(cmount, "client_permissions", "0"), 0);
731   ASSERT_EQ(ceph_fchown(cmount, fd, 65534, 65534), 0);
732   ASSERT_EQ(ceph_conf_set(cmount, "client_permissions", "1"), 0);
733
734   ceph_close(cmount, fd);
735
736   fd = ceph_open(cmount, test_file, O_RDWR, 0);
737   ASSERT_EQ(fd, -EACCES);
738
739   ceph_shutdown(cmount);
740 }
741
742 #if defined(__linux__) && defined(O_PATH)
743 TEST(LibCephFS, FlagO_PATH) {
744   struct ceph_mount_info *cmount;
745
746   ASSERT_EQ(0, ceph_create(&cmount, NULL));
747   ASSERT_EQ(0, ceph_conf_read_file(cmount, NULL));
748   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
749   ASSERT_EQ(0, ceph_mount(cmount, NULL));
750
751   char test_file[PATH_MAX];
752   sprintf(test_file, "test_oflag_%d", getpid());
753
754   int fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR|O_PATH, 0666);
755   ASSERT_EQ(-ENOENT, fd);
756
757   fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0666);
758   ASSERT_GT(fd, 0);
759   ASSERT_EQ(0, ceph_close(cmount, fd));
760
761   // ok, the file has been created. perform real checks now
762   fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR|O_PATH, 0666);
763   ASSERT_GT(fd, 0);
764
765   char buf[128];
766   ASSERT_EQ(-EBADF, ceph_read(cmount, fd, buf, sizeof(buf), 0));
767   ASSERT_EQ(-EBADF, ceph_write(cmount, fd, buf, sizeof(buf), 0));
768
769   // set perms to readable and writeable only by owner
770   ASSERT_EQ(-EBADF, ceph_fchmod(cmount, fd, 0600));
771
772   // change ownership to nobody -- we assume nobody exists and id is always 65534
773   ASSERT_EQ(-EBADF, ceph_fchown(cmount, fd, 65534, 65534));
774
775   // try to sync
776   ASSERT_EQ(-EBADF, ceph_fsync(cmount, fd, false));
777
778   struct ceph_statx stx;
779   ASSERT_EQ(0, ceph_fstatx(cmount, fd, &stx, 0, 0));
780
781   ASSERT_EQ(0, ceph_close(cmount, fd));
782   ceph_shutdown(cmount);
783 }
784 #endif /* __linux */
785
786 TEST(LibCephFS, Symlinks) {
787   struct ceph_mount_info *cmount;
788   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
789   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
790   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
791   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
792
793   char test_file[256];
794   sprintf(test_file, "test_symlinks_%d", getpid());
795
796   int fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0666);
797   ASSERT_GT(fd, 0);
798
799   ceph_close(cmount, fd);
800
801   char test_symlink[256];
802   sprintf(test_symlink, "test_symlinks_sym_%d", getpid());
803
804   ASSERT_EQ(ceph_symlink(cmount, test_file, test_symlink), 0);
805
806   // test the O_NOFOLLOW case
807   fd = ceph_open(cmount, test_symlink, O_NOFOLLOW, 0);
808   ASSERT_EQ(fd, -ELOOP);
809
810   // stat the original file
811   struct ceph_statx stx_orig;
812   ASSERT_EQ(ceph_statx(cmount, test_file, &stx_orig, CEPH_STATX_ALL_STATS, 0), 0);
813   // stat the symlink
814   struct ceph_statx stx_symlink_orig;
815   ASSERT_EQ(ceph_statx(cmount, test_symlink, &stx_symlink_orig, CEPH_STATX_ALL_STATS, 0), 0);
816   // ensure the statx bufs are equal
817   ASSERT_EQ(memcmp(&stx_orig, &stx_symlink_orig, sizeof(stx_orig)), 0);
818
819   sprintf(test_file, "/test_symlinks_abs_%d", getpid());
820
821   fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0666);
822   ASSERT_GT(fd, 0);
823
824   ceph_close(cmount, fd);
825
826   sprintf(test_symlink, "/test_symlinks_abs_sym_%d", getpid());
827
828   ASSERT_EQ(ceph_symlink(cmount, test_file, test_symlink), 0);
829
830   // stat the original file
831   ASSERT_EQ(ceph_statx(cmount, test_file, &stx_orig, CEPH_STATX_ALL_STATS, 0), 0);
832   // stat the symlink
833   ASSERT_EQ(ceph_statx(cmount, test_symlink, &stx_symlink_orig, CEPH_STATX_ALL_STATS, 0), 0);
834   // ensure the statx bufs are equal
835   ASSERT_TRUE(!memcmp(&stx_orig, &stx_symlink_orig, sizeof(stx_orig)));
836
837   // test lstat
838   ASSERT_EQ(ceph_statx(cmount, test_symlink, &stx_orig, CEPH_STATX_ALL_STATS, AT_SYMLINK_NOFOLLOW), 0);
839   ASSERT_TRUE(S_ISLNK(stx_orig.stx_mode));
840
841   ceph_shutdown(cmount);
842 }
843
844 TEST(LibCephFS, DirSyms) {
845   struct ceph_mount_info *cmount;
846   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
847   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
848   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
849   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
850
851   char test_dir1[256];
852   sprintf(test_dir1, "dir1_symlinks_%d", getpid());
853
854   ASSERT_EQ(ceph_mkdir(cmount, test_dir1, 0700), 0);
855
856   char test_symdir[256];
857   sprintf(test_symdir, "symdir_symlinks_%d", getpid());
858
859   ASSERT_EQ(ceph_symlink(cmount, test_dir1, test_symdir), 0);
860
861   char test_file[256];
862   sprintf(test_file, "/symdir_symlinks_%d/test_symdir_file", getpid());
863   int fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0600);
864   ASSERT_GT(fd, 0);
865   ceph_close(cmount, fd);
866
867   struct ceph_statx stx;
868   ASSERT_EQ(ceph_statx(cmount, test_file, &stx, 0, AT_SYMLINK_NOFOLLOW), 0);
869
870   // ensure that its a file not a directory we get back
871   ASSERT_TRUE(S_ISREG(stx.stx_mode));
872
873   ceph_shutdown(cmount);
874 }
875
876 TEST(LibCephFS, LoopSyms) {
877   struct ceph_mount_info *cmount;
878   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
879   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
880   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
881   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
882
883   char test_dir1[256];
884   sprintf(test_dir1, "dir1_loopsym_%d", getpid());
885
886   ASSERT_EQ(ceph_mkdir(cmount, test_dir1, 0700), 0);
887
888   char test_dir2[256];
889   sprintf(test_dir2, "/dir1_loopsym_%d/loop_dir", getpid());
890
891   ASSERT_EQ(ceph_mkdir(cmount, test_dir2, 0700), 0);
892
893   // symlink it itself:  /path/to/mysym -> /path/to/mysym
894   char test_symdir[256];
895   sprintf(test_symdir, "/dir1_loopsym_%d/loop_dir/symdir", getpid());
896
897   ASSERT_EQ(ceph_symlink(cmount, test_symdir, test_symdir), 0);
898
899   char test_file[256];
900   sprintf(test_file, "/dir1_loopsym_%d/loop_dir/symdir/test_loopsym_file", getpid());
901   int fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0600);
902   ASSERT_EQ(fd, -ELOOP);
903
904   // loop: /a -> /b, /b -> /c, /c -> /a
905   char a[256], b[256], c[256];
906   sprintf(a, "/%s/a", test_dir1);
907   sprintf(b, "/%s/b", test_dir1);
908   sprintf(c, "/%s/c", test_dir1);
909   ASSERT_EQ(ceph_symlink(cmount, a, b), 0);
910   ASSERT_EQ(ceph_symlink(cmount, b, c), 0);
911   ASSERT_EQ(ceph_symlink(cmount, c, a), 0);
912   ASSERT_EQ(ceph_open(cmount, a, O_RDWR, 0), -ELOOP);
913
914   ceph_shutdown(cmount);
915 }
916
917 TEST(LibCephFS, HardlinkNoOriginal) {
918
919   int mypid = getpid();
920
921   struct ceph_mount_info *cmount;
922   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
923   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
924   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
925   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
926
927   char dir[256];
928   sprintf(dir, "/test_rmdirfail%d", mypid);
929   ASSERT_EQ(ceph_mkdir(cmount, dir, 0777), 0);
930
931   ASSERT_EQ(ceph_chdir(cmount, dir), 0);
932
933   int fd = ceph_open(cmount, "f1", O_CREAT, 0644);
934   ASSERT_GT(fd, 0);
935
936   ceph_close(cmount, fd);
937
938   // create hard link
939   ASSERT_EQ(ceph_link(cmount, "f1", "hardl1"), 0);
940
941   // remove file link points to
942   ASSERT_EQ(ceph_unlink(cmount, "f1"), 0);
943
944   ceph_shutdown(cmount);
945
946   // now cleanup
947   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
948   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
949   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
950   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
951   ASSERT_EQ(ceph_chdir(cmount, dir), 0);
952   ASSERT_EQ(ceph_unlink(cmount, "hardl1"), 0);
953   ASSERT_EQ(ceph_rmdir(cmount, dir), 0);
954
955   ceph_shutdown(cmount);
956 }
957
958 TEST(LibCephFS, BadArgument) {
959   struct ceph_mount_info *cmount;
960   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
961   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
962   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
963   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
964
965   int fd = ceph_open(cmount, "test_file", O_CREAT|O_RDWR, 0666);
966   ASSERT_GT(fd, 0);
967   char buf[100];
968   ASSERT_EQ(ceph_write(cmount, fd, buf, sizeof(buf), 0), (int)sizeof(buf));
969   ASSERT_EQ(ceph_read(cmount, fd, buf, 0, 5), 0);
970   ceph_close(cmount, fd);
971   ASSERT_EQ(ceph_unlink(cmount, "test_file"), 0);
972
973   ceph_shutdown(cmount);
974 }
975
976 TEST(LibCephFS, BadFileDesc) {
977   struct ceph_mount_info *cmount;
978   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
979   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
980   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
981   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
982
983   ASSERT_EQ(ceph_fchmod(cmount, -1, 0655), -EBADF);
984   ASSERT_EQ(ceph_close(cmount, -1), -EBADF);
985   ASSERT_EQ(ceph_lseek(cmount, -1, 0, SEEK_SET), -EBADF);
986
987   char buf[0];
988   ASSERT_EQ(ceph_read(cmount, -1, buf, 0, 0), -EBADF);
989   ASSERT_EQ(ceph_write(cmount, -1, buf, 0, 0), -EBADF);
990
991   ASSERT_EQ(ceph_ftruncate(cmount, -1, 0), -EBADF);
992   ASSERT_EQ(ceph_fsync(cmount, -1, 0), -EBADF);
993
994   struct ceph_statx stx;
995   ASSERT_EQ(ceph_fstatx(cmount, -1, &stx, 0, 0), -EBADF);
996
997   struct sockaddr_storage addr;
998   ASSERT_EQ(ceph_get_file_stripe_address(cmount, -1, 0, &addr, 1), -EBADF);
999
1000   ASSERT_EQ(ceph_get_file_stripe_unit(cmount, -1), -EBADF);
1001   ASSERT_EQ(ceph_get_file_pool(cmount, -1), -EBADF);
1002   char poolname[80];
1003   ASSERT_EQ(ceph_get_file_pool_name(cmount, -1, poolname, sizeof(poolname)), -EBADF);
1004   ASSERT_EQ(ceph_get_file_replication(cmount, -1), -EBADF);
1005   ASSERT_EQ(ceph_get_file_object_size(cmount, -1), -EBADF);
1006   int stripe_unit, stripe_count, object_size, pg_pool;
1007   ASSERT_EQ(ceph_get_file_layout(cmount, -1, &stripe_unit, &stripe_count, &object_size, &pg_pool), -EBADF);
1008   ASSERT_EQ(ceph_get_file_stripe_count(cmount, -1), -EBADF);
1009
1010   ceph_shutdown(cmount);
1011 }
1012
1013 TEST(LibCephFS, ReadEmptyFile) {
1014   struct ceph_mount_info *cmount;
1015   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
1016   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
1017   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
1018   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
1019
1020   // test the read_sync path in the client for zero files
1021   ASSERT_EQ(ceph_conf_set(cmount, "client_debug_force_sync_read", "true"), 0);
1022
1023   int mypid = getpid();
1024   char testf[256];
1025
1026   sprintf(testf, "test_reademptyfile%d", mypid);
1027   int fd = ceph_open(cmount, testf, O_CREAT|O_TRUNC|O_WRONLY, 0644);
1028   ASSERT_GT(fd, 0);
1029
1030   ceph_close(cmount, fd);
1031
1032   fd = ceph_open(cmount, testf, O_RDONLY, 0);
1033   ASSERT_GT(fd, 0);
1034
1035   char buf[4096];
1036   ASSERT_EQ(ceph_read(cmount, fd, buf, 4096, 0), 0);
1037
1038   ceph_close(cmount, fd);
1039   ceph_shutdown(cmount);
1040 }
1041
1042 TEST(LibCephFS, PreadvPwritev) {
1043   struct ceph_mount_info *cmount;
1044   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
1045   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
1046   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
1047   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
1048
1049   int mypid = getpid();
1050   char testf[256];
1051
1052   sprintf(testf, "test_preadvpwritevfile%d", mypid);
1053   int fd = ceph_open(cmount, testf, O_CREAT|O_RDWR, 0666);
1054   ASSERT_GT(fd, 0);
1055
1056   char out0[] = "hello ";
1057   char out1[] = "world\n";
1058   struct iovec iov_out[2] = {
1059         {out0, sizeof(out0)},
1060         {out1, sizeof(out1)},
1061   };
1062   char in0[sizeof(out0)];
1063   char in1[sizeof(out1)];
1064   struct iovec iov_in[2] = {
1065         {in0, sizeof(in0)},
1066         {in1, sizeof(in1)},
1067   };
1068   ssize_t nwritten = iov_out[0].iov_len + iov_out[1].iov_len; 
1069   ssize_t nread = iov_in[0].iov_len + iov_in[1].iov_len; 
1070
1071   ASSERT_EQ(ceph_pwritev(cmount, fd, iov_out, 2, 0), nwritten);
1072   ASSERT_EQ(ceph_preadv(cmount, fd, iov_in, 2, 0), nread);
1073   ASSERT_EQ(0, strncmp((const char*)iov_in[0].iov_base, (const char*)iov_out[0].iov_base, iov_out[0].iov_len));
1074   ASSERT_EQ(0, strncmp((const char*)iov_in[1].iov_base, (const char*)iov_out[1].iov_base, iov_out[1].iov_len));
1075
1076   ceph_close(cmount, fd);
1077   ceph_shutdown(cmount);
1078 }
1079
1080 TEST(LibCephFS, StripeUnitGran) {
1081   struct ceph_mount_info *cmount;
1082   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
1083   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
1084   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
1085   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
1086   ASSERT_GT(ceph_get_stripe_unit_granularity(cmount), 0);
1087   ceph_shutdown(cmount);
1088 }
1089
1090 TEST(LibCephFS, Rename) {
1091   struct ceph_mount_info *cmount;
1092   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
1093   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
1094   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
1095   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
1096
1097   int mypid = getpid();
1098   char path_src[256];
1099   char path_dst[256];
1100
1101   /* make a source file */
1102   sprintf(path_src, "test_rename_src%d", mypid);
1103   int fd = ceph_open(cmount, path_src, O_CREAT|O_TRUNC|O_WRONLY, 0777);
1104   ASSERT_GT(fd, 0);
1105   ASSERT_EQ(0, ceph_close(cmount, fd));
1106
1107   /* rename to a new dest path */
1108   sprintf(path_dst, "test_rename_dst%d", mypid);
1109   ASSERT_EQ(0, ceph_rename(cmount, path_src, path_dst));
1110
1111   /* test that dest path exists */
1112   struct ceph_statx stx;
1113   ASSERT_EQ(0, ceph_statx(cmount, path_dst, &stx, 0, 0));
1114
1115   /* test that src path doesn't exist */
1116   ASSERT_EQ(-ENOENT, ceph_statx(cmount, path_src, &stx, 0, AT_SYMLINK_NOFOLLOW));
1117
1118   /* rename with non-existent source path */
1119   ASSERT_EQ(-ENOENT, ceph_rename(cmount, path_src, path_dst));
1120
1121   ASSERT_EQ(0, ceph_unlink(cmount, path_dst));
1122   ceph_shutdown(cmount);
1123 }
1124
1125 TEST(LibCephFS, UseUnmounted) {
1126   struct ceph_mount_info *cmount;
1127   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
1128   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
1129   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
1130
1131   struct statvfs stvfs;
1132   EXPECT_EQ(-ENOTCONN, ceph_statfs(cmount, "/", &stvfs));
1133   EXPECT_EQ(-ENOTCONN, ceph_get_local_osd(cmount));
1134   EXPECT_EQ(-ENOTCONN, ceph_chdir(cmount, "/"));
1135
1136   struct ceph_dir_result *dirp;
1137   EXPECT_EQ(-ENOTCONN, ceph_opendir(cmount, "/", &dirp));
1138   EXPECT_EQ(-ENOTCONN, ceph_closedir(cmount, dirp));
1139
1140   ceph_readdir(cmount, dirp);
1141   EXPECT_EQ(ENOTCONN, errno);
1142
1143   struct dirent rdent;
1144   EXPECT_EQ(-ENOTCONN, ceph_readdir_r(cmount, dirp, &rdent));
1145
1146   struct ceph_statx stx;
1147   EXPECT_EQ(-ENOTCONN, ceph_readdirplus_r(cmount, dirp, &rdent, &stx, 0, 0, NULL));
1148   EXPECT_EQ(-ENOTCONN, ceph_getdents(cmount, dirp, NULL, 0));
1149   EXPECT_EQ(-ENOTCONN, ceph_getdnames(cmount, dirp, NULL, 0));
1150   EXPECT_EQ(-ENOTCONN, ceph_telldir(cmount, dirp));
1151   EXPECT_EQ(-ENOTCONN, ceph_link(cmount, "/", "/link"));
1152   EXPECT_EQ(-ENOTCONN, ceph_unlink(cmount, "/path"));
1153   EXPECT_EQ(-ENOTCONN, ceph_rename(cmount, "/path", "/path"));
1154   EXPECT_EQ(-ENOTCONN, ceph_mkdir(cmount, "/", 0655));
1155   EXPECT_EQ(-ENOTCONN, ceph_mkdirs(cmount, "/", 0655));
1156   EXPECT_EQ(-ENOTCONN, ceph_rmdir(cmount, "/path"));
1157   EXPECT_EQ(-ENOTCONN, ceph_readlink(cmount, "/path", NULL, 0));
1158   EXPECT_EQ(-ENOTCONN, ceph_symlink(cmount, "/path", "/path"));
1159   EXPECT_EQ(-ENOTCONN, ceph_statx(cmount, "/path", &stx, 0, 0));
1160   EXPECT_EQ(-ENOTCONN, ceph_setattrx(cmount, "/path", &stx, 0, 0));
1161   EXPECT_EQ(-ENOTCONN, ceph_getxattr(cmount, "/path", "name", NULL, 0));
1162   EXPECT_EQ(-ENOTCONN, ceph_lgetxattr(cmount, "/path", "name", NULL, 0));
1163   EXPECT_EQ(-ENOTCONN, ceph_listxattr(cmount, "/path", NULL, 0));
1164   EXPECT_EQ(-ENOTCONN, ceph_llistxattr(cmount, "/path", NULL, 0));
1165   EXPECT_EQ(-ENOTCONN, ceph_removexattr(cmount, "/path", "name"));
1166   EXPECT_EQ(-ENOTCONN, ceph_lremovexattr(cmount, "/path", "name"));
1167   EXPECT_EQ(-ENOTCONN, ceph_setxattr(cmount, "/path", "name", NULL, 0, 0));
1168   EXPECT_EQ(-ENOTCONN, ceph_lsetxattr(cmount, "/path", "name", NULL, 0, 0));
1169   EXPECT_EQ(-ENOTCONN, ceph_fsetattrx(cmount, 0, &stx, 0));
1170   EXPECT_EQ(-ENOTCONN, ceph_chmod(cmount, "/path", 0));
1171   EXPECT_EQ(-ENOTCONN, ceph_fchmod(cmount, 0, 0));
1172   EXPECT_EQ(-ENOTCONN, ceph_chown(cmount, "/path", 0, 0));
1173   EXPECT_EQ(-ENOTCONN, ceph_lchown(cmount, "/path", 0, 0));
1174   EXPECT_EQ(-ENOTCONN, ceph_fchown(cmount, 0, 0, 0));
1175
1176   struct utimbuf utb;
1177   EXPECT_EQ(-ENOTCONN, ceph_utime(cmount, "/path", &utb));
1178   EXPECT_EQ(-ENOTCONN, ceph_truncate(cmount, "/path", 0));
1179   EXPECT_EQ(-ENOTCONN, ceph_mknod(cmount, "/path", 0, 0));
1180   EXPECT_EQ(-ENOTCONN, ceph_open(cmount, "/path", 0, 0));
1181   EXPECT_EQ(-ENOTCONN, ceph_open_layout(cmount, "/path", 0, 0, 0, 0, 0, "pool"));
1182   EXPECT_EQ(-ENOTCONN, ceph_close(cmount, 0));
1183   EXPECT_EQ(-ENOTCONN, ceph_lseek(cmount, 0, 0, SEEK_SET));
1184   EXPECT_EQ(-ENOTCONN, ceph_read(cmount, 0, NULL, 0, 0));
1185   EXPECT_EQ(-ENOTCONN, ceph_write(cmount, 0, NULL, 0, 0));
1186   EXPECT_EQ(-ENOTCONN, ceph_ftruncate(cmount, 0, 0));
1187   EXPECT_EQ(-ENOTCONN, ceph_fsync(cmount, 0, 0));
1188   EXPECT_EQ(-ENOTCONN, ceph_fstatx(cmount, 0, &stx, 0, 0));
1189   EXPECT_EQ(-ENOTCONN, ceph_sync_fs(cmount));
1190   EXPECT_EQ(-ENOTCONN, ceph_get_file_stripe_unit(cmount, 0));
1191   EXPECT_EQ(-ENOTCONN, ceph_get_file_stripe_count(cmount, 0));
1192   EXPECT_EQ(-ENOTCONN, ceph_get_file_layout(cmount, 0, NULL, NULL ,NULL ,NULL));
1193   EXPECT_EQ(-ENOTCONN, ceph_get_file_object_size(cmount, 0));
1194   EXPECT_EQ(-ENOTCONN, ceph_get_file_pool(cmount, 0));
1195   EXPECT_EQ(-ENOTCONN, ceph_get_file_pool_name(cmount, 0, NULL, 0));
1196   EXPECT_EQ(-ENOTCONN, ceph_get_file_replication(cmount, 0));
1197   EXPECT_EQ(-ENOTCONN, ceph_get_path_replication(cmount, "/path"));
1198   EXPECT_EQ(-ENOTCONN, ceph_get_path_layout(cmount, "/path", NULL, NULL, NULL, NULL));
1199   EXPECT_EQ(-ENOTCONN, ceph_get_path_object_size(cmount, "/path"));
1200   EXPECT_EQ(-ENOTCONN, ceph_get_path_stripe_count(cmount, "/path"));
1201   EXPECT_EQ(-ENOTCONN, ceph_get_path_stripe_unit(cmount, "/path"));
1202   EXPECT_EQ(-ENOTCONN, ceph_get_path_pool(cmount, "/path"));
1203   EXPECT_EQ(-ENOTCONN, ceph_get_path_pool_name(cmount, "/path", NULL, 0));
1204   EXPECT_EQ(-ENOTCONN, ceph_get_pool_name(cmount, 0, NULL, 0));
1205   EXPECT_EQ(-ENOTCONN, ceph_get_file_stripe_address(cmount, 0, 0, NULL, 0));
1206   EXPECT_EQ(-ENOTCONN, ceph_localize_reads(cmount, 0));
1207   EXPECT_EQ(-ENOTCONN, ceph_debug_get_fd_caps(cmount, 0));
1208   EXPECT_EQ(-ENOTCONN, ceph_debug_get_file_caps(cmount, "/path"));
1209   EXPECT_EQ(-ENOTCONN, ceph_get_stripe_unit_granularity(cmount));
1210   EXPECT_EQ(-ENOTCONN, ceph_get_pool_id(cmount, "data"));
1211   EXPECT_EQ(-ENOTCONN, ceph_get_pool_replication(cmount, 1));
1212
1213   ceph_release(cmount);
1214 }
1215
1216 TEST(LibCephFS, GetPoolId) {
1217   struct ceph_mount_info *cmount;
1218   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
1219   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
1220   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
1221   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
1222
1223   char name[80];
1224   memset(name, 0, sizeof(name));
1225   ASSERT_LE(0, ceph_get_path_pool_name(cmount, "/", name, sizeof(name)));
1226   ASSERT_GE(ceph_get_pool_id(cmount, name), 0);
1227   ASSERT_EQ(ceph_get_pool_id(cmount, "weflkjwelfjwlkejf"), -ENOENT);
1228
1229   ceph_shutdown(cmount);
1230 }
1231
1232 TEST(LibCephFS, GetPoolReplication) {
1233   struct ceph_mount_info *cmount;
1234   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
1235   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
1236   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
1237   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
1238
1239   /* negative pools */
1240   ASSERT_EQ(ceph_get_pool_replication(cmount, -10), -ENOENT);
1241
1242   /* valid pool */
1243   int pool_id;
1244   int stripe_unit, stripe_count, object_size;
1245   ASSERT_EQ(0, ceph_get_path_layout(cmount, "/", &stripe_unit, &stripe_count,
1246                                     &object_size, &pool_id));
1247   ASSERT_GE(pool_id, 0);
1248   ASSERT_GT(ceph_get_pool_replication(cmount, pool_id), 0);
1249
1250   ceph_shutdown(cmount);
1251 }
1252
1253 TEST(LibCephFS, GetExtentOsds) {
1254   struct ceph_mount_info *cmount;
1255   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
1256
1257   EXPECT_EQ(-ENOTCONN, ceph_get_file_extent_osds(cmount, 0, 0, NULL, NULL, 0));
1258
1259   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
1260   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
1261   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
1262
1263   int stripe_unit = (1<<18);
1264
1265   /* make a file! */
1266   char test_file[256];
1267   sprintf(test_file, "test_extent_osds_%d", getpid());
1268   int fd = ceph_open_layout(cmount, test_file, O_CREAT|O_RDWR, 0666,
1269       stripe_unit, 2, stripe_unit*2, NULL);
1270   ASSERT_GT(fd, 0);
1271
1272   /* get back how many osds > 0 */
1273   int ret = ceph_get_file_extent_osds(cmount, fd, 0, NULL, NULL, 0);
1274   EXPECT_GT(ret, 0);
1275
1276   int64_t len;
1277   int osds[ret];
1278
1279   /* full stripe extent */
1280   EXPECT_EQ(ret, ceph_get_file_extent_osds(cmount, fd, 0, &len, osds, ret));
1281   EXPECT_EQ(len, (int64_t)stripe_unit);
1282
1283   /* half stripe extent */
1284   EXPECT_EQ(ret, ceph_get_file_extent_osds(cmount, fd, stripe_unit/2, &len, osds, ret));
1285   EXPECT_EQ(len, (int64_t)stripe_unit/2);
1286
1287   /* 1.5 stripe unit offset -1 byte */
1288   EXPECT_EQ(ret, ceph_get_file_extent_osds(cmount, fd, 3*stripe_unit/2-1, &len, osds, ret));
1289   EXPECT_EQ(len, (int64_t)stripe_unit/2+1);
1290
1291   /* 1.5 stripe unit offset +1 byte */
1292   EXPECT_EQ(ret, ceph_get_file_extent_osds(cmount, fd, 3*stripe_unit/2+1, &len, osds, ret));
1293   EXPECT_EQ(len, (int64_t)stripe_unit/2-1);
1294
1295   /* only when more than 1 osd */
1296   if (ret > 1) {
1297     EXPECT_EQ(-ERANGE, ceph_get_file_extent_osds(cmount, fd, 0, NULL, osds, 1));
1298   }
1299
1300   ceph_close(cmount, fd);
1301
1302   ceph_shutdown(cmount);
1303 }
1304
1305 TEST(LibCephFS, GetOsdCrushLocation) {
1306   struct ceph_mount_info *cmount;
1307   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
1308
1309   EXPECT_EQ(-ENOTCONN, ceph_get_osd_crush_location(cmount, 0, NULL, 0));
1310
1311   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
1312   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
1313   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
1314
1315   ASSERT_EQ(ceph_get_osd_crush_location(cmount, 0, NULL, 1), -EINVAL);
1316
1317   char path[256];
1318   ASSERT_EQ(ceph_get_osd_crush_location(cmount, 9999999, path, 0), -ENOENT);
1319   ASSERT_EQ(ceph_get_osd_crush_location(cmount, -1, path, 0), -EINVAL);
1320
1321   char test_file[256];
1322   sprintf(test_file, "test_osds_loc_%d", getpid());
1323   int fd = ceph_open(cmount, test_file, O_CREAT|O_RDWR, 0666);
1324   ASSERT_GT(fd, 0);
1325
1326   /* get back how many osds > 0 */
1327   int ret = ceph_get_file_extent_osds(cmount, fd, 0, NULL, NULL, 0);
1328   EXPECT_GT(ret, 0);
1329
1330   /* full stripe extent */
1331   int osds[ret];
1332   EXPECT_EQ(ret, ceph_get_file_extent_osds(cmount, fd, 0, NULL, osds, ret));
1333
1334   ASSERT_GT(ceph_get_osd_crush_location(cmount, 0, path, 0), 0);
1335   ASSERT_EQ(ceph_get_osd_crush_location(cmount, 0, path, 1), -ERANGE);
1336
1337   for (int i = 0; i < ret; i++) {
1338     int len = ceph_get_osd_crush_location(cmount, osds[i], path, sizeof(path));
1339     ASSERT_GT(len, 0);
1340     int pos = 0;
1341     while (pos < len) {
1342       std::string type(path + pos);
1343       ASSERT_GT((int)type.size(), 0);
1344       pos += type.size() + 1;
1345
1346       std::string name(path + pos);
1347       ASSERT_GT((int)name.size(), 0);
1348       pos += name.size() + 1;
1349     }
1350   }
1351
1352   ceph_close(cmount, fd);
1353   ceph_shutdown(cmount);
1354 }
1355
1356 TEST(LibCephFS, GetOsdAddr) {
1357   struct ceph_mount_info *cmount;
1358   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
1359
1360   EXPECT_EQ(-ENOTCONN, ceph_get_osd_addr(cmount, 0, NULL));
1361
1362   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
1363   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
1364   ASSERT_EQ(ceph_mount(cmount, NULL), 0);
1365
1366   ASSERT_EQ(-EINVAL, ceph_get_osd_addr(cmount, 0, NULL));
1367
1368   struct sockaddr_storage addr;
1369   ASSERT_EQ(-ENOENT, ceph_get_osd_addr(cmount, -1, &addr));
1370   ASSERT_EQ(-ENOENT, ceph_get_osd_addr(cmount, 9999999, &addr));
1371
1372   ASSERT_EQ(0, ceph_get_osd_addr(cmount, 0, &addr));
1373
1374   ceph_shutdown(cmount);
1375 }
1376
1377 TEST(LibCephFS, OpenNoClose) {
1378   struct ceph_mount_info *cmount;
1379   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
1380   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
1381   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
1382   ASSERT_EQ(ceph_mount(cmount, "/"), 0);
1383
1384   pid_t mypid = getpid();
1385   char str_buf[256];
1386   sprintf(str_buf, "open_no_close_dir%d", mypid);
1387   ASSERT_EQ(0, ceph_mkdirs(cmount, str_buf, 0777));
1388
1389   struct ceph_dir_result *ls_dir = NULL;
1390   ASSERT_EQ(ceph_opendir(cmount, str_buf, &ls_dir), 0);
1391
1392   sprintf(str_buf, "open_no_close_file%d", mypid);
1393   int fd = ceph_open(cmount, str_buf, O_RDONLY|O_CREAT, 0666);
1394   ASSERT_LT(0, fd);
1395
1396   // shutdown should force close opened file/dir
1397   ceph_shutdown(cmount);
1398 }
1399
1400 TEST(LibCephFS, Nlink) {
1401   struct ceph_mount_info *cmount;
1402   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
1403   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
1404   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
1405   ASSERT_EQ(ceph_mount(cmount, "/"), 0);
1406
1407   Inode *root, *dir, *file;
1408
1409   ASSERT_EQ(ceph_ll_lookup_root(cmount, &root), 0);
1410
1411   char dirname[32], filename[32], linkname[32];
1412   sprintf(dirname, "nlinkdir%x", getpid());
1413   sprintf(filename, "nlinkorig%x", getpid());
1414   sprintf(linkname, "nlinklink%x", getpid());
1415
1416   struct ceph_statx stx;
1417   Fh *fh;
1418   UserPerm *perms = ceph_mount_perms(cmount);
1419
1420   ASSERT_EQ(ceph_ll_mkdir(cmount, root, dirname, 0755, &dir, &stx, 0, 0, perms), 0);
1421   ASSERT_EQ(ceph_ll_create(cmount, dir, filename, 0666, O_RDWR|O_CREAT|O_EXCL,
1422                            &file, &fh, &stx, CEPH_STATX_NLINK, 0, perms), 0);
1423   ASSERT_EQ(ceph_ll_close(cmount, fh), 0);
1424   ASSERT_EQ(stx.stx_nlink, (nlink_t)1);
1425
1426   ASSERT_EQ(ceph_ll_link(cmount, file, dir, linkname, perms), 0);
1427   ASSERT_EQ(ceph_ll_getattr(cmount, file, &stx, CEPH_STATX_NLINK, 0, perms), 0);
1428   ASSERT_EQ(stx.stx_nlink, (nlink_t)2);
1429
1430   ASSERT_EQ(ceph_ll_unlink(cmount, dir, linkname, perms), 0);
1431   ASSERT_EQ(ceph_ll_lookup(cmount, dir, filename, &file, &stx,
1432                            CEPH_STATX_NLINK, 0, perms), 0);
1433   ASSERT_EQ(stx.stx_nlink, (nlink_t)1);
1434
1435   ceph_shutdown(cmount);
1436 }
1437
1438 TEST(LibCephFS, SlashDotDot) {
1439   struct ceph_mount_info *cmount;
1440   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
1441   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
1442   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
1443   ASSERT_EQ(ceph_mount(cmount, "/"), 0);
1444
1445   struct ceph_statx     stx;
1446   ASSERT_EQ(ceph_statx(cmount, "/.", &stx, CEPH_STATX_INO, 0), 0);
1447
1448   ino_t ino = stx.stx_ino;
1449   ASSERT_EQ(ceph_statx(cmount, "/..", &stx, CEPH_STATX_INO, 0), 0);
1450
1451   /* At root, "." and ".." should be the same inode */
1452   ASSERT_EQ(ino, stx.stx_ino);
1453
1454   /* Test accessing the parent of an unlinked directory */
1455   char dir1[32], dir2[32];
1456   sprintf(dir1, "/sldotdot%x", getpid());
1457   sprintf(dir2, "%s/sub%x", dir1, getpid());
1458
1459   ASSERT_EQ(ceph_mkdir(cmount, dir1, 0755), 0);
1460   ASSERT_EQ(ceph_mkdir(cmount, dir2, 0755), 0);
1461
1462   ASSERT_EQ(ceph_chdir(cmount, dir2), 0);
1463
1464   /* Test behavior when unlinking cwd */
1465   struct ceph_dir_result *rdir;
1466   ASSERT_EQ(ceph_opendir(cmount, ".", &rdir), 0);
1467   ASSERT_EQ(ceph_rmdir(cmount, dir2), 0);
1468
1469   /* get "." entry */
1470   struct dirent *result = ceph_readdir(cmount, rdir);
1471   ino = result->d_ino;
1472
1473   /* get ".." entry */
1474   result = ceph_readdir(cmount, rdir);
1475   ASSERT_EQ(ino, result->d_ino);
1476   ceph_closedir(cmount, rdir);
1477
1478   /* Make sure it works same way when mounting subtree */
1479   ASSERT_EQ(ceph_unmount(cmount), 0);
1480   ASSERT_EQ(ceph_mount(cmount, dir1), 0);
1481   ASSERT_EQ(ceph_statx(cmount, "/..", &stx, CEPH_STATX_INO, 0), 0);
1482
1483   /* Test readdir behavior */
1484   ASSERT_EQ(ceph_opendir(cmount, "/", &rdir), 0);
1485   result = ceph_readdir(cmount, rdir);
1486   ASSERT_TRUE(result != NULL);
1487   ASSERT_STREQ(result->d_name, ".");
1488   ino = result->d_ino;
1489   result = ceph_readdir(cmount, rdir);
1490   ASSERT_TRUE(result != NULL);
1491   ASSERT_STREQ(result->d_name, "..");
1492   ASSERT_EQ(ino, result->d_ino);
1493
1494   ceph_shutdown(cmount);
1495 }
1496
1497 static inline bool
1498 timespec_eq(timespec const& lhs, timespec const& rhs)
1499 {
1500   return lhs.tv_sec == rhs.tv_sec && lhs.tv_nsec == rhs.tv_nsec;
1501 }
1502
1503 TEST(LibCephFS, Btime) {
1504   struct ceph_mount_info *cmount;
1505   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
1506   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
1507   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
1508   ASSERT_EQ(ceph_mount(cmount, "/"), 0);
1509
1510   char filename[32];
1511   sprintf(filename, "/getattrx%x", getpid());
1512
1513   ceph_unlink(cmount, filename);
1514   int fd = ceph_open(cmount, filename, O_RDWR|O_CREAT|O_EXCL, 0666);
1515   ASSERT_LT(0, fd);
1516
1517   /* make sure fstatx works */
1518   struct ceph_statx     stx;
1519
1520   ASSERT_EQ(ceph_fstatx(cmount, fd, &stx, CEPH_STATX_CTIME|CEPH_STATX_BTIME, 0), 0);
1521   ASSERT_TRUE(stx.stx_mask & (CEPH_STATX_CTIME|CEPH_STATX_BTIME));
1522   ASSERT_TRUE(timespec_eq(stx.stx_ctime, stx.stx_btime));
1523   ceph_close(cmount, fd);
1524
1525   ASSERT_EQ(ceph_statx(cmount, filename, &stx, CEPH_STATX_CTIME|CEPH_STATX_BTIME, 0), 0);
1526   ASSERT_TRUE(timespec_eq(stx.stx_ctime, stx.stx_btime));
1527   ASSERT_TRUE(stx.stx_mask & (CEPH_STATX_CTIME|CEPH_STATX_BTIME));
1528
1529   struct timespec old_btime = stx.stx_btime;
1530
1531   /* Now sleep, do a chmod and verify that the ctime changed, but btime didn't */
1532   sleep(1);
1533   ASSERT_EQ(ceph_chmod(cmount, filename, 0644), 0);
1534   ASSERT_EQ(ceph_statx(cmount, filename, &stx, CEPH_STATX_CTIME|CEPH_STATX_BTIME, 0), 0);
1535   ASSERT_TRUE(stx.stx_mask & CEPH_STATX_BTIME);
1536   ASSERT_TRUE(timespec_eq(stx.stx_btime, old_btime));
1537   ASSERT_FALSE(timespec_eq(stx.stx_ctime, stx.stx_btime));
1538
1539   ceph_shutdown(cmount);
1540 }
1541
1542 TEST(LibCephFS, SetBtime) {
1543   struct ceph_mount_info *cmount;
1544   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
1545   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
1546   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
1547   ASSERT_EQ(ceph_mount(cmount, "/"), 0);
1548
1549   char filename[32];
1550   sprintf(filename, "/setbtime%x", getpid());
1551
1552   ceph_unlink(cmount, filename);
1553   int fd = ceph_open(cmount, filename, O_RDWR|O_CREAT|O_EXCL, 0666);
1554   ASSERT_LT(0, fd);
1555   ceph_close(cmount, fd);
1556
1557   struct ceph_statx stx;
1558   struct timespec old_btime = { 1, 2 };
1559
1560   stx.stx_btime = old_btime;
1561
1562   ASSERT_EQ(ceph_setattrx(cmount, filename, &stx, CEPH_SETATTR_BTIME, 0), 0);
1563
1564   ASSERT_EQ(ceph_statx(cmount, filename, &stx, CEPH_STATX_BTIME, 0), 0);
1565   ASSERT_TRUE(stx.stx_mask & CEPH_STATX_BTIME);
1566   ASSERT_TRUE(timespec_eq(stx.stx_btime, old_btime));
1567
1568   ceph_shutdown(cmount);
1569 }
1570
1571 TEST(LibCephFS, LazyStatx) {
1572   struct ceph_mount_info *cmount1, *cmount2;
1573   ASSERT_EQ(ceph_create(&cmount1, NULL), 0);
1574   ASSERT_EQ(ceph_create(&cmount2, NULL), 0);
1575   ASSERT_EQ(ceph_conf_read_file(cmount1, NULL), 0);
1576   ASSERT_EQ(ceph_conf_read_file(cmount2, NULL), 0);
1577   ASSERT_EQ(0, ceph_conf_parse_env(cmount1, NULL));
1578   ASSERT_EQ(0, ceph_conf_parse_env(cmount2, NULL));
1579   ASSERT_EQ(ceph_mount(cmount1, "/"), 0);
1580   ASSERT_EQ(ceph_mount(cmount2, "/"), 0);
1581
1582   char filename[32];
1583   sprintf(filename, "lazystatx%x", getpid());
1584
1585   Inode *root1, *file1, *root2, *file2;
1586   struct ceph_statx stx;
1587   Fh *fh;
1588   UserPerm *perms1 = ceph_mount_perms(cmount1);
1589   UserPerm *perms2 = ceph_mount_perms(cmount2);
1590
1591   ASSERT_EQ(ceph_ll_lookup_root(cmount1, &root1), 0);
1592   ceph_ll_unlink(cmount1, root1, filename, perms1);
1593   ASSERT_EQ(ceph_ll_create(cmount1, root1, filename, 0666, O_RDWR|O_CREAT|O_EXCL,
1594                            &file1, &fh, &stx, 0, 0, perms1), 0);
1595   ASSERT_EQ(ceph_ll_close(cmount1, fh), 0);
1596
1597   ASSERT_EQ(ceph_ll_lookup_root(cmount2, &root2), 0);
1598
1599   ASSERT_EQ(ceph_ll_lookup(cmount2, root2, filename, &file2, &stx, CEPH_STATX_CTIME, 0, perms2), 0);
1600
1601   struct timespec old_ctime = stx.stx_ctime;
1602
1603   /*
1604    * Now sleep, do a chmod on the first client and the see whether we get a
1605    * different ctime with a statx that uses AT_NO_ATTR_SYNC
1606    */
1607   sleep(1);
1608   stx.stx_mode = 0644;
1609   ASSERT_EQ(ceph_ll_setattr(cmount1, file1, &stx, CEPH_SETATTR_MODE, perms1), 0);
1610
1611   ASSERT_EQ(ceph_ll_getattr(cmount2, file2, &stx, CEPH_STATX_CTIME, AT_NO_ATTR_SYNC, perms2), 0);
1612   ASSERT_TRUE(stx.stx_mask & CEPH_STATX_CTIME);
1613   ASSERT_TRUE(stx.stx_ctime.tv_sec == old_ctime.tv_sec &&
1614               stx.stx_ctime.tv_nsec == old_ctime.tv_nsec);
1615
1616   ceph_shutdown(cmount1);
1617   ceph_shutdown(cmount2);
1618 }
1619
1620 TEST(LibCephFS, ChangeAttr) {
1621   struct ceph_mount_info *cmount;
1622   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
1623   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
1624   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
1625   ASSERT_EQ(ceph_mount(cmount, "/"), 0);
1626
1627   char filename[32];
1628   sprintf(filename, "/changeattr%x", getpid());
1629
1630   ceph_unlink(cmount, filename);
1631   int fd = ceph_open(cmount, filename, O_RDWR|O_CREAT|O_EXCL, 0666);
1632   ASSERT_LT(0, fd);
1633
1634   struct ceph_statx     stx;
1635   ASSERT_EQ(ceph_statx(cmount, filename, &stx, CEPH_STATX_VERSION, 0), 0);
1636   ASSERT_TRUE(stx.stx_mask & CEPH_STATX_VERSION);
1637
1638   uint64_t old_change_attr = stx.stx_version;
1639
1640   /* do chmod, and check whether change_attr changed */
1641   ASSERT_EQ(ceph_chmod(cmount, filename, 0644), 0);
1642   ASSERT_EQ(ceph_statx(cmount, filename, &stx, CEPH_STATX_VERSION, 0), 0);
1643   ASSERT_TRUE(stx.stx_mask & CEPH_STATX_VERSION);
1644   ASSERT_NE(stx.stx_version, old_change_attr);
1645   old_change_attr = stx.stx_version;
1646
1647   /* now do a write and see if it changed again */
1648   ASSERT_EQ(3, ceph_write(cmount, fd, "foo", 3, 0));
1649   ASSERT_EQ(ceph_statx(cmount, filename, &stx, CEPH_STATX_VERSION, 0), 0);
1650   ASSERT_TRUE(stx.stx_mask & CEPH_STATX_VERSION);
1651   ASSERT_NE(stx.stx_version, old_change_attr);
1652   old_change_attr = stx.stx_version;
1653
1654   /* Now truncate and check again */
1655   ASSERT_EQ(0, ceph_ftruncate(cmount, fd, 0));
1656   ASSERT_EQ(ceph_statx(cmount, filename, &stx, CEPH_STATX_VERSION, 0), 0);
1657   ASSERT_TRUE(stx.stx_mask & CEPH_STATX_VERSION);
1658   ASSERT_NE(stx.stx_version, old_change_attr);
1659
1660   ceph_close(cmount, fd);
1661   ceph_shutdown(cmount);
1662 }
1663
1664 TEST(LibCephFS, DirChangeAttr) {
1665   struct ceph_mount_info *cmount;
1666   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
1667   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
1668   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
1669   ASSERT_EQ(ceph_mount(cmount, "/"), 0);
1670
1671   char dirname[32], filename[32];
1672   sprintf(dirname, "/dirchange%x", getpid());
1673   sprintf(filename, "%s/foo", dirname);
1674
1675   ASSERT_EQ(ceph_mkdir(cmount, dirname, 0755), 0);
1676
1677   struct ceph_statx     stx;
1678   ASSERT_EQ(ceph_statx(cmount, dirname, &stx, CEPH_STATX_VERSION, 0), 0);
1679   ASSERT_TRUE(stx.stx_mask & CEPH_STATX_VERSION);
1680
1681   uint64_t old_change_attr = stx.stx_version;
1682
1683   int fd = ceph_open(cmount, filename, O_RDWR|O_CREAT|O_EXCL, 0666);
1684   ASSERT_LT(0, fd);
1685   ceph_close(cmount, fd);
1686
1687   ASSERT_EQ(ceph_statx(cmount, dirname, &stx, CEPH_STATX_VERSION, 0), 0);
1688   ASSERT_TRUE(stx.stx_mask & CEPH_STATX_VERSION);
1689   ASSERT_NE(stx.stx_version, old_change_attr);
1690
1691   old_change_attr = stx.stx_version;
1692
1693   ASSERT_EQ(ceph_unlink(cmount, filename), 0);
1694   ASSERT_EQ(ceph_statx(cmount, dirname, &stx, CEPH_STATX_VERSION, 0), 0);
1695   ASSERT_TRUE(stx.stx_mask & CEPH_STATX_VERSION);
1696   ASSERT_NE(stx.stx_version, old_change_attr);
1697
1698   ceph_shutdown(cmount);
1699 }
1700
1701 TEST(LibCephFS, SetSize) {
1702   struct ceph_mount_info *cmount;
1703   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
1704   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
1705   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
1706   ASSERT_EQ(ceph_mount(cmount, "/"), 0);
1707
1708   char filename[32];
1709   sprintf(filename, "/setsize%x", getpid());
1710
1711   ceph_unlink(cmount, filename);
1712   int fd = ceph_open(cmount, filename, O_RDWR|O_CREAT|O_EXCL, 0666);
1713   ASSERT_LT(0, fd);
1714
1715   struct ceph_statx stx;
1716   uint64_t size = 8388608;
1717   stx.stx_size = size;
1718   ASSERT_EQ(ceph_fsetattrx(cmount, fd, &stx, CEPH_SETATTR_SIZE), 0);
1719   ASSERT_EQ(ceph_fstatx(cmount, fd, &stx, CEPH_STATX_SIZE, 0), 0);
1720   ASSERT_EQ(stx.stx_size, size);
1721
1722   ceph_close(cmount, fd);
1723   ceph_shutdown(cmount);
1724 }
1725
1726 TEST(LibCephFS, ClearSetuid) {
1727   struct ceph_mount_info *cmount;
1728   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
1729   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
1730   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
1731   ASSERT_EQ(ceph_mount(cmount, "/"), 0);
1732
1733   Inode *root;
1734   ASSERT_EQ(ceph_ll_lookup_root(cmount, &root), 0);
1735
1736   char filename[32];
1737   sprintf(filename, "clearsetuid%x", getpid());
1738
1739   Fh *fh;
1740   Inode *in;
1741   struct ceph_statx stx;
1742   const mode_t after_mode = S_IRWXU;
1743   const mode_t before_mode = S_IRWXU | S_ISUID | S_ISGID;
1744   const unsigned want = CEPH_STATX_UID|CEPH_STATX_GID|CEPH_STATX_MODE;
1745   UserPerm *usercred = ceph_mount_perms(cmount);
1746
1747   ceph_ll_unlink(cmount, root, filename, usercred);
1748   ASSERT_EQ(ceph_ll_create(cmount, root, filename, before_mode,
1749                            O_RDWR|O_CREAT|O_EXCL, &in, &fh, &stx, want, 0,
1750                            usercred), 0);
1751
1752   ASSERT_EQ(stx.stx_mode & (mode_t)ALLPERMS, before_mode);
1753
1754   // write
1755   ASSERT_EQ(ceph_ll_write(cmount, fh, 0, 3, "foo"), 3);
1756   ASSERT_EQ(ceph_ll_getattr(cmount, in, &stx, CEPH_STATX_MODE, 0, usercred), 0);
1757   ASSERT_TRUE(stx.stx_mask & CEPH_STATX_MODE);
1758   ASSERT_EQ(stx.stx_mode & (mode_t)ALLPERMS, after_mode);
1759
1760   // reset mode
1761   stx.stx_mode = before_mode;
1762   ASSERT_EQ(ceph_ll_setattr(cmount, in, &stx, CEPH_STATX_MODE, usercred), 0);
1763   ASSERT_EQ(ceph_ll_getattr(cmount, in, &stx, CEPH_STATX_MODE, 0, usercred), 0);
1764   ASSERT_TRUE(stx.stx_mask & CEPH_STATX_MODE);
1765   ASSERT_EQ(stx.stx_mode & (mode_t)ALLPERMS, before_mode);
1766
1767   // truncate
1768   stx.stx_size = 1;
1769   ASSERT_EQ(ceph_ll_setattr(cmount, in, &stx, CEPH_SETATTR_SIZE, usercred), 0);
1770   ASSERT_EQ(ceph_ll_getattr(cmount, in, &stx, CEPH_STATX_MODE, 0, usercred), 0);
1771   ASSERT_TRUE(stx.stx_mask & CEPH_STATX_MODE);
1772   ASSERT_EQ(stx.stx_mode & (mode_t)ALLPERMS, after_mode);
1773
1774   // reset mode
1775   stx.stx_mode = before_mode;
1776   ASSERT_EQ(ceph_ll_setattr(cmount, in, &stx, CEPH_STATX_MODE, usercred), 0);
1777   ASSERT_EQ(ceph_ll_getattr(cmount, in, &stx, CEPH_STATX_MODE, 0, usercred), 0);
1778   ASSERT_TRUE(stx.stx_mask & CEPH_STATX_MODE);
1779   ASSERT_EQ(stx.stx_mode & (mode_t)ALLPERMS, before_mode);
1780
1781   // chown  -- for this we need to be "root"
1782   UserPerm *rootcred = ceph_userperm_new(0, 0, 0, NULL);
1783   ASSERT_TRUE(rootcred);
1784   stx.stx_uid++;
1785   stx.stx_gid++;
1786   ASSERT_EQ(ceph_ll_setattr(cmount, in, &stx, CEPH_SETATTR_UID|CEPH_SETATTR_GID, rootcred), 0);
1787   ASSERT_EQ(ceph_ll_getattr(cmount, in, &stx, CEPH_STATX_MODE, 0, usercred), 0);
1788   ASSERT_TRUE(stx.stx_mask & CEPH_STATX_MODE);
1789   ASSERT_EQ(stx.stx_mode & (mode_t)ALLPERMS, after_mode);
1790
1791   /* test chown with supplementary groups, and chown with/without exe bit */
1792   uid_t u = 65534;
1793   gid_t g = 65534;
1794   gid_t gids[] = {65533,65532};
1795   UserPerm *altcred = ceph_userperm_new(u, g, sizeof gids / sizeof gids[0], gids);
1796   stx.stx_uid = u;
1797   stx.stx_gid = g;
1798   mode_t m = S_ISGID|S_ISUID|S_IRUSR|S_IWUSR;
1799   stx.stx_mode = m;
1800   ASSERT_EQ(ceph_ll_setattr(cmount, in, &stx, CEPH_STATX_MODE|CEPH_SETATTR_UID|CEPH_SETATTR_GID, rootcred), 0);
1801   ASSERT_EQ(ceph_ll_getattr(cmount, in, &stx, CEPH_STATX_MODE, 0, altcred), 0);
1802   ASSERT_EQ(stx.stx_mode&(mode_t)ALLPERMS, m);
1803   /* not dropped without exe bit */
1804   stx.stx_gid = gids[0];
1805   ASSERT_EQ(ceph_ll_setattr(cmount, in, &stx, CEPH_SETATTR_GID, altcred), 0);
1806   ASSERT_EQ(ceph_ll_getattr(cmount, in, &stx, CEPH_STATX_MODE, 0, altcred), 0);
1807   ASSERT_EQ(stx.stx_mode&(mode_t)ALLPERMS, m);
1808   /* now check dropped with exe bit */
1809   m = S_ISGID|S_ISUID|S_IRWXU;
1810   stx.stx_mode = m;
1811   ASSERT_EQ(ceph_ll_setattr(cmount, in, &stx, CEPH_STATX_MODE, altcred), 0);
1812   ASSERT_EQ(ceph_ll_getattr(cmount, in, &stx, CEPH_STATX_MODE, 0, altcred), 0);
1813   ASSERT_EQ(stx.stx_mode&(mode_t)ALLPERMS, m);
1814   stx.stx_gid = gids[1];
1815   ASSERT_EQ(ceph_ll_setattr(cmount, in, &stx, CEPH_SETATTR_GID, altcred), 0);
1816   ASSERT_EQ(ceph_ll_getattr(cmount, in, &stx, CEPH_STATX_MODE, 0, altcred), 0);
1817   ASSERT_EQ(stx.stx_mode&(mode_t)ALLPERMS, m&(S_IRWXU|S_IRWXG|S_IRWXO));
1818   ceph_userperm_destroy(altcred);
1819
1820   ASSERT_EQ(ceph_ll_close(cmount, fh), 0);
1821   ceph_shutdown(cmount);
1822 }
1823
1824 TEST(LibCephFS, OperationsOnRoot)
1825 {
1826   struct ceph_mount_info *cmount;
1827   ASSERT_EQ(ceph_create(&cmount, NULL), 0);
1828   ASSERT_EQ(ceph_conf_read_file(cmount, NULL), 0);
1829   ASSERT_EQ(0, ceph_conf_parse_env(cmount, NULL));
1830   ASSERT_EQ(ceph_mount(cmount, "/"), 0);
1831
1832   char dirname[32];
1833   sprintf(dirname, "/somedir%x", getpid());
1834
1835   ASSERT_EQ(ceph_mkdir(cmount, dirname, 0755), 0);
1836
1837   ASSERT_EQ(ceph_rmdir(cmount, "/"), -EBUSY);
1838
1839   ASSERT_EQ(ceph_link(cmount, "/", "/"), -EEXIST);
1840   ASSERT_EQ(ceph_link(cmount, dirname, "/"), -EEXIST);
1841   ASSERT_EQ(ceph_link(cmount, "nonExisitingDir", "/"), -ENOENT);
1842
1843   ASSERT_EQ(ceph_unlink(cmount, "/"), -EISDIR);
1844
1845   ASSERT_EQ(ceph_rename(cmount, "/", "/"), -EBUSY);
1846   ASSERT_EQ(ceph_rename(cmount, dirname, "/"), -EBUSY);
1847   ASSERT_EQ(ceph_rename(cmount, "nonExistingDir", "/"), -EBUSY);
1848   ASSERT_EQ(ceph_rename(cmount, "/", dirname), -EBUSY);
1849   ASSERT_EQ(ceph_rename(cmount, "/", "nonExistingDir"), -EBUSY);
1850
1851   ASSERT_EQ(ceph_mkdir(cmount, "/", 0777), -EEXIST);
1852
1853   ASSERT_EQ(ceph_mknod(cmount, "/", 0, 0), -EEXIST);
1854
1855   ASSERT_EQ(ceph_symlink(cmount, "/", "/"), -EEXIST);
1856   ASSERT_EQ(ceph_symlink(cmount, dirname, "/"), -EEXIST);
1857   ASSERT_EQ(ceph_symlink(cmount, "nonExistingDir", "/"), -EEXIST);
1858
1859   ceph_shutdown(cmount);
1860 }