|
1 /** |
|
2 * ShaderShark |
|
3 * Copyright © 2023 František Kučera (Frantovo.cz, GlobalCode.info) |
|
4 * |
|
5 * This program is free software: you can redistribute it and/or modify |
|
6 * it under the terms of the GNU General Public License as published by |
|
7 * the Free Software Foundation, version 3 of the License. |
|
8 * |
|
9 * This program is distributed in the hope that it will be useful, |
|
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 * GNU General Public License for more details. |
|
13 * |
|
14 * You should have received a copy of the GNU General Public License |
|
15 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 */ |
|
17 |
|
18 #include <string> |
|
19 #include <cstring> |
|
20 #include <stdexcept> |
|
21 #include <vector> |
|
22 #include <sys/xattr.h> |
|
23 |
|
24 #include "XAttrs.h" |
|
25 |
|
26 std::string xattrGet( |
|
27 const std::string fileName, |
|
28 const std::string name, |
|
29 bool* exists = nullptr) { |
|
30 std::string buffer; |
|
31 ssize_t size = getxattr(fileName.c_str(), name.c_str(), nullptr, 0); |
|
32 if (size > 0) { |
|
33 buffer.resize(size); |
|
34 getxattr(fileName.c_str(), name.c_str(), buffer.data(), buffer.size()); |
|
35 if (exists) *exists = true; |
|
36 return buffer; |
|
37 } else if (size == 0) { |
|
38 if (exists) *exists = true; |
|
39 return ""; |
|
40 } else if (errno == ENODATA) { |
|
41 if (exists) *exists = false; |
|
42 return ""; |
|
43 } else if (errno == ERANGE) { |
|
44 // rare race condition - the value has changed between the two calls |
|
45 return xattrGet(fileName, name, exists); |
|
46 } else { |
|
47 throw std::logic_error( |
|
48 std::string("Unable to get extended attribute: ") |
|
49 + strerror(errno)); |
|
50 } |
|
51 } |
|
52 |
|
53 bool xattrSet(const std::string fileName, |
|
54 const std::string name, |
|
55 const std::string value, |
|
56 bool exists = true) { |
|
57 int result; |
|
58 if (exists) { |
|
59 result = setxattr( |
|
60 fileName.c_str(), |
|
61 name.c_str(), |
|
62 value.c_str(), |
|
63 value.size(), |
|
64 0); |
|
65 } else { |
|
66 result = removexattr(fileName.c_str(), name.c_str()); |
|
67 if (result < 0 && errno == ENODATA) return false; |
|
68 } |
|
69 |
|
70 if (result < 0) throw std::logic_error( |
|
71 std::string("Unable to set extended attribute: ") |
|
72 + strerror(errno)); |
|
73 else return true; |
|
74 } |
|
75 |
|
76 std::vector<std::string> xattrList(const std::string fileName) { |
|
77 std::string buffer; |
|
78 ssize_t size = listxattr(fileName.c_str(), nullptr, 0); |
|
79 if (size >= 0) { |
|
80 buffer.resize(size); |
|
81 listxattr(fileName.c_str(), buffer.data(), buffer.size()); |
|
82 |
|
83 std::vector<std::string> result; |
|
84 for (const char* k = buffer.c_str(); strlen(k); k += strlen(k) + 1) { |
|
85 result.push_back(k); |
|
86 } |
|
87 return result; |
|
88 } else if (errno == ERANGE) { |
|
89 // rare race condition - the list has changed between the two calls |
|
90 return xattrList(fileName); |
|
91 } else { |
|
92 throw std::logic_error( |
|
93 std::string("Unable to list extended attributes: ") |
|
94 + strerror(errno)); |
|
95 } |
|
96 } |
|
97 |
|
98 class XAttrs::Attribute::Impl { |
|
99 public: |
|
100 |
|
101 Impl() { |
|
102 } |
|
103 |
|
104 virtual ~Impl() { |
|
105 } |
|
106 |
|
107 std::shared_ptr<XAttrs::Impl> xattrs; |
|
108 std::string name; |
|
109 std::string value; |
|
110 bool exists = true; |
|
111 bool loaded = false; |
|
112 }; |
|
113 |
|
114 class XAttrs::Impl { |
|
115 public: |
|
116 std::string nameSpace; |
|
117 std::string fileName; |
|
118 std::vector<Attribute> attributes; |
|
119 bool loaded = false; |
|
120 const XAttrs::Attribute& save(const XAttrs::Attribute& attribute); |
|
121 XAttrs::Attribute& load(XAttrs::Attribute& attribute); |
|
122 }; |
|
123 |
|
124 XAttrs::XAttrs(const std::string& fileName, const std::string& nameSpace) : |
|
125 impl(std::make_shared<Impl>()) { |
|
126 impl->nameSpace = nameSpace; |
|
127 impl->fileName = fileName; |
|
128 } |
|
129 |
|
130 XAttrs::~XAttrs() { |
|
131 } |
|
132 |
|
133 const std::string XAttrs::getFileName() const { |
|
134 return impl->fileName; |
|
135 } |
|
136 |
|
137 size_t XAttrs::size(bool reload) { |
|
138 if (reload || !impl->loaded) { |
|
139 impl->attributes.clear(); |
|
140 std::vector<std::string> names = xattrList(impl->fileName); |
|
141 // FIXME: remove nameSpace |
|
142 for (const std::string& name : names) { |
|
143 Attribute a; |
|
144 a.impl->xattrs = impl; |
|
145 a.impl->name = name; |
|
146 a.impl->value = xattrGet(impl->fileName, name, &a.impl->exists); |
|
147 impl->attributes.push_back(a); |
|
148 } |
|
149 impl->loaded = true; |
|
150 } |
|
151 return impl->attributes.size(); |
|
152 } |
|
153 |
|
154 XAttrs::Attribute* XAttrs::begin() { |
|
155 size(); |
|
156 return &impl->attributes[0]; |
|
157 } |
|
158 |
|
159 XAttrs::Attribute* XAttrs::end() { |
|
160 size(); |
|
161 return &impl->attributes[impl->attributes.size()]; |
|
162 } |
|
163 |
|
164 const XAttrs::Attribute& |
|
165 XAttrs::Impl::save(const XAttrs::Attribute& attribute) { |
|
166 xattrSet( |
|
167 fileName, |
|
168 nameSpace + "." + attribute.impl->name, |
|
169 attribute.impl->value, |
|
170 attribute.impl->exists); |
|
171 loaded &= attribute.impl->exists; |
|
172 return attribute; |
|
173 } |
|
174 |
|
175 XAttrs::Attribute& |
|
176 XAttrs::Impl::load(XAttrs::Attribute& attribute) { |
|
177 attribute.impl->value = xattrGet(fileName, |
|
178 nameSpace + "." + attribute.impl->name, &attribute.impl->exists); |
|
179 return attribute; |
|
180 } |
|
181 |
|
182 XAttrs::Attribute& XAttrs::operator[](std::string name) { |
|
183 for (XAttrs::Attribute& a : impl->attributes) { |
|
184 if (a.getName() == name) return a; // impl->load(a); |
|
185 } |
|
186 |
|
187 XAttrs::Attribute a; |
|
188 a.impl->xattrs = impl; |
|
189 a.impl->name = name; |
|
190 a.impl->exists = false; |
|
191 // impl->load(a); |
|
192 |
|
193 impl->attributes.push_back(a); |
|
194 return impl->attributes.back(); |
|
195 } |
|
196 |
|
197 XAttrs::Attribute& XAttrs::operator[](std::size_t index) { |
|
198 if (impl->attributes.empty()) size(); |
|
199 return impl->attributes[index]; |
|
200 } |
|
201 |
|
202 XAttrs::Attribute::Attribute() : impl(std::make_shared<Impl>()) { |
|
203 } |
|
204 |
|
205 XAttrs::Attribute::~Attribute() { |
|
206 } |
|
207 |
|
208 const std::string XAttrs::Attribute::getName() { |
|
209 return impl->name; |
|
210 } |
|
211 |
|
212 const std::string XAttrs::Attribute::getValue(bool reload) { |
|
213 if (reload || !impl->loaded) impl->xattrs->load(*this); |
|
214 return impl->value; |
|
215 } |
|
216 |
|
217 bool XAttrs::Attribute::exists(bool reload) { |
|
218 if (reload || !impl->loaded) impl->xattrs->load(*this); |
|
219 return impl->exists; |
|
220 } |
|
221 |
|
222 bool XAttrs::Attribute::missing(bool reload) { |
|
223 return !exists(reload); |
|
224 } |
|
225 |
|
226 XAttrs::Attribute& XAttrs::Attribute::operator=(const std::string& value) { |
|
227 impl->value = value; |
|
228 impl->exists = true; |
|
229 impl->xattrs->save(*this); |
|
230 return *this; |
|
231 } |
|
232 |
|
233 XAttrs::Attribute& XAttrs::Attribute:: |
|
234 operator=(const XAttrs::Attribute& value) { |
|
235 impl->value = value.impl->value; |
|
236 impl->exists = value.impl->exists; |
|
237 impl->xattrs->save(*this); |
|
238 return *this; |
|
239 } |
|
240 |
|
241 XAttrs::Attribute::operator std::string() { |
|
242 impl->xattrs->load(*this); |
|
243 return impl->value; |
|
244 } |
|
245 |
|
246 std::ostream& operator<<(std::ostream& s, XAttrs::Attribute& a) { |
|
247 s << a.getValue().c_str(); |
|
248 return s; |
|
249 } |
|
250 |
|
251 std::ostream& operator>>(XAttrs::Attribute& a, std::ostream& s) { |
|
252 s << a.getValue().c_str(); |
|
253 return s; |
|
254 } |
|
255 |
|
256 XAttrs::Null::Null() { |
|
257 impl->exists = false; |
|
258 } |