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