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