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 +24 -0
- data/README.txt +53 -0
- data/ext/extattr.bsd +186 -0
- data/ext/extattr.c +345 -0
- data/ext/extattr.linux +209 -0
- data/ext/extattr.windows +407 -0
- data/ext/extconf.rb +28 -0
- data/rspecs/extattr.rb +53 -0
- metadata +66 -0
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
|
+
}
|
data/ext/extattr.windows
ADDED
@@ -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
|