extattr 0.1 → 0.4
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.
- checksums.yaml +7 -0
- data/HISTORY.ja.md +49 -0
- data/{LICENSE.txt → LICENSE.md} +5 -0
- data/QUICKREF.ja.md +112 -0
- data/README.md +84 -0
- data/Rakefile +212 -0
- data/ext/extattr-extattr.h +192 -0
- data/ext/extattr-windows.h +724 -0
- data/ext/extattr-xattr.h +229 -0
- data/ext/extattr.c +393 -206
- data/ext/extconf.rb +16 -12
- data/gemstub.rb +22 -0
- data/lib/extattr.rb +274 -0
- data/test/test_extattr.rb +75 -0
- metadata +70 -37
- data/README.txt +0 -53
- data/ext/extattr.bsd +0 -186
- data/ext/extattr.linux +0 -209
- data/ext/extattr.windows +0 -407
- data/rspecs/extattr.rb +0 -53
data/ext/extattr.windows
DELETED
@@ -1,407 +0,0 @@
|
|
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/rspecs/extattr.rb
DELETED
@@ -1,53 +0,0 @@
|
|
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
|