]>
Commit | Line | Data |
---|---|---|
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 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2015 Red Hat | |
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 | ||
16 | #include <string> | |
17 | #include <errno.h> | |
18 | #include <sstream> | |
19 | ||
20 | #include "objclass/objclass.h" | |
21 | ||
22 | #include "cls_cephfs.h" | |
23 | ||
24 | CLS_VER(1,0) | |
25 | CLS_NAME(cephfs) | |
26 | ||
27 | ||
28 | std::ostream &operator<<(std::ostream &out, const ObjCeiling &in) | |
29 | { | |
30 | out << "id: " << in.id << " size: " << in.size; | |
31 | return out; | |
32 | } | |
33 | ||
34 | ||
35 | /** | |
36 | * Set a named xattr to a given value, if and only if the xattr | |
37 | * is not already set to a greater value. | |
38 | * | |
39 | * If the xattr is missing, then it is set to the input integer. | |
40 | * | |
41 | * @param xattr_name: name of xattr to compare against and set | |
42 | * @param input_val: candidate new value, of ::encode()'able type | |
43 | * @returns 0 on success (irrespective of whether our new value | |
44 | * was used) else an error code | |
45 | */ | |
46 | template <typename A> | |
47 | static int set_if_greater(cls_method_context_t hctx, | |
48 | const std::string &xattr_name, const A input_val) | |
49 | { | |
50 | bufferlist existing_val_bl; | |
51 | ||
52 | bool set_val = false; | |
53 | int r = cls_cxx_getxattr(hctx, xattr_name.c_str(), &existing_val_bl); | |
54 | if (r == -ENOENT || existing_val_bl.length() == 0) { | |
55 | set_val = true; | |
56 | } else if (r >= 0) { | |
57 | bufferlist::iterator existing_p = existing_val_bl.begin(); | |
58 | try { | |
59 | A existing_val; | |
60 | ::decode(existing_val, existing_p); | |
61 | if (!existing_p.end()) { | |
62 | // Trailing junk? Consider it invalid and overwrite | |
63 | set_val = true; | |
64 | } else { | |
65 | // Valid existing value, do comparison | |
66 | set_val = input_val > existing_val; | |
67 | } | |
68 | } catch (const buffer::error &err) { | |
69 | // Corrupt or empty existing value, overwrite it | |
70 | set_val = true; | |
71 | } | |
72 | } else { | |
73 | return r; | |
74 | } | |
75 | ||
76 | // Conditionally set the new xattr | |
77 | if (set_val) { | |
78 | bufferlist set_bl; | |
79 | ::encode(input_val, set_bl); | |
80 | return cls_cxx_setxattr(hctx, xattr_name.c_str(), &set_bl); | |
81 | } else { | |
82 | return 0; | |
83 | } | |
84 | } | |
85 | ||
86 | static int accumulate_inode_metadata(cls_method_context_t hctx, | |
87 | bufferlist *in, bufferlist *out) | |
88 | { | |
89 | assert(in != NULL); | |
90 | assert(out != NULL); | |
91 | ||
92 | int r = 0; | |
93 | ||
94 | // Decode `in` | |
95 | bufferlist::iterator q = in->begin(); | |
96 | AccumulateArgs args; | |
97 | try { | |
98 | args.decode(q); | |
99 | } catch (const buffer::error &err) { | |
100 | return -EINVAL; | |
101 | } | |
102 | ||
103 | ObjCeiling ceiling(args.obj_index, args.obj_size); | |
104 | r = set_if_greater(hctx, args.obj_xattr_name, ceiling); | |
105 | if (r < 0) { | |
106 | return r; | |
107 | } | |
108 | ||
109 | r = set_if_greater(hctx, args.mtime_xattr_name, args.mtime); | |
110 | if (r < 0) { | |
111 | return r; | |
112 | } | |
113 | ||
114 | r = set_if_greater(hctx, args.obj_size_xattr_name, args.obj_size); | |
115 | if (r < 0) { | |
116 | return r; | |
117 | } | |
118 | ||
119 | return 0; | |
120 | } | |
121 | ||
122 | // I want to select objects that have a name ending 00000000 | |
123 | // and an xattr (scrub_tag) not equal to a specific value. | |
124 | // This is so special case that we can't really pretend it's | |
125 | // generic, so just fess up and call this the cephfs filter. | |
126 | class PGLSCephFSFilter : public PGLSFilter { | |
127 | protected: | |
128 | std::string scrub_tag; | |
129 | public: | |
130 | int init(bufferlist::iterator& params) override { | |
131 | try { | |
132 | InodeTagFilterArgs args; | |
133 | args.decode(params); | |
134 | scrub_tag = args.scrub_tag; | |
135 | } catch (buffer::error &e) { | |
136 | return -EINVAL; | |
137 | } | |
138 | ||
139 | if (scrub_tag.empty()) { | |
140 | xattr = ""; | |
141 | } else { | |
142 | xattr = "_scrub_tag"; | |
143 | } | |
144 | ||
145 | return 0; | |
146 | } | |
147 | ||
148 | ~PGLSCephFSFilter() override {} | |
149 | bool reject_empty_xattr() override { return false; } | |
150 | bool filter(const hobject_t &obj, bufferlist& xattr_data, | |
151 | bufferlist& outdata) override; | |
152 | }; | |
153 | ||
154 | bool PGLSCephFSFilter::filter(const hobject_t &obj, | |
155 | bufferlist& xattr_data, bufferlist& outdata) | |
156 | { | |
157 | const std::string need_ending = ".00000000"; | |
158 | const std::string &obj_name = obj.oid.name; | |
159 | ||
160 | if (obj_name.length() < need_ending.length()) { | |
161 | return false; | |
162 | } | |
163 | ||
164 | const bool match = obj_name.compare (obj_name.length() - need_ending.length(), need_ending.length(), need_ending) == 0; | |
165 | if (!match) { | |
166 | return false; | |
167 | } | |
168 | ||
169 | if (!scrub_tag.empty() && xattr_data.length() > 0) { | |
170 | std::string tag_ondisk; | |
171 | bufferlist::iterator q = xattr_data.begin(); | |
172 | try { | |
173 | ::decode(tag_ondisk, q); | |
174 | if (tag_ondisk == scrub_tag) | |
175 | return false; | |
176 | } catch (const buffer::error &err) { | |
177 | } | |
178 | } | |
179 | ||
180 | return true; | |
181 | } | |
182 | ||
183 | PGLSFilter *inode_tag_filter() | |
184 | { | |
185 | return new PGLSCephFSFilter(); | |
186 | } | |
187 | ||
188 | /** | |
189 | * initialize class | |
190 | * | |
191 | * We do two things here: we register the new class, and then register | |
192 | * all of the class's methods. | |
193 | */ | |
194 | CLS_INIT(cephfs) | |
195 | { | |
196 | // this log message, at level 0, will always appear in the ceph-osd | |
197 | // log file. | |
198 | CLS_LOG(0, "loading cephfs"); | |
199 | ||
200 | cls_handle_t h_class; | |
201 | cls_method_handle_t h_accumulate_inode_metadata; | |
202 | ||
203 | cls_register("cephfs", &h_class); | |
204 | cls_register_cxx_method(h_class, "accumulate_inode_metadata", | |
205 | CLS_METHOD_WR | CLS_METHOD_RD, | |
206 | accumulate_inode_metadata, &h_accumulate_inode_metadata); | |
207 | ||
208 | // A PGLS filter | |
209 | cls_register_cxx_filter(h_class, "inode_tag", inode_tag_filter); | |
210 | } | |
211 |