extattr 0.1.2-x86-mingw32 → 0.2-x86-mingw32

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.
@@ -0,0 +1,724 @@
1
+ #include <windows.h>
2
+ #include <windowsx.h>
3
+ #include <winnt.h>
4
+ #include <psapi.h>
5
+ #include <ntdef.h>
6
+ #define PEXECUTION_STATE PEXECUTION_STATE__FAKE
7
+ #include <ddk/ntifs.h>
8
+ #include <ddk/winddk.h>
9
+ #include <wchar.h>
10
+ #include <ruby/win32.h>
11
+
12
+ /*
13
+ * - TODO:
14
+ * バッファサイズの確保量が結構大きめ。最適化をするべき。
15
+ * - TODO:
16
+ * Win9X シリーズだとこのライブラリ読み込みすら出来ない。
17
+ * メソッド定義だけは行うようにするべき? (実際に呼び出したら NotImplementedError 例外を投げる、とか)
18
+ * - リパースポイントって、ADSはつけられないのかな? 操作方法がわからない。
19
+ */
20
+
21
+ enum {
22
+ EXTATTR_NAMESPACE_USER = 0,
23
+ EXTATTR_NAMESPACE_SYSTEM = 1,
24
+
25
+ /*
26
+ * ADS は普通のファイルのように扱えるから、extattr とみなして扱う場合は容量の制限を設けることにする。
27
+ *
28
+ * ruby-extattr で不十分な巨大なデータを扱う場合は File.open で開くことが出来るので必要であればそちらで。
29
+ *
30
+ * TODO: 最適値を探すべき
31
+ */
32
+ EXT_EXTATTR_ADS_DATAMAX = 65535,
33
+ EXT_EXTATTR_ADS_NAMEMAX = 255,
34
+
35
+ EXT_EXTATTR_EA_DATAMAX = 65535,
36
+ EXT_EXTATTR_EA_NAMEMAX = 255,
37
+
38
+ EXT_NAMETERMSIZE = 1, // C string termination / '\0'
39
+ };
40
+
41
+
42
+ static rb_encoding *ENCutf8p;
43
+ static VALUE ENCutf8;
44
+ static rb_encoding *ENCutf16lep;
45
+ static VALUE ENCutf16le;
46
+
47
+
48
+ static VALUE
49
+ str2wcs(VALUE str)
50
+ {
51
+ str = rb_str_encode(str, ENCutf16le, 0, Qnil);
52
+ rb_str_buf_cat(str, "\0", 1);
53
+ return str;
54
+ }
55
+
56
+ static const wchar_t *
57
+ str2wpath(VALUE *str)
58
+ {
59
+ VALUE tmp = (*str = str2wcs(*str));
60
+ wchar_t *p = (wchar_t *)RSTRING_PTR(tmp);
61
+ const wchar_t *end = p + RSTRING_LEN(tmp) / 2;
62
+ for (; p < end; p ++) {
63
+ if (*p == L'/') { *p = L'\\'; }
64
+ }
65
+ return (const wchar_t *)RSTRING_PTR(tmp);
66
+ }
67
+
68
+ #define STR2WPATH(str) str2wpath(&str)
69
+
70
+ static VALUE
71
+ wcs2str(const wchar_t str[], size_t size)
72
+ {
73
+ if (!str) { return Qnil; }
74
+ VALUE v = rb_str_new((const char *)str, size * sizeof(str[0]));
75
+ rb_enc_associate(v, ENCutf16lep);
76
+
77
+ return rb_str_encode(v, ENCutf8, ECONV_INVALID_REPLACE | ECONV_UNDEF_REPLACE, Qnil);
78
+ }
79
+
80
+ static VALUE
81
+ wpath2str(const wchar_t path[], size_t size)
82
+ {
83
+ VALUE v = wcs2str(path, size);
84
+ if (NIL_P(v)) { return Qnil; }
85
+ char *p = RSTRING_PTR(v);
86
+ const char *end = p + RSTRING_LEN(v);
87
+ for (; p < end; p ++) {
88
+ if (*p == '\\') { *p = '/'; }
89
+ }
90
+ return v;
91
+ }
92
+
93
+ /*
94
+ * ADS 名の先端のコロン『:』と、終端の『:$DATA』を除去して ruby の文字列を返す。
95
+ */
96
+ static VALUE
97
+ adsname2str(const wchar_t name[], size_t size)
98
+ {
99
+ if (name[0] == L':') {
100
+ name ++;
101
+ size --;
102
+ if (size >= 6 && wcsncmp(name + size - 6, L":$DATA", 6) == 0) {
103
+ size -= 6;
104
+ } else {
105
+ size --;
106
+ }
107
+ }
108
+
109
+ if (size > 0) {
110
+ return wpath2str(name, size);
111
+ } else {
112
+ return Qnil;
113
+ }
114
+ }
115
+
116
+ static VALUE
117
+ ext_join_adspath(VALUE path, VALUE name)
118
+ {
119
+ return rb_str_append(rb_str_plus(path, rb_str_new(":", 1)), name);
120
+ }
121
+
122
+
123
+ static void
124
+ raise_win32_error(DWORD status, VALUE name)
125
+ {
126
+ if (NIL_P(name)) {
127
+ VALUE args[] = { INT2NUM(rb_w32_map_errno(status)), };
128
+ rb_exc_raise(rb_class_new_instance(1, args, rb_eSystemCallError));
129
+ } else {
130
+ // TODO: name がファイルオブジェクトなのであればパス名を取り出す。パス名がない場合は fd を取り出す。
131
+
132
+ VALUE args[] = { StringValue(name), INT2NUM(rb_w32_map_errno(status)), };
133
+ rb_exc_raise(rb_class_new_instance(2, args, rb_eSystemCallError));
134
+ }
135
+ }
136
+
137
+ static void
138
+ raise_ntstatus_error(NTSTATUS status, VALUE name)
139
+ {
140
+ raise_win32_error(RtlNtStatusToDosError(status), name);
141
+ }
142
+
143
+ static void
144
+ check_status_error(NTSTATUS status)
145
+ {
146
+ if (status != STATUS_SUCCESS) {
147
+ raise_ntstatus_error(status, Qnil);
148
+ }
149
+ }
150
+
151
+
152
+ static VALUE
153
+ get_filepath(HANDLE file)
154
+ {
155
+ DWORD size = 65536; // ファイルの完全パス名の最大バイト数 (UTF-16 換算で32768文字)
156
+ VALUE buf = rb_str_buf_new(size);
157
+ OBJECT_NAME_INFORMATION *info = (void *)RSTRING_PTR(buf);
158
+ NTSTATUS status = NtQueryObject(file, ObjectNameInformation,
159
+ info, size, &size);
160
+ check_status_error(status);
161
+
162
+ if (wcsnicmp(info->Name.Buffer, L"\\Device\\", 8) == 0) {
163
+ // 先頭の "/Device/" を "//?/" に置き換える
164
+ wcsncpy(info->Name.Buffer + 4, L"\\\\?\\", 4);
165
+ return wpath2str(info->Name.Buffer + 4, info->Name.Length / 2 - 4);
166
+ } else {
167
+ return wpath2str(info->Name.Buffer, info->Name.Length / 2);
168
+ }
169
+ }
170
+
171
+ static uint64_t
172
+ get_filesize(HANDLE file)
173
+ {
174
+ BY_HANDLE_FILE_INFORMATION info;
175
+
176
+ if (!GetFileInformationByHandle(file, &info)) {
177
+ raise_win32_error(GetLastError(), Qnil);
178
+ }
179
+
180
+ return ((uint64_t)info.nFileSizeHigh << 32) | info.nFileSizeLow;
181
+ }
182
+
183
+ static VALUE
184
+ file_close(HANDLE file)
185
+ {
186
+ // rb_ensure から直接呼びたかったけど、呼び出し規約が違うから無理だよね。
187
+
188
+ CloseHandle(file);
189
+
190
+ return Qnil;
191
+ }
192
+
193
+ typedef VALUE (ext_extattr_ctrl_f)(HANDLE file, VALUE pathsrc, VALUE extname, VALUE extdata);
194
+
195
+ static VALUE
196
+ ext_extattr_ctrl_common_try(VALUE args[])
197
+ {
198
+ return ((ext_extattr_ctrl_f *)args[4])((HANDLE)args[0], args[1], args[2], args[3]);
199
+ }
200
+
201
+ static VALUE
202
+ ext_extattr_ctrl_fileopen(VALUE path, int flags, int access, int create, VALUE pathsrc, VALUE name, VALUE data, ext_extattr_ctrl_f *func)
203
+ {
204
+ HANDLE file = CreateFileW(STR2WPATH(path), access,
205
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
206
+ NULL, create,
207
+ FILE_FLAG_BACKUP_SEMANTICS | flags,
208
+ 0);
209
+ if (file == INVALID_HANDLE_VALUE) { raise_win32_error(GetLastError(), pathsrc); }
210
+ VALUE args[] = { (VALUE)file, pathsrc, name, data, (VALUE)func };
211
+ return rb_ensure(ext_extattr_ctrl_common_try, (VALUE)args, file_close, (VALUE)file);
212
+ }
213
+
214
+ struct ext_extattr_ctrl_traits_t
215
+ {
216
+ int access;
217
+ int create;
218
+ ext_extattr_ctrl_f *func;
219
+ };
220
+
221
+ struct ext_extattr_ctrl_traits_set_t
222
+ {
223
+ struct ext_extattr_ctrl_traits_t user;
224
+ struct ext_extattr_ctrl_traits_t system;
225
+ };
226
+
227
+ static VALUE
228
+ ext_extattr_ctrl_ads(HANDLE file, VALUE path, int flags, VALUE pathsrc, VALUE name, VALUE data, const struct ext_extattr_ctrl_traits_t *traits)
229
+ {
230
+ if (file) {
231
+ path = get_filepath(file);
232
+ }
233
+
234
+ if (!NIL_P(name)) {
235
+ path = ext_join_adspath(path, name);
236
+ }
237
+
238
+ return ext_extattr_ctrl_fileopen(path, flags, traits->access, traits->create,
239
+ pathsrc, name, data, traits->func);
240
+ }
241
+
242
+ static VALUE
243
+ ext_extattr_ctrl_ea(HANDLE file, VALUE path, int flags, VALUE pathsrc, VALUE name, VALUE data, const struct ext_extattr_ctrl_traits_t *traits)
244
+ {
245
+ if (file) {
246
+ return traits->func(file, pathsrc, name, data);
247
+ }
248
+
249
+ return ext_extattr_ctrl_fileopen(path,
250
+ flags, traits->access, traits->create,
251
+ pathsrc, name, data, traits->func);
252
+ }
253
+
254
+ static VALUE
255
+ ext_extattr_ctrl_common(HANDLE file, VALUE path, int flags, VALUE pathsrc, int namespace1, VALUE name, VALUE data, const struct ext_extattr_ctrl_traits_set_t *traits)
256
+ {
257
+ switch (namespace1) {
258
+ case EXTATTR_NAMESPACE_USER:
259
+ return ext_extattr_ctrl_ads(file, path, flags, pathsrc, name, data, &traits->user);
260
+ case EXTATTR_NAMESPACE_SYSTEM:
261
+ return ext_extattr_ctrl_ea(file, path, flags, pathsrc, name, data, &traits->system);
262
+ default:
263
+ ext_error_namespace(pathsrc, namespace1);
264
+ return Qnil;
265
+ }
266
+ }
267
+
268
+ /*
269
+ * extattr_list
270
+ */
271
+
272
+ static void
273
+ extattr_list_ads_name(const char *ptr, VALUE infection_source, void *(*func)(void *, VALUE), void *user)
274
+ {
275
+ for (;;) {
276
+ const FILE_STREAM_INFORMATION *info = (const FILE_STREAM_INFORMATION *)ptr;
277
+ VALUE name = adsname2str(info->StreamName, info->StreamNameLength / 2);
278
+ if (!NIL_P(name) && RSTRING_LEN(name) > 0) {
279
+ OBJ_INFECT(name, infection_source);
280
+ func(user, name);
281
+ }
282
+ size_t size = info->NextEntryOffset;
283
+ if (size == 0) { break; }
284
+ ptr += size;
285
+ }
286
+ }
287
+
288
+ typedef void *(extattr_list_ea_push_f)(void *, VALUE);
289
+
290
+ static VALUE
291
+ ext_extattr_list_ads_main(HANDLE file, VALUE pathsrc, VALUE name, VALUE data)
292
+ {
293
+ VALUE iostatusblock = rb_str_buf_new(4096);
294
+ size_t size = 65536; // TODO: 最適値を見つける
295
+ VALUE infobuf = rb_str_buf_new(size);
296
+ char *ptr = RSTRING_PTR(infobuf);
297
+ memset(ptr, 0, sizeof(FILE_STREAM_INFORMATION));
298
+ NTSTATUS status = NtQueryInformationFile(file,
299
+ (PIO_STATUS_BLOCK)RSTRING_PTR(iostatusblock),
300
+ ptr, size,
301
+ FileStreamInformation);
302
+ check_status_error(status);
303
+
304
+ if (rb_block_given_p()) {
305
+ extattr_list_ads_name(ptr, pathsrc,
306
+ (void *(*)(void *, VALUE))rb_yield_values,
307
+ (void *)1);
308
+ return Qnil;
309
+ } else {
310
+ VALUE list = rb_ary_new();
311
+ OBJ_INFECT(list, pathsrc);
312
+ extattr_list_ads_name(ptr, pathsrc,
313
+ (void *(*)(void *, VALUE))rb_ary_push,
314
+ (void *)list);
315
+ return list;
316
+ }
317
+ }
318
+
319
+ typedef void *(extattr_list_ea_push_f)(void *, VALUE);
320
+
321
+ static void
322
+ extattr_list_ea_name(HANDLE file, VALUE infection_source, IO_STATUS_BLOCK *iostatusblock, extattr_list_ea_push_f *func, void *funcparam)
323
+ {
324
+ size_t bufsize = 4096;
325
+ VALUE infobuf;
326
+ FILE_FULL_EA_INFORMATION *info = (void *)ALLOCV(infobuf, bufsize);
327
+ ULONG index = 1; // one based index
328
+ for (;;) {
329
+ NTSTATUS status = NtQueryEaFile(file, iostatusblock, info, bufsize,
330
+ TRUE, NULL, 0, &index, (index == 1 ? TRUE : FALSE));
331
+ if (status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL) {
332
+ bufsize *= 2;
333
+ rb_str_resize(infobuf, bufsize);
334
+ info = (void *)RSTRING_PTR(infobuf);
335
+ continue;
336
+ } else if (status == STATUS_NO_MORE_EAS) {
337
+ break;
338
+ }
339
+ check_status_error(status);
340
+ if (info->EaNameLength > 0) {
341
+ VALUE name = rb_str_new(info->EaName, info->EaNameLength);
342
+ OBJ_INFECT(name, infection_source);
343
+ func(funcparam, name);
344
+ }
345
+ index ++;
346
+ }
347
+ ALLOCV_END(infobuf);
348
+ }
349
+
350
+ static VALUE
351
+ ext_extattr_list_ea_main(HANDLE file, VALUE pathsrc, VALUE name, VALUE data)
352
+ {
353
+ VALUE iostatusblock_pool = rb_str_buf_new(4096);
354
+ IO_STATUS_BLOCK *iostatusblock = (IO_STATUS_BLOCK *)RSTRING_PTR(iostatusblock_pool);
355
+ FILE_EA_INFORMATION eainfo;
356
+ NTSTATUS status = NtQueryInformationFile(file, iostatusblock,
357
+ &eainfo, sizeof(eainfo), FileEaInformation);
358
+ check_status_error(status);
359
+
360
+ VALUE namelist;
361
+ extattr_list_ea_push_f *func;
362
+ void *funcparam;
363
+ if (rb_block_given_p()) {
364
+ namelist = Qnil;
365
+ func = (extattr_list_ea_push_f *)rb_yield_values;
366
+ funcparam = (void *)(1); // call as rb_yield_values(1, name)
367
+ } else {
368
+ namelist = rb_ary_new();
369
+ OBJ_INFECT(namelist, pathsrc);
370
+ func = (extattr_list_ea_push_f *)rb_ary_push;
371
+ funcparam = (void *)(namelist); // call as rb_ary_push(namelist, name)
372
+ }
373
+
374
+ if (eainfo.EaSize == 0) {
375
+ return namelist;
376
+ }
377
+
378
+ extattr_list_ea_name(file, pathsrc, iostatusblock, func, funcparam);
379
+
380
+ return namelist;
381
+ }
382
+
383
+ static const struct ext_extattr_ctrl_traits_set_t ext_extattr_ctrl_traits_list = {
384
+ .user = {
385
+ .access = GENERIC_READ,
386
+ .create = OPEN_EXISTING,
387
+ .func = ext_extattr_list_ads_main,
388
+ },
389
+ .system = {
390
+ .access = GENERIC_READ,
391
+ .create = OPEN_EXISTING,
392
+ .func = ext_extattr_list_ea_main,
393
+ },
394
+ };
395
+
396
+ static VALUE
397
+ file_extattr_list_main(VALUE file, int fd, int namespace1)
398
+ {
399
+ return ext_extattr_ctrl_common((HANDLE)_get_osfhandle(fd), Qnil, 0,
400
+ file, namespace1, Qnil, Qnil,
401
+ &ext_extattr_ctrl_traits_list);
402
+ }
403
+
404
+ static VALUE
405
+ file_s_extattr_list_main(VALUE path, int namespace1)
406
+ {
407
+ return ext_extattr_ctrl_common(0, path, 0,
408
+ path, namespace1, Qnil, Qnil,
409
+ &ext_extattr_ctrl_traits_list);
410
+ }
411
+
412
+ static VALUE
413
+ file_s_extattr_list_link_main(VALUE path, int namespace1)
414
+ {
415
+ return ext_extattr_ctrl_common(0, path, FILE_FLAG_OPEN_REPARSE_POINT,
416
+ path, namespace1, Qnil, Qnil,
417
+ &ext_extattr_ctrl_traits_list);
418
+ }
419
+
420
+ /*
421
+ * extattr_size
422
+ */
423
+
424
+ static VALUE
425
+ ext_extattr_size_ads_main(HANDLE file, VALUE pathsrc, VALUE name, VALUE data)
426
+ {
427
+ return ULL2NUM(get_filesize(file));
428
+ }
429
+
430
+ static VALUE ext_extattr_get_ea_main(HANDLE file, VALUE pathsrc, VALUE name, VALUE data);
431
+
432
+ static VALUE
433
+ ext_extattr_size_ea_main(HANDLE file, VALUE pathsrc, VALUE name, VALUE data)
434
+ {
435
+ VALUE v = ext_extattr_get_ea_main(file, pathsrc, name, data);
436
+ return SIZET2NUM(RSTRING_LEN(v));
437
+ }
438
+
439
+ static const struct ext_extattr_ctrl_traits_set_t ext_extattr_ctrl_traits_size = {
440
+ .user = {
441
+ .access = GENERIC_READ,
442
+ .create = OPEN_EXISTING,
443
+ .func = ext_extattr_size_ads_main,
444
+ },
445
+ .system = {
446
+ .access = GENERIC_READ,
447
+ .create = OPEN_EXISTING,
448
+ .func = ext_extattr_size_ea_main,
449
+ },
450
+ };
451
+
452
+ static VALUE
453
+ file_extattr_size_main(VALUE file, int fd, int namespace1, VALUE name)
454
+ {
455
+ return ext_extattr_ctrl_common((HANDLE)_get_osfhandle(fd), Qnil, 0,
456
+ file, namespace1, name, Qnil,
457
+ &ext_extattr_ctrl_traits_size);
458
+ }
459
+
460
+ static VALUE
461
+ file_s_extattr_size_main(VALUE path, int namespace1, VALUE name)
462
+ {
463
+ return ext_extattr_ctrl_common(0, path, 0,
464
+ path, namespace1, name, Qnil,
465
+ &ext_extattr_ctrl_traits_size);
466
+ }
467
+
468
+ static VALUE
469
+ file_s_extattr_size_link_main(VALUE path, int namespace1, VALUE name)
470
+ {
471
+ return ext_extattr_ctrl_common(0, path, FILE_FLAG_OPEN_REPARSE_POINT,
472
+ path, namespace1, name, Qnil,
473
+ &ext_extattr_ctrl_traits_size);
474
+ }
475
+
476
+ /*
477
+ * extattr_get
478
+ */
479
+
480
+ static inline size_t
481
+ alignunit4(size_t num)
482
+ {
483
+ return (num + 3) & ~0x03;
484
+ }
485
+
486
+ static VALUE
487
+ ext_extattr_get_ads_main(HANDLE file, VALUE pathsrc, VALUE name, VALUE data)
488
+ {
489
+ uint64_t size = get_filesize(file);
490
+ if (size > EXT_EXTATTR_ADS_DATAMAX) { rb_raise(rb_eSystemCallError, "extattr too huge"); }
491
+ VALUE buf = rb_str_buf_new(size);
492
+ DWORD readsize = 0;
493
+ if (!ReadFile(file, RSTRING_PTR(buf), size, &readsize, NULL)) {
494
+ raise_win32_error(GetLastError(), Qnil);
495
+ }
496
+ rb_str_set_len(buf, readsize);
497
+ return buf;
498
+ }
499
+
500
+ static VALUE
501
+ ext_extattr_get_ea_main(HANDLE file, VALUE pathsrc, VALUE name, VALUE data)
502
+ {
503
+ size_t namesize = RSTRING_LEN(name);
504
+ if (namesize > EXT_EXTATTR_EA_NAMEMAX) {
505
+ ext_error_extattr(ENAMETOOLONG, pathsrc, name);
506
+ }
507
+
508
+ size_t bufsize = sizeof(FILE_FULL_EA_INFORMATION) + namesize + EXT_EXTATTR_EA_DATAMAX;
509
+ VALUE info_pool;
510
+ FILE_FULL_EA_INFORMATION *info = ALLOCV(info_pool, bufsize);
511
+ VALUE list_pool;
512
+ FILE_GET_EA_INFORMATION *list = ALLOCV(list_pool, 512);
513
+ list->NextEntryOffset = 0;
514
+ list->EaNameLength = namesize;
515
+ memcpy(list->EaName, RSTRING_PTR(name), namesize);
516
+ list->EaName[namesize] = '\0';
517
+
518
+ IO_STATUS_BLOCK iostatusblock;
519
+ memset(&iostatusblock, 0, sizeof(iostatusblock));
520
+ NTSTATUS status = NtQueryEaFile(file, &iostatusblock, info, bufsize,
521
+ TRUE, list, 4 + 1 + namesize + 1, NULL, TRUE);
522
+ check_status_error(status);
523
+
524
+ if (info->EaValueLength < 1) {
525
+ //ext_error_extattr(ENOATTR, pathsrc, name);
526
+ ext_error_extattr(ENOENT, pathsrc, name);
527
+ }
528
+ VALUE v = rb_str_buf_new(info->EaValueLength);
529
+ rb_str_buf_cat(v, info->EaName + info->EaNameLength + 1, info->EaValueLength);
530
+ return v;
531
+ }
532
+
533
+ static const struct ext_extattr_ctrl_traits_set_t ext_extattr_ctrl_traits_get = {
534
+ .user = {
535
+ .access = GENERIC_READ,
536
+ .create = OPEN_EXISTING,
537
+ .func = ext_extattr_get_ads_main,
538
+ },
539
+ .system = {
540
+ .access = GENERIC_READ,
541
+ .create = OPEN_EXISTING,
542
+ .func = ext_extattr_get_ea_main,
543
+ },
544
+ };
545
+
546
+ static VALUE
547
+ file_extattr_get_main(VALUE file, int fd, int namespace1, VALUE name)
548
+ {
549
+ return ext_extattr_ctrl_common((HANDLE)_get_osfhandle(fd), Qnil, 0,
550
+ file, namespace1, name, Qnil,
551
+ &ext_extattr_ctrl_traits_get);
552
+ }
553
+
554
+ static VALUE
555
+ file_s_extattr_get_main(VALUE path, int namespace1, VALUE name)
556
+ {
557
+ return ext_extattr_ctrl_common(0, path, 0,
558
+ path, namespace1, name, Qnil,
559
+ &ext_extattr_ctrl_traits_get);
560
+ }
561
+
562
+ static VALUE
563
+ file_s_extattr_get_link_main(VALUE path, int namespace1, VALUE name)
564
+ {
565
+ return ext_extattr_ctrl_common(0, path, FILE_FLAG_OPEN_REPARSE_POINT,
566
+ path, namespace1, name, Qnil,
567
+ &ext_extattr_ctrl_traits_get);
568
+ }
569
+
570
+ /*
571
+ * extattr_set
572
+ */
573
+
574
+ static VALUE
575
+ ext_extattr_set_ads_main(HANDLE file, VALUE pathsrc, VALUE name, VALUE data)
576
+ {
577
+ uint64_t size = RSTRING_LEN(data);
578
+ if (size > EXT_EXTATTR_ADS_DATAMAX) { ext_error_extattr(E2BIG, pathsrc, name); }
579
+ DWORD wrotesize = size;
580
+ if (!WriteFile(file, RSTRING_PTR(data), size, &wrotesize, NULL)) {
581
+ raise_win32_error(GetLastError(), Qnil);
582
+ }
583
+ return Qnil;
584
+ }
585
+
586
+ static VALUE
587
+ ext_extattr_set_ea_main(HANDLE file, VALUE pathsrc, VALUE name, VALUE data)
588
+ {
589
+ size_t namesize = RSTRING_LEN(name);
590
+ if (namesize > EXT_EXTATTR_EA_NAMEMAX) { ext_error_extattr(E2BIG, pathsrc, name); }
591
+ size_t datasize = RSTRING_LEN(data);
592
+ if (datasize > EXT_EXTATTR_EA_DATAMAX) { ext_error_extattr(E2BIG, pathsrc, name); }
593
+
594
+ size_t infosize = sizeof(FILE_FULL_EA_INFORMATION) + namesize + EXT_NAMETERMSIZE + datasize;
595
+ VALUE info_pool;
596
+ FILE_FULL_EA_INFORMATION *info = ALLOCV(info_pool, infosize);
597
+ memset(info, 0, infosize);
598
+ info->EaNameLength = namesize;
599
+ memcpy(info->EaName, RSTRING_PTR(name), namesize);
600
+ info->EaName[namesize] = '\0';
601
+ info->EaValueLength = datasize;
602
+ memcpy(info->EaName + namesize + EXT_NAMETERMSIZE, RSTRING_PTR(data), datasize);
603
+
604
+ IO_STATUS_BLOCK iostatusblock;
605
+ memset(&iostatusblock, 0, sizeof(iostatusblock));
606
+ NTSTATUS status = NtSetEaFile(file, &iostatusblock, info, infosize);
607
+ check_status_error(status);
608
+
609
+ return Qnil;
610
+ }
611
+
612
+ static const struct ext_extattr_ctrl_traits_set_t ext_extattr_ctrl_traits_set = {
613
+ .user = {
614
+ .access = GENERIC_WRITE,
615
+ .create = CREATE_ALWAYS,
616
+ .func = ext_extattr_set_ads_main,
617
+ },
618
+ .system = {
619
+ .access = GENERIC_WRITE,
620
+ .create = OPEN_EXISTING,
621
+ .func = ext_extattr_set_ea_main,
622
+ },
623
+ };
624
+
625
+ static VALUE
626
+ file_extattr_set_main(VALUE file, int fd, int namespace1, VALUE name, VALUE data)
627
+ {
628
+ return ext_extattr_ctrl_common((HANDLE)_get_osfhandle(fd), Qnil, 0,
629
+ file, namespace1, name, data,
630
+ &ext_extattr_ctrl_traits_set);
631
+ }
632
+
633
+ static VALUE
634
+ file_s_extattr_set_main(VALUE path, int namespace1, VALUE name, VALUE data)
635
+ {
636
+ return ext_extattr_ctrl_common(0, path, 0,
637
+ path, namespace1, name, data,
638
+ &ext_extattr_ctrl_traits_set);
639
+ }
640
+
641
+ static VALUE
642
+ file_s_extattr_set_link_main(VALUE path, int namespace1, VALUE name, VALUE data)
643
+ {
644
+ return ext_extattr_ctrl_common(0, path, FILE_FLAG_OPEN_REPARSE_POINT,
645
+ path, namespace1, name, data,
646
+ &ext_extattr_ctrl_traits_set);
647
+ }
648
+
649
+ /*
650
+ * extattr_delete
651
+ */
652
+
653
+ static VALUE
654
+ ext_extattr_delete_ads(HANDLE file, VALUE path, VALUE pathsrc, VALUE name)
655
+ {
656
+ if (NIL_P(path)) { path = get_filepath(file); }
657
+ VALUE path1 = ext_join_adspath(path, name);
658
+ if (!DeleteFileW(STR2WPATH(path1))) {
659
+ raise_win32_error(GetLastError(), pathsrc);
660
+ }
661
+ return Qnil;
662
+ }
663
+
664
+ static VALUE
665
+ ext_extattr_delete_ea(HANDLE file, VALUE path, int flags, VALUE pathsrc, VALUE name)
666
+ {
667
+ if (file) {
668
+ return ext_extattr_set_ea_main(file, pathsrc, name, rb_str_new(0, 0));
669
+ }
670
+
671
+ return ext_extattr_ctrl_fileopen(path, flags, GENERIC_WRITE, OPEN_EXISTING,
672
+ pathsrc, name, rb_str_new(0, 0), ext_extattr_set_ea_main);
673
+ }
674
+
675
+ static VALUE
676
+ ext_extattr_delete_common(HANDLE file, VALUE path, int flags, VALUE pathsrc, int namespace1, VALUE name)
677
+ {
678
+ switch (namespace1) {
679
+ case EXTATTR_NAMESPACE_USER:
680
+ return ext_extattr_delete_ads(file, path, pathsrc, name);
681
+ case EXTATTR_NAMESPACE_SYSTEM:
682
+ return ext_extattr_delete_ea(file, path, flags, pathsrc, name);
683
+ default:
684
+ ext_error_namespace(namespace1, pathsrc);
685
+ return Qnil;
686
+ }
687
+ return Qnil;
688
+ }
689
+
690
+ static VALUE
691
+ file_extattr_delete_main(VALUE file, int fd, int namespace1, VALUE name)
692
+ {
693
+ return ext_extattr_delete_common((HANDLE)_get_osfhandle(fd), Qnil, 0,
694
+ file, namespace1, name);
695
+ }
696
+
697
+ static VALUE
698
+ file_s_extattr_delete_main(VALUE path, int namespace1, VALUE name)
699
+ {
700
+ return ext_extattr_delete_common(0, path, 0,
701
+ path, namespace1, name);
702
+ }
703
+
704
+ static VALUE
705
+ file_s_extattr_delete_link_main(VALUE path, int namespace1, VALUE name)
706
+ {
707
+ return ext_extattr_delete_common(0, path, FILE_FLAG_OPEN_REPARSE_POINT,
708
+ path, namespace1, name);
709
+ }
710
+
711
+
712
+ static void
713
+ ext_init_implement(void)
714
+ {
715
+ ENCutf8p = rb_enc_find("UTF-8");
716
+ ENCutf8 = rb_enc_from_encoding(ENCutf8p);
717
+ rb_gc_register_address(&ENCutf8);
718
+
719
+ ENCutf16lep = rb_enc_find("UTF-16LE");
720
+ ENCutf16le = rb_enc_from_encoding(ENCutf16lep);
721
+ rb_gc_register_address(&ENCutf16le);
722
+
723
+ rb_define_const(rb_cFile, "EXTATTR_IMPLEMANT", rb_str_freeze(rb_str_new_cstr("windows")));
724
+ }