extattr 0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE.txt ADDED
@@ -0,0 +1,24 @@
1
+ Copyright (c) 2012, dearblue. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or
4
+ without modification, are permitted provided that the following
5
+ conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright
8
+ notice, this list of conditions and the following disclaimer.
9
+ 2. Redistributions in binary form must reproduce the above copyright
10
+ notice, this list of conditions and the following disclaimer in
11
+ the documentation and/or other materials provided with the
12
+ distribution.
13
+
14
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
18
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.txt ADDED
@@ -0,0 +1,53 @@
1
+ = extattr
2
+
3
+ extattr is extended attribute operation library for Windows and FreeBSD.
4
+
5
+ ----
6
+
7
+ extattr は拡張属性を操作するライブラリで、Windows、FreeBSD に対応しています (GNU/Linux はコードを書いてみただけで、試験がまったくされていません)。
8
+
9
+ サポートされる環境で、統一的なメソッドを提供します。
10
+
11
+ クラスメソッドに『!』がついているものはシンボリックリンクに対する操作となります。
12
+
13
+ キーワード引数の <code>namespace</code> を与えると、拡張属性の名前空間を指定できます。規定値は <code>EXTATTR_NAMESPACE_USER</code> で、ほかの値は <code>EXTATTR_NAMESPACE_SYSTEM</code> のみが指定できます (Windows 版では <code>EXTATTR_NAMESPACE_USER</code> のみが指定可能です)。
14
+
15
+
16
+ == 拡張属性の属性名を取得:
17
+
18
+ - File#extattr_list(namespace: File::EXTATTR_NAMESPACE_USER) -> array
19
+ - File.extattr_list(path, namespace: File::EXTATTR_NAMESPACE_USER) -> array
20
+ - File.extattr_list!(path, namespace: File::EXTATTR_NAMESPACE_USER) -> array
21
+
22
+ == 拡張属性の要素の大きさを取得:
23
+
24
+ - File#extattr_size(name, namespace: File::EXTATTR_NAMESPACE_USER) -> size
25
+ - File.extattr_size(path, name, namespace: File::EXTATTR_NAMESPACE_USER) -> size
26
+ - File.extattr_size!(path, name, namespace: File::EXTATTR_NAMESPACE_USER) -> size
27
+
28
+ == 拡張属性の要素を取得:
29
+
30
+ - File#extattr_get(name, namespace: File::EXTATTR_NAMESPACE_USER) -> data (String)
31
+ - File.extattr_get(path, name, namespace: File::EXTATTR_NAMESPACE_USER) -> data (String)
32
+ - File.extattr_get!(path, name, namespace: File::EXTATTR_NAMESPACE_USER) -> data (String)
33
+
34
+ == 拡張属性の要素を設定:
35
+
36
+ - File#extattr_set(name, data, namespace: File::EXTATTR_NAMESPACE_USER) -> nil
37
+ - File.extattr_set(path, name, data, namespace: File::EXTATTR_NAMESPACE_USER) -> nil
38
+ - File.extattr_set!(path, name, data, namespace: File::EXTATTR_NAMESPACE_USER) -> nil
39
+
40
+ == 拡張属性の要素を削除:
41
+
42
+ - File#extattr_delete(name, namespace: File::EXTATTR_NAMESPACE_USER) -> nil
43
+ - File.extattr_delete(path, name, namespace: File::EXTATTR_NAMESPACE_USER) -> nil
44
+ - File.extattr_delete!(path, name, namespace: File::EXTATTR_NAMESPACE_USER) -> nil
45
+
46
+
47
+ == Microsoft Windows における諸注意
48
+
49
+ Windows 2000 以降でのみ動作します。Windows 9X シリーズでは <code>require "extattr"</code> の段階で例外が発生するでしょう。
50
+
51
+ リパースポイント (ジャンクションやシンボリックリンク) に対する ADS は要素の取得や設定、削除は出来ません。必ずリンク先に対する操作となります。
52
+
53
+ 128 KiB を超える ADS は取得も設定も出来ません。これは『拡張属性』と捉えた場合、巨大なデータを扱えるべきではないという考えによるためです (本当のところは FreeBSD の拡張属性が最大 64KiB 弱であることが由来です)。巨大な ADS を扱いたい場合は、File.open で自由に読み書きできます (これは ruby に限ったことではなく、Windows による仕様です)。この場合の与えるファイル名は、path + ":" + name という形になります。
data/ext/extattr.bsd ADDED
@@ -0,0 +1,186 @@
1
+ #include <sys/types.h>
2
+ #include <sys/extattr.h>
3
+
4
+
5
+ static void
6
+ split_name(const char list[], size_t size, void (*func)(void *, VALUE), void *userdata)
7
+ {
8
+ // Each list entry consists of a single byte containing the length of
9
+ // the attribute name, followed by the attribute name.
10
+ // The attribute name is not terminated by ASCII 0 (nul).
11
+ const char *ptr = list;
12
+ const char *end = list + size;
13
+
14
+ while (ptr < end) {
15
+ size_t len = (uint8_t)*ptr ++;
16
+ if (ptr + len > end) { return; }
17
+ func(userdata, rb_str_new(ptr, len));
18
+ ptr += len;
19
+ }
20
+ }
21
+
22
+ static int
23
+ get_extattr_list_size(int (*extattr_list)(), intptr_t d, int namespace)
24
+ {
25
+ int size = extattr_list(d, namespace, NULL, 0);
26
+ if (size < 0) { rb_sys_fail("extattr_list call error"); }
27
+ return size;
28
+ }
29
+
30
+ static VALUE
31
+ extattr_list0(int (*extattr_list)(), intptr_t d, int namespace)
32
+ {
33
+ size_t size = get_extattr_list_size(extattr_list, d, namespace);
34
+ VALUE buf = rb_str_buf_new(size);
35
+ char *ptr = RSTRING_PTR(buf);
36
+
37
+ ssize_t size1 = extattr_list(d, namespace, ptr, size);
38
+ if (size1 < 0) { rb_sys_fail("extattr_list call error"); }
39
+
40
+ VALUE list = Qnil;
41
+ if (rb_block_given_p()) {
42
+ split_name(ptr, size1, (void (*)(void *, VALUE))rb_yield_values, (void *)(1));
43
+ } else {
44
+ list = rb_ary_new();
45
+ split_name(ptr, size1, (void (*)(void *, VALUE))rb_ary_push, (void *)list);
46
+ }
47
+ rb_free_tmp_buffer(&buf);
48
+
49
+ return list;
50
+ }
51
+
52
+ static VALUE
53
+ file_extattr_list0(VALUE file, int fd, int namespace)
54
+ {
55
+ return extattr_list0(extattr_list_fd, fd, namespace);
56
+ }
57
+
58
+ static VALUE
59
+ file_s_extattr_list0(VALUE path, int namespace)
60
+ {
61
+ return extattr_list0(extattr_list_file, (intptr_t)StringValueCStr(path), namespace);
62
+ }
63
+
64
+ static VALUE
65
+ file_s_extattr_list_link0(VALUE path, int namespace)
66
+ {
67
+ return extattr_list0(extattr_list_link, (intptr_t)StringValueCStr(path), namespace);
68
+ }
69
+
70
+
71
+ static VALUE
72
+ extattr_size0(int (*extattr_get)(), intptr_t d, int namespace, VALUE name)
73
+ {
74
+ ssize_t size = extattr_get(d, namespace, RSTRING_PTR(name), NULL, 0);
75
+ if (size < 0) { rb_sys_fail("extattr_get call error"); }
76
+ return SIZET2NUM(size);
77
+ }
78
+
79
+ static VALUE
80
+ file_extattr_size0(VALUE file, int fd, int namespace, VALUE name)
81
+ {
82
+ return extattr_size0(extattr_get_fd, fd, namespace, name);
83
+ }
84
+
85
+ static VALUE
86
+ file_s_extattr_size0(VALUE path, int namespace, VALUE name)
87
+ {
88
+ return extattr_size0(extattr_get_file, (intptr_t)StringValueCStr(path), namespace, name);
89
+ }
90
+
91
+ static VALUE
92
+ file_s_extattr_size_link0(VALUE path, int namespace, VALUE name)
93
+ {
94
+ return extattr_size0(extattr_get_link, (intptr_t)StringValueCStr(path), namespace, name);
95
+ }
96
+
97
+
98
+ static VALUE
99
+ extattr_get0(int (*extattr_get)(), intptr_t d, VALUE path, int namespace, VALUE name)
100
+ {
101
+ ssize_t size = extattr_get(d, namespace, RSTRING_PTR(name), NULL, 0);
102
+ if (size < 0) { rb_sys_fail(StringValueCStr(path)); }
103
+ VALUE buf = rb_str_buf_new(size);
104
+ size = extattr_get(d, namespace, RSTRING_PTR(name), RSTRING_PTR(buf), size);
105
+ if (size < 0) { rb_sys_fail(StringValueCStr(path)); }
106
+ rb_str_set_len(buf, size);
107
+ return buf;
108
+ }
109
+
110
+ static VALUE
111
+ file_extattr_get0(VALUE file, int fd, int namespace, VALUE name)
112
+ {
113
+ return extattr_get0(extattr_get_fd, fd, RFILE(file)->fptr->pathv, namespace, name);
114
+ }
115
+
116
+ static VALUE
117
+ file_s_extattr_get0(VALUE path, int namespace, VALUE name)
118
+ {
119
+ return extattr_get0(extattr_get_file, (intptr_t)StringValueCStr(path), path, namespace, name);
120
+ }
121
+
122
+ static VALUE
123
+ file_s_extattr_get_link0(VALUE path, int namespace, VALUE name)
124
+ {
125
+ return extattr_get0(extattr_get_link, (intptr_t)StringValueCStr(path), path, namespace, name);
126
+ }
127
+
128
+
129
+ static VALUE
130
+ extattr_set0(int (*extattr_set)(), intptr_t d, int namespace, VALUE name, VALUE data)
131
+ {
132
+ int status = extattr_set(d, namespace, RSTRING_PTR(name), RSTRING_PTR(data), RSTRING_LEN(data));
133
+ if (status < 0) { rb_sys_fail("extattr_set call error"); }
134
+ return Qnil;
135
+ }
136
+
137
+ static VALUE
138
+ file_extattr_set0(VALUE file, int fd, int namespace, VALUE name, VALUE data)
139
+ {
140
+ return extattr_set0(extattr_set_fd, fd, namespace, name, data);
141
+ }
142
+
143
+ static VALUE
144
+ file_s_extattr_set0(VALUE path, int namespace, VALUE name, VALUE data)
145
+ {
146
+ return extattr_set0(extattr_set_file, (intptr_t)StringValueCStr(path), namespace, name, data);
147
+ }
148
+
149
+ static VALUE
150
+ file_s_extattr_set_link0(VALUE path, int namespace, VALUE name, VALUE data)
151
+ {
152
+ return extattr_set0(extattr_set_link, (intptr_t)StringValueCStr(path), namespace, name, data);
153
+ }
154
+
155
+
156
+ static VALUE
157
+ extattr_delete0(int (*extattr_delete)(), intptr_t d, int namespace, VALUE name)
158
+ {
159
+ int status = extattr_delete(d, namespace, RSTRING_PTR(name), NULL, 0);
160
+ if (status < 0) { rb_sys_fail("extattr_delete call error"); }
161
+ return Qnil;
162
+ }
163
+
164
+ static VALUE
165
+ file_extattr_delete0(VALUE file, int fd, int namespace, VALUE name)
166
+ {
167
+ return extattr_delete0(extattr_delete_fd, fd, namespace, name);
168
+ }
169
+
170
+ static VALUE
171
+ file_s_extattr_delete0(VALUE path, int namespace, VALUE name)
172
+ {
173
+ return extattr_delete0(extattr_delete_file, (intptr_t)StringValueCStr(path), namespace, name);
174
+ }
175
+
176
+ static VALUE
177
+ file_s_extattr_delete_link0(VALUE path, int namespace, VALUE name)
178
+ {
179
+ return extattr_delete0(extattr_delete_link, (intptr_t)StringValueCStr(path), namespace, name);
180
+ }
181
+
182
+
183
+ static void
184
+ setup(void)
185
+ {
186
+ }
data/ext/extattr.c ADDED
@@ -0,0 +1,345 @@
1
+ /* encoding:utf-8 */
2
+
3
+ /*
4
+ * extattr.c
5
+ * AUTHOR:: dearblue <dearblue@zoho.com>
6
+ * LICENSE:: 2-clause BSD License
7
+ * WWW:: http://sourceforge.jp/projects/rutsubo
8
+ */
9
+
10
+ #include <ruby.h>
11
+ #include <ruby/io.h>
12
+ #include <ruby/intern.h>
13
+
14
+
15
+ static VALUE file_extattr_list0(VALUE file, int fd, int namespace);
16
+ static VALUE file_extattr_size0(VALUE file, int fd, int namespace, VALUE name);
17
+ static VALUE file_extattr_get0(VALUE file, int fd, int namespace, VALUE name);
18
+ static VALUE file_extattr_set0(VALUE file, int fd, int namespace, VALUE name, VALUE data);
19
+ static VALUE file_extattr_delete0(VALUE file, int fd, int namespace, VALUE name);
20
+ static VALUE file_s_extattr_list0(VALUE path, int namespace);
21
+ static VALUE file_s_extattr_list_link0(VALUE path, int namespace);
22
+ static VALUE file_s_extattr_size0(VALUE path, int namespace, VALUE name);
23
+ static VALUE file_s_extattr_size_link0(VALUE path, int namespace, VALUE name);
24
+ static VALUE file_s_extattr_get0(VALUE path, int namespace, VALUE name);
25
+ static VALUE file_s_extattr_get_link0(VALUE path, int namespace, VALUE name);
26
+ static VALUE file_s_extattr_set0(VALUE path, int namespace, VALUE name, VALUE data);
27
+ static VALUE file_s_extattr_set_link0(VALUE path, int namespace, VALUE name, VALUE data);
28
+ static VALUE file_s_extattr_delete0(VALUE path, int namespace, VALUE name);
29
+ static VALUE file_s_extattr_delete_link0(VALUE path, int namespace, VALUE name);
30
+
31
+ // Init_extattr から呼び出される、環境ごとの初期設定。
32
+ static void setup(void);
33
+
34
+
35
+ #define RDOC(...)
36
+
37
+ #define ELEMENTOF(V) (sizeof(V) / sizeof((V)[0]))
38
+
39
+
40
+ static inline VALUE
41
+ hash_lookup(VALUE hash, VALUE key, VALUE default_value)
42
+ {
43
+ if (NIL_P(hash)) {
44
+ return default_value;
45
+ } else if (rb_obj_is_kind_of(hash, rb_cHash)) {
46
+ return rb_hash_lookup2(hash, key, default_value);
47
+ } else {
48
+ rb_raise(rb_eTypeError, "not hash");
49
+ }
50
+ }
51
+
52
+ static inline int
53
+ file2fd(VALUE file)
54
+ {
55
+ rb_io_t *fptr;
56
+ // FIXME: ファイルであることを確認すべき
57
+ GetOpenFile(file, fptr);
58
+ return fptr->fd;
59
+ }
60
+
61
+
62
+ #if defined(HAVE_SYS_EXTATTR_H)
63
+ # include "extattr.bsd"
64
+ #elif defined(HAVE_ATTR_XATTR_H)
65
+ # include "extattr.linux"
66
+ #elif defined(HAVE_WINNT_H)
67
+ # include "extattr.windows"
68
+ #else
69
+ # error ruby extattr not supported on your system
70
+ #endif
71
+
72
+
73
+ static VALUE SYMnamespace;
74
+
75
+
76
+ /*
77
+ * call-seq:
78
+ * file.extattr_list(namespace: File::EXTATTR_NAMESPACE_USER) -> names array
79
+ * file.extattr_list(namespace: File::EXTATTR_NAMESPACE_USER) { |name| ... } -> nil
80
+ *
81
+ * 開いているファイルの拡張属性名一覧を得ます。
82
+ *
83
+ * ブロックが指定された場合、一つ一つの拡張属性名を渡してブロックが評価されます。ブロックの戻り値は無視されます。
84
+ */
85
+ static VALUE
86
+ file_extattr_list(int argc, VALUE argv[], VALUE file)
87
+ {
88
+ VALUE opts = Qnil;
89
+ rb_scan_args(argc, argv, "0:", &opts);
90
+ return file_extattr_list0(file, file2fd(file),
91
+ NUM2INT(hash_lookup(opts, SYMnamespace, INT2NUM(EXTATTR_NAMESPACE_USER))));
92
+ }
93
+
94
+ /*
95
+ * call-seq:
96
+ * File.extattr_list(path, namespace: File::EXTATTR_NAMESPACE_USER) -> names array
97
+ * File.extattr_list(path, namespace: File::EXTATTR_NAMESPACE_USER) { |name| ... } -> nil
98
+ *
99
+ * ファイル名を指定すること以外は File#extattr_list と同じです。
100
+ */
101
+ static VALUE
102
+ file_s_extattr_list(int argc, VALUE argv[], VALUE file)
103
+ {
104
+ VALUE path, opts = Qnil;
105
+ rb_scan_args(argc, argv, "1:", &path, &opts);
106
+ return file_s_extattr_list0(StringValue(path),
107
+ NUM2INT(hash_lookup(opts, SYMnamespace, INT2NUM(EXTATTR_NAMESPACE_USER))));
108
+ }
109
+
110
+ /*
111
+ * call-seq:
112
+ * File.extattr_list!(path, namespace: File::EXTATTR_NAMESPACE_USER) -> names array
113
+ * File.extattr_list!(path, namespace: File::EXTATTR_NAMESPACE_USER) { |name| ... } -> nil
114
+ *
115
+ * シンボリックリンクに対する操作という以外は、File.extattr_list と同じです。
116
+ */
117
+ static VALUE
118
+ file_s_extattr_list_link(int argc, VALUE argv[], VALUE file)
119
+ {
120
+ VALUE path, opts = Qnil;
121
+ rb_scan_args(argc, argv, "1:", &path, &opts);
122
+ return file_s_extattr_list_link0(StringValue(path),
123
+ NUM2INT(hash_lookup(opts, SYMnamespace, INT2NUM(EXTATTR_NAMESPACE_USER))));
124
+ }
125
+
126
+
127
+ /*
128
+ * call-seq:
129
+ * file.extattr_size(name, namespace: File::EXTATTR_NAMESPACE_USER) -> names array
130
+ *
131
+ * 拡張属性の大きさを取得します。
132
+ */
133
+ static VALUE
134
+ file_extattr_size(int argc, VALUE argv[], VALUE file)
135
+ {
136
+ VALUE name, opts = Qnil;
137
+ rb_scan_args(argc, argv, "1:", &name, &opts);
138
+ return file_extattr_size0(file, file2fd(file),
139
+ NUM2INT(hash_lookup(opts, SYMnamespace, INT2NUM(EXTATTR_NAMESPACE_USER))),
140
+ StringValue(name));
141
+ }
142
+
143
+ /*
144
+ * call-seq:
145
+ * File.extattr_size(path, name, namespace: File::EXTATTR_NAMESPACE_USER) -> size
146
+ */
147
+ static VALUE
148
+ file_s_extattr_size(int argc, VALUE argv[], VALUE file)
149
+ {
150
+ VALUE path, name, opts = Qnil;
151
+ rb_scan_args(argc, argv, "2:", &path, &name, &opts);
152
+ return file_s_extattr_size0(StringValue(path),
153
+ NUM2INT(hash_lookup(opts, SYMnamespace, INT2NUM(EXTATTR_NAMESPACE_USER))),
154
+ StringValue(name));
155
+ }
156
+
157
+ /*
158
+ * call-seq:
159
+ * File.extattr_size!(path, name, namespace: File::EXTATTR_NAMESPACE_USER) -> size
160
+ */
161
+ static VALUE
162
+ file_s_extattr_size_link(int argc, VALUE argv[], VALUE file)
163
+ {
164
+ VALUE path, name, opts = Qnil;
165
+ rb_scan_args(argc, argv, "2:", &path, &name, &opts);
166
+ return file_s_extattr_size_link0(StringValue(path),
167
+ NUM2INT(hash_lookup(opts, SYMnamespace, INT2NUM(EXTATTR_NAMESPACE_USER))),
168
+ StringValue(name));
169
+ }
170
+
171
+
172
+ /*
173
+ * call-seq:
174
+ * file.extattr_get(name, namespace: File::EXTATTR_NAMESPACE_USER) -> data
175
+ */
176
+ static VALUE
177
+ file_extattr_get(int argc, VALUE argv[], VALUE file)
178
+ {
179
+ VALUE name, opts = Qnil;
180
+ rb_scan_args(argc, argv, "1:", &name, &opts);
181
+ return file_extattr_get0(file, file2fd(file),
182
+ NUM2INT(hash_lookup(opts, SYMnamespace, INT2NUM(EXTATTR_NAMESPACE_USER))),
183
+ StringValue(name));
184
+ }
185
+
186
+ /*
187
+ * call-seq:
188
+ * File.extattr_get(path, name, namespace: File::EXTATTR_NAMESPACE_USER) -> data
189
+ */
190
+ static VALUE
191
+ file_s_extattr_get(int argc, VALUE argv[], VALUE file)
192
+ {
193
+ VALUE path, name, opts = Qnil;
194
+ rb_scan_args(argc, argv, "2:", &path, &name, &opts);
195
+ return file_s_extattr_get0(StringValue(path),
196
+ NUM2INT(hash_lookup(opts, SYMnamespace, INT2NUM(EXTATTR_NAMESPACE_USER))),
197
+ StringValue(name));
198
+ }
199
+
200
+ /*
201
+ * call-seq:
202
+ * File.extattr_get!(path, name, namespace: File::EXTATTR_NAMESPACE_USER) -> data
203
+ */
204
+ static VALUE
205
+ file_s_extattr_get_link(int argc, VALUE argv[], VALUE file)
206
+ {
207
+ VALUE path, name, opts = Qnil;
208
+ rb_scan_args(argc, argv, "2:", &path, &name, &opts);
209
+ return file_s_extattr_get_link0(StringValue(path),
210
+ NUM2INT(hash_lookup(opts, SYMnamespace, INT2NUM(EXTATTR_NAMESPACE_USER))),
211
+ StringValue(name));
212
+ }
213
+
214
+
215
+ /*
216
+ * call-seq:
217
+ * file.extattr_set(name, data, namespace: File::EXTATTR_NAMESPACE_USER) -> nil
218
+ */
219
+ static VALUE
220
+ file_extattr_set(int argc, VALUE argv[], VALUE file)
221
+ {
222
+ VALUE name, data, opts = Qnil;
223
+ rb_scan_args(argc, argv, "11:", &name, &data, &opts);
224
+ return file_extattr_set0(file, file2fd(file),
225
+ NUM2INT(hash_lookup(opts, SYMnamespace, INT2NUM(EXTATTR_NAMESPACE_USER))),
226
+ StringValue(name),
227
+ StringValue(data));
228
+ }
229
+
230
+ /*
231
+ * call-seq:
232
+ * File.extattr_set(path, name, data, namespace: File::EXTATTR_NAMESPACE_USER) -> nil
233
+ */
234
+ static VALUE
235
+ file_s_extattr_set(int argc, VALUE argv[], VALUE file)
236
+ {
237
+ VALUE path, name, data, opts = Qnil;
238
+ rb_scan_args(argc, argv, "21:", &path, &name, &data, &opts);
239
+ return file_s_extattr_set0(StringValue(path),
240
+ NUM2INT(hash_lookup(opts, SYMnamespace, INT2NUM(EXTATTR_NAMESPACE_USER))),
241
+ StringValue(name),
242
+ StringValue(data));
243
+ }
244
+
245
+ /*
246
+ * call-seq:
247
+ * File.extattr_set!(path, name, data, namespace: File::EXTATTR_NAMESPACE_USER) -> nil
248
+ */
249
+ static VALUE
250
+ file_s_extattr_set_link(int argc, VALUE argv[], VALUE file)
251
+ {
252
+ VALUE path, name, data, opts = Qnil;
253
+ rb_scan_args(argc, argv, "21:", &path, &name, &data, &opts);
254
+ return file_s_extattr_set_link0(StringValue(path),
255
+ NUM2INT(hash_lookup(opts, SYMnamespace, INT2NUM(EXTATTR_NAMESPACE_USER))),
256
+ StringValue(name),
257
+ StringValue(data));
258
+ }
259
+
260
+
261
+ /*
262
+ * call-seq:
263
+ * file.extattr_delete(name, namespace: File::EXTATTR_NAMESPACE_USER) -> nil
264
+ */
265
+ static VALUE
266
+ file_extattr_delete(int argc, VALUE argv[], VALUE file)
267
+ {
268
+ VALUE name, opts = Qnil;
269
+ rb_scan_args(argc, argv, "1:", &name, &opts);
270
+ return file_extattr_delete0(file, file2fd(file),
271
+ NUM2INT(hash_lookup(opts, SYMnamespace, INT2NUM(EXTATTR_NAMESPACE_USER))),
272
+ StringValue(name));
273
+ }
274
+
275
+ /*
276
+ * call-seq:
277
+ * File.extattr_delete(path, name, namespace: File::EXTATTR_NAMESPACE_USER) -> nil
278
+ */
279
+ static VALUE
280
+ file_s_extattr_delete(int argc, VALUE argv[], VALUE file)
281
+ {
282
+ VALUE path, name, opts = Qnil;
283
+ rb_scan_args(argc, argv, "2:", &path, &name, &opts);
284
+ return file_s_extattr_delete0(StringValue(path),
285
+ NUM2INT(hash_lookup(opts, SYMnamespace, INT2NUM(EXTATTR_NAMESPACE_USER))),
286
+ StringValue(name));
287
+ }
288
+
289
+ /*
290
+ * call-seq:
291
+ * File.extattr_delete!(path, name, namespace: File::EXTATTR_NAMESPACE_USER) -> nil
292
+ */
293
+ static VALUE
294
+ file_s_extattr_delete_link(int argc, VALUE argv[], VALUE file)
295
+ {
296
+ VALUE path, name, opts = Qnil;
297
+ rb_scan_args(argc, argv, "2:", &path, &name, &opts);
298
+ return file_s_extattr_delete_link0(StringValue(path),
299
+ NUM2INT(hash_lookup(opts, SYMnamespace, INT2NUM(EXTATTR_NAMESPACE_USER))),
300
+ StringValue(name));
301
+ }
302
+
303
+
304
+ /*
305
+ * Document-class: File
306
+ *
307
+ * File クラスに拡張属性を操作するメソッドを追加します。
308
+ *
309
+ * 感嘆符(『!』)のついたメソッドは、シンボリックリンクに対する操作となります。
310
+ *
311
+ * メソッドにキーワード引数として <code>namespace:</code> を与えることにより、拡張属性の名前空間を指定することが出来ます。
312
+ * 現在の実装においては <code>EXTATTR_NAMESPACE_USER</code> と <code>EXTATTR_NAMESPACE_SYSTEM</code> のみが利用可能です。
313
+ */
314
+
315
+
316
+ void
317
+ Init_extattr(void)
318
+ {
319
+ SYMnamespace = ID2SYM(rb_intern("namespace"));
320
+
321
+ rb_const_set(rb_cFile, rb_intern("EXTATTR_NAMESPACE_USER"), INT2NUM(EXTATTR_NAMESPACE_USER));
322
+ rb_const_set(rb_cFile, rb_intern("EXTATTR_NAMESPACE_SYSTEM"), INT2NUM(EXTATTR_NAMESPACE_SYSTEM));
323
+
324
+ rb_define_method(rb_cFile, "extattr_list", RUBY_METHOD_FUNC(file_extattr_list), -1);
325
+ rb_define_singleton_method(rb_cFile, "extattr_list", RUBY_METHOD_FUNC(file_s_extattr_list), -1);
326
+ rb_define_singleton_method(rb_cFile, "extattr_list!", RUBY_METHOD_FUNC(file_s_extattr_list_link), -1);
327
+
328
+ rb_define_method(rb_cFile, "extattr_size", RUBY_METHOD_FUNC(file_extattr_size), -1);
329
+ rb_define_singleton_method(rb_cFile, "extattr_size", RUBY_METHOD_FUNC(file_s_extattr_size), -1);
330
+ rb_define_singleton_method(rb_cFile, "extattr_size!", RUBY_METHOD_FUNC(file_s_extattr_size_link), -1);
331
+
332
+ rb_define_method(rb_cFile, "extattr_get", RUBY_METHOD_FUNC(file_extattr_get), -1);
333
+ rb_define_singleton_method(rb_cFile, "extattr_get", RUBY_METHOD_FUNC(file_s_extattr_get), -1);
334
+ rb_define_singleton_method(rb_cFile, "extattr_get!", RUBY_METHOD_FUNC(file_s_extattr_get_link), -1);
335
+
336
+ rb_define_method(rb_cFile, "extattr_set", RUBY_METHOD_FUNC(file_extattr_set), -1);
337
+ rb_define_singleton_method(rb_cFile, "extattr_set", RUBY_METHOD_FUNC(file_s_extattr_set), -1);
338
+ rb_define_singleton_method(rb_cFile, "extattr_set!", RUBY_METHOD_FUNC(file_s_extattr_set_link), -1);
339
+
340
+ rb_define_method(rb_cFile, "extattr_delete", RUBY_METHOD_FUNC(file_extattr_delete), -1);
341
+ rb_define_singleton_method(rb_cFile, "extattr_delete", RUBY_METHOD_FUNC(file_s_extattr_delete), -1);
342
+ rb_define_singleton_method(rb_cFile, "extattr_delete!", RUBY_METHOD_FUNC(file_s_extattr_delete_link), -1);
343
+
344
+ setup();
345
+ }
data/ext/extattr.linux ADDED
@@ -0,0 +1,209 @@
1
+ #include <sys/types.h>
2
+ #include <attr/xattr.h>
3
+
4
+ #define EXTATTR_NAMESPACE_USER 0
5
+ #define EXTATTR_NAMESPACE_SYSTEM 1
6
+
7
+ static VALUE NAMESPACE_USER_PREFIX, NAMESPACE_SYSTEM_PREFIX;
8
+
9
+
10
+ static VALUE
11
+ extattr_list0(ssize_t (*func)(), void *d, int namespace)
12
+ {
13
+ ssize_t size = 65536;
14
+ VALUE buf = rb_str_buf_new(size);
15
+ char *ptr = RSTRING_PTR(buf);
16
+ size = func(d, ptr, size);
17
+ if (size < 0) { rb_sys_fail("listxattr call error"); }
18
+
19
+ VALUE (*reduce)(void *, VALUE);
20
+ void *first;
21
+ VALUE list;
22
+ if (rb_block_given_p()) {
23
+ reduce = (VALUE (*)(void *, VALUE))rb_yield_values;
24
+ first = (void *)1;
25
+ list = Qnil;
26
+ } else {
27
+ reduce = (VALUE (*)(void *, VALUE))rb_ary_push;
28
+ first = (void *)(list = rb_ary_new());
29
+ }
30
+
31
+ const char *end = ptr + size;
32
+ while (ptr < end) {
33
+ int len = strlen(ptr);
34
+ VALUE name;
35
+ if (namespace == EXTATTR_NAMESPACE_USER && len > 5 && strncmp(ptr, "user.", 5) == 0) {
36
+ ptr += 5;
37
+ } else if (namespace == EXTATTR_NAMESPACE_SYSTEM && len > 7 && strncmp(ptr, "system.", 7) == 0) {
38
+ ptr += 7;
39
+ } else {
40
+ ptr += len + 1;
41
+ continue;
42
+ }
43
+
44
+ name = rb_str_new_cstr(ptr);
45
+ reduce(first, name);
46
+ ptr += RSTRING_LEN(name) + 1; // 最後の『+1』は、ヌルバイトの分。
47
+ }
48
+
49
+ return list;
50
+ }
51
+
52
+ static VALUE
53
+ file_extattr_list0(VALUE file, int fd, int namespace)
54
+ {
55
+ return extattr_list0(flistxattr, (void *)fd, namespace);
56
+ }
57
+
58
+ static VALUE
59
+ file_s_extattr_list0(VALUE path, int namespace)
60
+ {
61
+ return extattr_list0(listxattr, StringValueCStr(path), namespace);
62
+ }
63
+
64
+ static VALUE
65
+ file_s_extattr_list_link0(VALUE path, int namespace)
66
+ {
67
+ return extattr_list0(llistxattr, StringValueCStr(path), namespace);
68
+ }
69
+
70
+
71
+ static VALUE
72
+ xattr_name(int namespace, VALUE name)
73
+ {
74
+ switch (namespace) {
75
+ case EXTATTR_NAMESPACE_USER:
76
+ return rb_str_plus(NAMESPACE_USER_PREFIX, name);
77
+ case EXTATTR_NAMESPACE_SYSTEM:
78
+ return rb_str_plus(NAMESPACE_SYSTEM_PREFIX, name);
79
+ default:
80
+ rb_raise(rb_eRuntimeError, "namespace error");
81
+ return Qnil;
82
+ }
83
+ }
84
+
85
+ static VALUE
86
+ extattr_size0(ssize_t (*func)(), void *d, int namespace, VALUE name)
87
+ {
88
+ name = xattr_name(namespace, name);
89
+ ssize_t size = func(d, StringValueCStr(name), NULL, 0);
90
+ if (size < 0) { rb_sys_fail("getxattr call error"); }
91
+ return SSIZET2NUM(size);
92
+ }
93
+
94
+ static VALUE
95
+ file_extattr_size0(VALUE file, int fd, int namespace, VALUE name)
96
+ {
97
+ return extattr_size0(fgetxattr, (void *)fd, namespace, name);
98
+ }
99
+
100
+ static VALUE
101
+ file_s_extattr_size0(VALUE path, int namespace, VALUE name)
102
+ {
103
+ return extattr_size0(getxattr, StringValueCStr(path), namespace, name);
104
+ }
105
+
106
+ static VALUE
107
+ file_s_extattr_size_link0(VALUE path, int namespace, VALUE name)
108
+ {
109
+ return extattr_size0(lgetxattr, StringValueCStr(path), namespace, name);
110
+ }
111
+
112
+
113
+ static VALUE
114
+ extattr_get0(ssize_t (*func)(), void *d, int namespace, VALUE name)
115
+ {
116
+ name = xattr_name(namespace, name);
117
+ ssize_t size = 65536;
118
+ VALUE buf = rb_str_buf_new(size);
119
+ char *ptr = RSTRING_PTR(buf);
120
+ size = func(d, StringValueCStr(name), ptr, size);
121
+ if (size < 0) { rb_sys_fail("getxattr call error"); }
122
+ rb_str_set_len(buf, size);
123
+ return buf;
124
+ }
125
+
126
+ static VALUE
127
+ file_extattr_get0(VALUE file, int fd, int namespace, VALUE name)
128
+ {
129
+ return extattr_get0(fgetxattr, (void *)fd, namespace, name);
130
+ }
131
+
132
+ static VALUE
133
+ file_s_extattr_get0(VALUE path, int namespace, VALUE name)
134
+ {
135
+ return extattr_get0(getxattr, StringValueCStr(path), namespace, name);
136
+ }
137
+
138
+ static VALUE
139
+ file_s_extattr_get_link0(VALUE path, int namespace, VALUE name)
140
+ {
141
+ return extattr_get0(lgetxattr, StringValueCStr(path), namespace, name);
142
+ }
143
+
144
+
145
+ static VALUE
146
+ extattr_set0(int (*func)(), void *d, int namespace, VALUE name, VALUE data)
147
+ {
148
+ name = xattr_name(namespace, name);
149
+ int status = func(d, StringValueCStr(name), RSTRING_PTR(data), RSTRING_LEN(data));
150
+ if (status < 0) { rb_sys_fail("getxattr call error"); }
151
+ return Qnil;
152
+ }
153
+
154
+ static VALUE
155
+ file_extattr_set0(VALUE file, int fd, int namespace, VALUE name, VALUE data)
156
+ {
157
+ return extattr_set0(fsetxattr, (void *)fd, namespace, name, data);
158
+ }
159
+
160
+ static VALUE
161
+ file_s_extattr_set0(VALUE path, int namespace, VALUE name, VALUE data)
162
+ {
163
+ return extattr_set0(setxattr, StringValueCStr(path), namespace, name, data);
164
+ }
165
+
166
+ static VALUE
167
+ file_s_extattr_set_link0(VALUE path, int namespace, VALUE name, VALUE data)
168
+ {
169
+ return extattr_set0(lsetxattr, StringValueCStr(path), namespace, name, data);
170
+ }
171
+
172
+
173
+ static VALUE
174
+ extattr_delete0(int (*func)(), void *d, int namespace, VALUE name)
175
+ {
176
+ name = xattr_name(namespace, name);
177
+ int status = func(d, StringValueCStr(name));
178
+ if (status < 0) { rb_sys_fail("removexattr call error"); }
179
+ return Qnil;
180
+ }
181
+
182
+ static VALUE
183
+ file_extattr_delete0(VALUE file, int fd, int namespace, VALUE name)
184
+ {
185
+ return extattr_delete0(fremovexattr, (void *)fd, namespace, name);
186
+ }
187
+
188
+ static VALUE
189
+ file_s_extattr_delete0(VALUE path, int namespace, VALUE name)
190
+ {
191
+ return extattr_delete0(removexattr, StringValueCStr(path), namespace, name);
192
+ }
193
+
194
+ static VALUE
195
+ file_s_extattr_delete_link0(VALUE path, int namespace, VALUE name)
196
+ {
197
+ return extattr_delete0(lremovexattr, StringValueCStr(path), namespace, name);
198
+ }
199
+
200
+
201
+ static void
202
+ setup(void)
203
+ {
204
+ NAMESPACE_USER_PREFIX = rb_str_new_cstr("user.");
205
+ NAMESPACE_SYSTEM_PREFIX = rb_str_new_cstr("system.");
206
+
207
+ rb_gc_register_mark_object(NAMESPACE_USER_PREFIX);
208
+ rb_gc_register_mark_object(NAMESPACE_SYSTEM_PREFIX);
209
+ }
@@ -0,0 +1,407 @@
1
+ #include <windows.h>
2
+ #include <windowsx.h>
3
+ #include <winnt.h>
4
+ #include <ddk/ntifs.h>
5
+ #include <ddk/winddk.h>
6
+ #include <ntdef.h>
7
+ #include <psapi.h>
8
+ #include <wchar.h>
9
+
10
+ #define EXTATTR_NAMESPACE_USER 0
11
+ #define EXTATTR_NAMESPACE_SYSTEM 1
12
+
13
+ /*
14
+ * ADS は普通のファイルのように扱えるから、extattr とみなして扱う場合は容量の制限を設けることにする。
15
+ * ruby-extattr で不十分な巨大なデータを扱う場合は File.open で開くことが出来るので必要であればそちらで。
16
+ * TODO: 最適値を探すべき
17
+ */
18
+ static const size_t EXTATTR_MAX = 131072;
19
+
20
+ /*
21
+ * TODO: バッファサイズの確保量が結構大きめ。最適化をするべき。
22
+ * TODO: CloseHandle してないところがありすぎ。修正。
23
+ * TODO: Win9X シリーズだとこのライブラリ読み込みすら出来ない。
24
+ * メソッド定義だけは行うようにするべき? (実際に呼び出したら NotImpError 例外を投げる)
25
+ * リパースポイントって、ADSはつけられないのかな? 操作方法がわからない。
26
+ */
27
+
28
+
29
+ static rb_encoding *ENCutf8p;
30
+ static VALUE ENCutf8;
31
+ static rb_encoding *ENCutf16lep;
32
+ static VALUE ENCutf16le;
33
+
34
+
35
+
36
+
37
+ static VALUE
38
+ str2wcs(VALUE str)
39
+ {
40
+ str = rb_str_encode(str, ENCutf16le, 0, Qnil);
41
+ rb_str_buf_cat(str, "\0", 1);
42
+ return str;
43
+ }
44
+
45
+ static VALUE
46
+ str2wpath(VALUE str)
47
+ {
48
+ str = str2wcs(str);
49
+ wchar_t *p = (wchar_t *)RSTRING_PTR(str);
50
+ const wchar_t *end = p + RSTRING_LEN(str) / 2;
51
+ for (; p < end; p ++) {
52
+ if (*p == L'/') { *p = L'\\'; }
53
+ }
54
+ return str;
55
+ }
56
+
57
+ #define STR2WPATH(str) \
58
+ ({ \
59
+ (str) = str2wpath(str); \
60
+ (const wchar_t *)RSTRING_PTR(str); \
61
+ }) \
62
+
63
+
64
+ static VALUE
65
+ wcs2str(const wchar_t str[], size_t size)
66
+ {
67
+ if (!str) { return Qnil; }
68
+ VALUE v = rb_str_new((const char *)str, size * sizeof(str[0]));
69
+ rb_enc_associate(v, ENCutf16lep);
70
+
71
+ return rb_str_encode(v, ENCutf8, ECONV_INVALID_REPLACE | ECONV_UNDEF_REPLACE, Qnil);
72
+ }
73
+
74
+ static VALUE
75
+ wpath2str(const wchar_t path[], size_t size)
76
+ {
77
+ VALUE v = wcs2str(path, size);
78
+ if (NIL_P(v)) { return Qnil; }
79
+ char *p = RSTRING_PTR(v);
80
+ const char *end = p + RSTRING_LEN(v);
81
+ for (; p < end; p ++) {
82
+ if (*p == '\\') { *p = '/'; }
83
+ }
84
+ return v;
85
+ }
86
+
87
+ /*
88
+ * ADS 名の先端のコロン『:』と、終端の『:$DATA』を除去してrbuyの文字列を返す。
89
+ */
90
+ static VALUE
91
+ adsname2str(const wchar_t name[], size_t size)
92
+ {
93
+ if (name[0] == L':' && wcsncmp(name + size - 6, L":$DATA", 6) == 0) {
94
+ name += 1;
95
+ size -= 7;
96
+ }
97
+
98
+ return wpath2str(name, size);
99
+ }
100
+
101
+
102
+ static void
103
+ raise_win32_error(DWORD status)
104
+ {
105
+ rb_raise(rb_eSystemCallError, "Win32 API error - %u (%08Xh)", status, status);
106
+ }
107
+
108
+ static void
109
+ raise_ntstatus_error(NTSTATUS status)
110
+ {
111
+ rb_raise(rb_eSystemCallError, "NTSTATUS error - %u (%08Xh)", status, status);
112
+ }
113
+
114
+ static VALUE
115
+ get_filepath(HANDLE file)
116
+ {
117
+ DWORD size = 65536;
118
+ VALUE buf = rb_str_buf_new(size);
119
+ NTSTATUS status = NtQueryObject(file,
120
+ ObjectNameInformation,
121
+ RSTRING_PTR(buf),
122
+ size,
123
+ &size);
124
+ if (status != 0) { raise_ntstatus_error(status); }
125
+ const OBJECT_NAME_INFORMATION *info = (const OBJECT_NAME_INFORMATION *)RSTRING_PTR(buf);
126
+ if (wcsnicmp(info->Name.Buffer, L"\\Device\\", 8) == 0) {
127
+ // 先頭の "/Device/" を "//?/" に置き換える
128
+ wcsncpy(info->Name.Buffer + 4, L"\\\\?\\", 4);
129
+ return wpath2str(info->Name.Buffer + 4, info->Name.Length / 2 - 4);
130
+ } else {
131
+ return wpath2str(info->Name.Buffer, info->Name.Length / 2);
132
+ }
133
+ }
134
+
135
+ static uint64_t
136
+ get_filesize(HANDLE file)
137
+ {
138
+ BY_HANDLE_FILE_INFORMATION info;
139
+
140
+ if (!GetFileInformationByHandle(file, &info)) {
141
+ raise_win32_error(GetLastError());
142
+ }
143
+
144
+ return ((uint64_t)info.nFileSizeHigh << 32) | info.nFileSizeLow;
145
+ }
146
+
147
+ static VALUE
148
+ file_close(HANDLE file)
149
+ {
150
+ /*
151
+ * rb_ensure から直接呼びたかったけど、呼び出し規約が違うから無理だよね。
152
+ */
153
+ CloseHandle(file);
154
+ }
155
+
156
+ static void
157
+ check_namespace(int namespace)
158
+ {
159
+ if (namespace != EXTATTR_NAMESPACE_USER) {
160
+ errno = EPERM;
161
+ rb_sys_fail(NULL);
162
+ }
163
+ }
164
+
165
+
166
+ static VALUE
167
+ extattr_list0(HANDLE file)
168
+ {
169
+ VALUE iostatusblock = rb_str_buf_new(4096);
170
+ VALUE infobuf = rb_str_buf_new(65536);
171
+ rb_str_set_len(infobuf, 65536);
172
+ NTSTATUS status = NtQueryInformationFile(file,
173
+ (PIO_STATUS_BLOCK)RSTRING_PTR(iostatusblock),
174
+ RSTRING_PTR(infobuf), RSTRING_LEN(infobuf),
175
+ FileStreamInformation);
176
+ if (status != 0) { raise_ntstatus_error(status); }
177
+ const char *ptr = RSTRING_PTR(infobuf);
178
+ VALUE list = Qnil;
179
+ if (!rb_block_given_p()) { list = rb_ary_new(); }
180
+ for (;;) {
181
+ const FILE_STREAM_INFORMATION *info = (const FILE_STREAM_INFORMATION *)ptr;
182
+ VALUE name = adsname2str(info->StreamName, info->StreamNameLength / 2);
183
+ if (RSTRING_LEN(name) > 0) {
184
+ if (NIL_P(list)) {
185
+ rb_yield(name);
186
+ } else {
187
+ rb_ary_push(list, name);
188
+ }
189
+ }
190
+ size_t size = info->NextEntryOffset;
191
+ if (size == 0) { break; }
192
+ ptr += size;
193
+ }
194
+ return list;
195
+ }
196
+
197
+ static VALUE
198
+ extattr_size0(HANDLE file)
199
+ {
200
+ return ULL2NUM(get_filesize(file));
201
+ }
202
+
203
+
204
+ static VALUE
205
+ file_extattr_list0(VALUE file, int fd, int namespace)
206
+ {
207
+ check_namespace(namespace);
208
+ HANDLE file1 = (HANDLE)_get_osfhandle(fd);
209
+ return extattr_list0(file1);
210
+ }
211
+
212
+ static VALUE
213
+ file_extattr_size0(VALUE file, int fd, int namespace, VALUE name)
214
+ {
215
+ return file_s_extattr_size0(get_filepath((HANDLE)_get_osfhandle(fd)),
216
+ namespace, name);
217
+ }
218
+
219
+ static VALUE
220
+ file_extattr_get0(VALUE file, int fd, int namespace, VALUE name)
221
+ {
222
+ return file_s_extattr_get0(get_filepath((HANDLE)_get_osfhandle(fd)),
223
+ namespace, name);
224
+ }
225
+
226
+ static VALUE
227
+ file_extattr_set0(VALUE file, int fd, int namespace, VALUE name, VALUE data)
228
+ {
229
+ return file_s_extattr_set0(get_filepath((HANDLE)_get_osfhandle(fd)),
230
+ namespace, name, data);
231
+ }
232
+
233
+ static VALUE
234
+ file_extattr_delete0(VALUE file, int fd, int namespace, VALUE name)
235
+ {
236
+ return file_s_extattr_delete0(get_filepath((HANDLE)_get_osfhandle(fd)),
237
+ namespace, name);
238
+ }
239
+
240
+
241
+ static VALUE
242
+ file_s_extattr_list0(VALUE path, int namespace)
243
+ {
244
+ check_namespace(namespace);
245
+ HANDLE file = CreateFileW(STR2WPATH(path), 0,
246
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
247
+ NULL, 3,
248
+ FILE_FLAG_BACKUP_SEMANTICS,
249
+ 0);
250
+ if (file == INVALID_HANDLE_VALUE) { raise_win32_error(GetLastError()); }
251
+ return rb_ensure(extattr_list0, (VALUE)file, file_close, (VALUE)file);
252
+ }
253
+
254
+ static VALUE
255
+ file_s_extattr_list_link0(VALUE path, int namespace)
256
+ {
257
+ check_namespace(namespace);
258
+ HANDLE file = CreateFileW(STR2WPATH(path), 0,
259
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
260
+ NULL, 3,
261
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
262
+ 0);
263
+ if (file == INVALID_HANDLE_VALUE) { raise_win32_error(GetLastError()); }
264
+ return rb_ensure(extattr_list0, (VALUE)file, file_close, (VALUE)file);
265
+ }
266
+
267
+ static VALUE
268
+ file_s_extattr_size0(VALUE path, int namespace, VALUE name)
269
+ {
270
+ check_namespace(namespace);
271
+ path = rb_str_plus(path, rb_str_new(":", 1));
272
+ rb_str_append(path, name);
273
+ HANDLE file = CreateFileW(STR2WPATH(path), 0,
274
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
275
+ NULL, 3,
276
+ FILE_FLAG_BACKUP_SEMANTICS,
277
+ 0);
278
+ if (file == INVALID_HANDLE_VALUE) { raise_win32_error(GetLastError()); }
279
+ return rb_ensure(extattr_size0, (VALUE)file, file_close, (VALUE)file);
280
+ }
281
+
282
+ static VALUE
283
+ file_s_extattr_size_link0(VALUE path, int namespace, VALUE name)
284
+ {
285
+ check_namespace(namespace);
286
+ path = rb_str_plus(path, rb_str_new(":", 1));
287
+ rb_str_append(path, name);
288
+ HANDLE file = CreateFileW(STR2WPATH(path), 0,
289
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
290
+ NULL, 3,
291
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
292
+ 0);
293
+ if (file == INVALID_HANDLE_VALUE) { raise_win32_error(GetLastError()); }
294
+ return rb_ensure(extattr_size0, (VALUE)file, file_close, (VALUE)file);
295
+ }
296
+
297
+
298
+ static VALUE
299
+ extattr_get0(VALUE args[3])
300
+ {
301
+ HANDLE file = (HANDLE)args[0];
302
+ int namespace = (int)args[1];
303
+ VALUE name = args[2];
304
+ uint64_t size = get_filesize(file);
305
+ if (size > EXTATTR_MAX) { rb_raise(rb_eSystemCallError, "extattr too huge"); }
306
+ VALUE buf = rb_str_buf_new(size);
307
+ DWORD readsize = 0;
308
+ if (!ReadFile(file, RSTRING_PTR(buf), size, &readsize, NULL)) {
309
+ raise_win32_error(GetLastError());
310
+ }
311
+ rb_str_set_len(buf, readsize);
312
+ return buf;
313
+ }
314
+
315
+ static VALUE
316
+ extattr_get1(VALUE path, int flags, int namespace, VALUE name)
317
+ {
318
+ check_namespace(namespace);
319
+ path = rb_str_plus(path, rb_str_new(":", 1));
320
+ rb_str_append(path, name);
321
+ HANDLE file = CreateFileW(STR2WPATH(path), GENERIC_READ,
322
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
323
+ NULL, 3,
324
+ FILE_FLAG_BACKUP_SEMANTICS | flags,
325
+ 0);
326
+ if (file == INVALID_HANDLE_VALUE) { raise_win32_error(GetLastError()); }
327
+ VALUE args[] = { (VALUE)file, (VALUE)namespace, name, };
328
+ return rb_ensure(extattr_get0, (VALUE)args, file_close, (VALUE)file);
329
+ }
330
+
331
+ static VALUE
332
+ file_s_extattr_get0(VALUE path, int namespace, VALUE name)
333
+ {
334
+ return extattr_get1(path, 0, namespace, name);
335
+ }
336
+
337
+ static VALUE
338
+ file_s_extattr_get_link0(VALUE path, int namespace, VALUE name)
339
+ {
340
+ return extattr_get1(path, FILE_FLAG_OPEN_REPARSE_POINT, namespace, name);
341
+ }
342
+
343
+ static VALUE
344
+ extattr_set0(VALUE args[])
345
+ {
346
+ HANDLE file = (HANDLE)args[0];
347
+ int namespace = (int)args[1];
348
+ VALUE name = (VALUE)args[2];
349
+ VALUE data = (VALUE)args[3];
350
+ uint64_t size = RSTRING_LEN(data);
351
+ if (size > EXTATTR_MAX) { rb_raise(rb_eSystemCallError, "extattr too huge"); }
352
+ DWORD wrotesize = size;
353
+ if (!WriteFile(file, RSTRING_PTR(data), size, &wrotesize, NULL)) {
354
+ raise_win32_error(GetLastError());
355
+ }
356
+ return Qnil;
357
+ }
358
+
359
+ static VALUE
360
+ file_s_extattr_set0(VALUE path, int namespace, VALUE name, VALUE data)
361
+ {
362
+ check_namespace(namespace);
363
+ path = rb_str_plus(path, rb_str_new(":", 1));
364
+ rb_str_append(path, name);
365
+ HANDLE file = CreateFileW(STR2WPATH(path), GENERIC_WRITE,
366
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
367
+ NULL, CREATE_ALWAYS,
368
+ FILE_FLAG_BACKUP_SEMANTICS,
369
+ 0);
370
+ if (file == INVALID_HANDLE_VALUE) { raise_win32_error(GetLastError()); }
371
+ VALUE args[] = { (VALUE)file, (VALUE)namespace, name, data };
372
+ return rb_ensure(extattr_set0, (VALUE)args, file_close, (VALUE)file);
373
+ }
374
+
375
+ static VALUE
376
+ file_s_extattr_set_link0(VALUE path, int namespace, VALUE name, VALUE data)
377
+ {
378
+ return file_s_extattr_set0(path, namespace, name, data);
379
+ }
380
+
381
+ static VALUE
382
+ file_s_extattr_delete0(VALUE path, int namespace, VALUE name)
383
+ {
384
+ check_namespace(namespace);
385
+ path = rb_str_plus(path, rb_str_new(":", 1));
386
+ rb_str_append(path, name);
387
+ DeleteFileW(STR2WPATH(path));
388
+ return Qnil;
389
+ }
390
+
391
+ static VALUE
392
+ file_s_extattr_delete_link0(VALUE path, int namespace, VALUE name)
393
+ {
394
+ return file_s_extattr_delete0(path, namespace, name);
395
+ }
396
+
397
+ static void
398
+ setup(void)
399
+ {
400
+ ENCutf8p = rb_enc_find("UTF-8");
401
+ ENCutf8 = rb_enc_from_encoding(ENCutf8p);
402
+ rb_gc_register_address(&ENCutf8);
403
+
404
+ ENCutf16lep = rb_enc_find("UTF-16LE");
405
+ ENCutf16le = rb_enc_from_encoding(ENCutf16lep);
406
+ rb_gc_register_address(&ENCutf16le);
407
+ }
data/ext/extconf.rb ADDED
@@ -0,0 +1,28 @@
1
+ #!ruby
2
+ #vim: set fileencoding:utf-8
3
+
4
+ raise "require ruby-1.9.3+" unless RUBY_VERSION >= "1.9.3"
5
+
6
+ require "mkmf"
7
+
8
+ #$CFLAGS << " -std=c99"
9
+
10
+ case
11
+ when have_header("sys/extattr.h")
12
+ when have_header("attr/xattr.h")
13
+ when have_header("winnt.h") && have_header("ntdef.h") && have_header("psapi.h") &&
14
+ have_header("ddk/ntifs.h") && have_header("ddk/winddk.h") &&
15
+ have_library("ntoskrnl") && have_library("ntdll") && have_library("psapi")
16
+ else
17
+ $stderr.puts <<-EOS
18
+ #$0: not supported target.
19
+ \tmust be available either ddk/ntifs.h, sys/extattr.h or attr/xattr.h on your system.
20
+ EOS
21
+ exit 1
22
+ end
23
+
24
+ create_makefile "extattr" or exit 2
25
+
26
+
27
+ #$CPPFLAGS << " -Wall -DFUSE_USE_VERSION=26"
28
+
data/rspecs/extattr.rb ADDED
@@ -0,0 +1,53 @@
1
+ #vim: set fileencoding:utf-8
2
+
3
+ require "tmpdir"
4
+ basedir = File.join(Dir.tmpdir, "ruby-extattr.test-work")
5
+ Dir.mkdir basedir unless File.directory? basedir
6
+
7
+ require "extattr"
8
+
9
+ extdata = "abcdefg"
10
+
11
+ Dir.chdir basedir do
12
+ describe "file" do
13
+ file = nil
14
+ before(:all) do
15
+ file = File.open("file1", "a")
16
+ end
17
+
18
+ it ".extattr_list" do
19
+ file.extattr_list.should eq([])
20
+ end
21
+
22
+ after(:all) do
23
+ file.close
24
+ file = nil
25
+ end
26
+ end
27
+
28
+ describe File do
29
+ it ".extattr_list" do
30
+ File.extattr_list("file1").should eq([])
31
+ end
32
+
33
+ it ".extattr_set" do
34
+ File.extattr_set("file1", "ext1", extdata).should nil
35
+ end
36
+
37
+ it ".extattr_list ((2))" do
38
+ File.extattr_list("file1").should eq(["ext1"])
39
+ end
40
+
41
+ it ".extattr_get" do
42
+ File.extattr_get("file1", "ext1").should eq(extdata)
43
+ end
44
+
45
+ it ".extattr_delete" do
46
+ File.extattr_delete("file1", "ext1").should nil
47
+ end
48
+
49
+ it ".extattr_list ((3))" do
50
+ File.extattr_list("file1").should eq([])
51
+ end
52
+ end
53
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: extattr
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - dearblue
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-02 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: ! 'extattr is extended attribute operation library for ruby.
15
+
16
+ Supported for Microsoft Windows and FreeBSD.
17
+
18
+ '
19
+ email: dearblue@users.sourceforge.jp
20
+ executables: []
21
+ extensions:
22
+ - ext/extconf.rb
23
+ extra_rdoc_files:
24
+ - README.txt
25
+ - LICENSE.txt
26
+ - ext/extattr.c
27
+ files:
28
+ - README.txt
29
+ - LICENSE.txt
30
+ - ext/extconf.rb
31
+ - ext/extattr.c
32
+ - ext/extattr.bsd
33
+ - ext/extattr.linux
34
+ - ext/extattr.windows
35
+ - rspecs/extattr.rb
36
+ homepage: http://sourceforge.jp/projects/rutsubo/
37
+ licenses:
38
+ - 2-clause BSD License
39
+ post_install_message:
40
+ rdoc_options:
41
+ - -e
42
+ - UTF-8
43
+ - -m
44
+ - README.txt
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ! '>='
51
+ - !ruby/object:Gem::Version
52
+ version: 1.9.3
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ! '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ requirements: []
60
+ rubyforge_project:
61
+ rubygems_version: 1.8.24
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: extended attribute operation library for ruby
65
+ test_files: []
66
+ has_rdoc: false