extattr 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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