Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / os / filestore / ZFSFileStoreBackend.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include "include/int_types.h"
5 #include "include/types.h"
6
7 #include <unistd.h>
8 #include <fcntl.h>
9 #include <errno.h>
10 #include <stdlib.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <sys/ioctl.h>
14
15 #include "include/compat.h"
16 #include "include/linux_fiemap.h"
17 #include "include/color.h"
18 #include "include/buffer.h"
19 #include "include/assert.h"
20
21 #include <iostream>
22 #include <fstream>
23 #include <sstream>
24
25 #include "common/errno.h"
26 #include "common/config.h"
27 #include "common/sync_filesystem.h"
28
29 #include "ZFSFileStoreBackend.h"
30
31 #define dout_context cct()
32 #define dout_subsys ceph_subsys_filestore
33 #undef dout_prefix
34 #define dout_prefix *_dout << "zfsfilestorebackend(" << get_basedir_path() << ") "
35
36 ZFSFileStoreBackend::ZFSFileStoreBackend(FileStore *fs) :
37   GenericFileStoreBackend(fs), base_zh(NULL), current_zh(NULL),
38   m_filestore_zfs_snap(cct()->_conf->filestore_zfs_snap)
39 {
40   int ret = zfs.init();
41   if (ret < 0) {
42     dout(0) << "ZFSFileStoreBackend: failed to init libzfs" << dendl;
43     return;
44   }
45
46   base_zh = zfs.path_to_zhandle(get_basedir_path().c_str(), ZFS::TYPE_FILESYSTEM);
47   if (!base_zh) {
48     dout(0) << "ZFSFileStoreBackend: failed to get zfs handler for basedir" << dendl;
49     return;
50   }
51
52   update_current_zh();
53 }
54
55 ZFSFileStoreBackend::~ZFSFileStoreBackend()
56 {
57   if (base_zh)
58     zfs.close(base_zh);
59   if (current_zh)
60     zfs.close(current_zh);
61 }
62
63 int ZFSFileStoreBackend::update_current_zh()
64 {
65   char path[PATH_MAX];
66   snprintf(path, sizeof(path), "%s/current", zfs.get_name(base_zh));
67   ZFS::Handle *zh = zfs.open(path, ZFS::TYPE_FILESYSTEM);
68   if (zh) {
69     char *mnt;
70     if (zfs.is_mounted(zh, &mnt)) {
71       int ret = get_current_path() == mnt;
72       free(mnt);
73       if (ret) {
74         current_zh = zh;
75         return 0;
76       }
77     } else {
78       int ret = zfs.mount(zh, NULL, 0);
79       if (ret < 0) {
80         ret = -errno;
81         dout(0) << "update_current_zh: zfs_mount '" << zfs.get_name(zh)
82                 << "' got " << cpp_strerror(ret) << dendl;
83         return ret;
84       }
85     }
86     zfs.close(zh);
87   } else {
88     dout(0) << "update_current_zh: zfs_open '" << path << "' got NULL" << dendl;
89     return -ENOENT;
90   }
91
92   zh = zfs.path_to_zhandle(get_current_path().c_str(), ZFS::TYPE_FILESYSTEM);
93   if (zh) {
94     if (strcmp(zfs.get_name(base_zh), zfs.get_name(zh))) {
95       current_zh = zh;
96       return 0;
97     }
98     zfs.close(zh);
99     dout(0) << "update_current_zh: basedir and current/ on the same filesystem" << dendl;
100   } else {
101     dout(0) << "update_current_zh: current/ not exist" << dendl;
102   }
103   return -ENOENT;
104 }
105
106 int ZFSFileStoreBackend::detect_features()
107 {
108   if (!current_zh)
109     dout(0) << "detect_features: null zfs handle for current/" << dendl;
110   return 0;
111 }
112
113 bool ZFSFileStoreBackend::can_checkpoint()
114 {
115   return m_filestore_zfs_snap && current_zh != NULL;
116 }
117
118 int ZFSFileStoreBackend::create_current()
119 {
120   struct stat st;
121   int ret = ::stat(get_current_path().c_str(), &st);
122   if (ret == 0) {
123     // current/ exists
124     if (!S_ISDIR(st.st_mode)) {
125       dout(0) << "create_current: current/ exists but is not a directory" << dendl;
126       return -ENOTDIR;
127     }
128     return 0;
129   } else if (errno != ENOENT) {
130     ret = -errno;
131     dout(0) << "create_current: cannot stat current/ " << cpp_strerror(ret) << dendl;
132     return ret;
133   }
134
135   char path[PATH_MAX];
136   snprintf(path, sizeof(path), "%s/current", zfs.get_name(base_zh));
137   ret = zfs.create(path, ZFS::TYPE_FILESYSTEM);
138   if (ret < 0 && errno != EEXIST) {
139     ret = -errno;
140     dout(0) << "create_current: zfs_create '" << path << "' got " << cpp_strerror(ret) << dendl;
141     return ret;
142   }
143
144   ret = update_current_zh();
145   return ret;
146 }
147
148 static int list_checkpoints_callback(ZFS::Handle *zh, void *data)
149 {
150   list<string> *ls = static_cast<list<string> *>(data);
151   string str = ZFS::get_name(zh);
152   size_t pos = str.find('@');
153   assert(pos != string::npos && pos + 1 != str.length());
154   ls->push_back(str.substr(pos + 1));
155   return 0;
156 }
157
158 int ZFSFileStoreBackend::list_checkpoints(list<string>& ls)
159 {
160   dout(10) << "list_checkpoints:" << dendl;
161   if (!current_zh)
162     return -EINVAL;
163
164   list<string> snaps;
165   int ret = zfs.iter_snapshots_sorted(current_zh, list_checkpoints_callback, &snaps);
166   if (ret < 0) {
167     ret = -errno;
168     dout(0) << "list_checkpoints: zfs_iter_snapshots_sorted got" << cpp_strerror(ret) << dendl;
169     return ret;
170   }
171   ls.swap(snaps);
172   return 0;
173 }
174
175 int ZFSFileStoreBackend::create_checkpoint(const string& name, uint64_t *cid)
176 {
177   dout(10) << "create_checkpoint: '" << name << "'" << dendl;
178   if (!current_zh)
179     return -EINVAL;
180
181   // looks like zfsonlinux doesn't flush dirty data when taking snapshot
182   int ret = sync_filesystem(get_current_fd());
183   if (ret < 0) {
184     ret = -errno;
185     dout(0) << "create_checkpoint: sync_filesystem got" << cpp_strerror(ret) << dendl;
186     return ret;
187   }
188
189   char path[PATH_MAX];
190   snprintf(path, sizeof(path), "%s@%s", zfs.get_name(current_zh), name.c_str());
191   ret = zfs.snapshot(path, false);
192   if (ret < 0) {
193     ret = -errno;
194     dout(0) << "create_checkpoint: zfs_snapshot '" << path << "' got" << cpp_strerror(ret) << dendl;
195     return ret;
196   }
197   if (cid)
198     *cid = 0;
199   return 0;
200 }
201
202 int ZFSFileStoreBackend::rollback_to(const string& name)
203 {
204   dout(10) << "rollback_to: '" << name << "'" << dendl;
205   if (!current_zh)
206     return -EINVAL;
207
208   // umount current to avoid triggering online rollback deadlock
209   int ret;
210   if (zfs.is_mounted(current_zh, NULL)) {
211     ret = zfs.umount(current_zh, NULL, 0);
212     if (ret < 0) {
213       ret = -errno;
214       dout(0) << "rollback_to: zfs_umount '" << zfs.get_name(current_zh) << "' got" << cpp_strerror(ret) << dendl;
215     }
216   }
217
218   char path[PATH_MAX];
219   snprintf(path, sizeof(path), "%s@%s", zfs.get_name(current_zh), name.c_str());
220
221   ZFS::Handle *snap_zh = zfs.open(path, ZFS::TYPE_SNAPSHOT);
222   if (!snap_zh) {
223     dout(0) << "rollback_to: zfs_open '" << path << "' got NULL" << dendl;
224     return -ENOENT;
225   }
226
227   ret = zfs.rollback(current_zh, snap_zh, false);
228   if (ret < 0) {
229     ret = -errno;
230     dout(0) << "rollback_to: zfs_rollback '" << zfs.get_name(snap_zh) << "' got" << cpp_strerror(ret) << dendl;
231   }
232
233   if (!zfs.is_mounted(current_zh, NULL)) {
234     int ret = zfs.mount(current_zh, NULL, 0);
235     if (ret < 0) {
236       ret = -errno;
237       dout(0) << "update_current_zh: zfs_mount '" << zfs.get_name(current_zh) << "' got " << cpp_strerror(ret) << dendl;
238       return ret;
239     }
240   }
241
242   zfs.close(snap_zh);
243   return ret;
244 }
245
246 int ZFSFileStoreBackend::destroy_checkpoint(const string& name)
247 {
248   dout(10) << "destroy_checkpoint: '" << name << "'" << dendl;
249   if (!current_zh)
250     return -EINVAL;
251
252   int ret = zfs.destroy_snaps(current_zh, name.c_str(), true);
253   if (ret < 0) {
254     ret = -errno;
255     dout(0) << "destroy_checkpoint: zfs_destroy_snaps '" << name << "' got" << cpp_strerror(ret) << dendl;
256   }
257   return ret;
258 }