]> git.proxmox.com Git - ceph.git/blame - ceph/src/os/filestore/ZFSFileStoreBackend.cc
update sources to v12.1.1
[ceph.git] / ceph / src / os / filestore / ZFSFileStoreBackend.cc
CommitLineData
7c673cae
FG
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
7c673cae
FG
29#include "ZFSFileStoreBackend.h"
30
224ce89b 31#define dout_context cct()
7c673cae
FG
32#define dout_subsys ceph_subsys_filestore
33#undef dout_prefix
34#define dout_prefix *_dout << "zfsfilestorebackend(" << get_basedir_path() << ") "
35
36ZFSFileStoreBackend::ZFSFileStoreBackend(FileStore *fs) :
37 GenericFileStoreBackend(fs), base_zh(NULL), current_zh(NULL),
224ce89b 38 m_filestore_zfs_snap(cct()->_conf->filestore_zfs_snap)
7c673cae
FG
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
55ZFSFileStoreBackend::~ZFSFileStoreBackend()
56{
57 if (base_zh)
58 zfs.close(base_zh);
59 if (current_zh)
60 zfs.close(current_zh);
61}
62
63int 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
106int ZFSFileStoreBackend::detect_features()
107{
108 if (!current_zh)
109 dout(0) << "detect_features: null zfs handle for current/" << dendl;
110 return 0;
111}
112
113bool ZFSFileStoreBackend::can_checkpoint()
114{
115 return m_filestore_zfs_snap && current_zh != NULL;
116}
117
118int 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
148static 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
158int 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
175int 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
202int 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
246int 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}