extattr 0.1 → 0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 = 1,
23
+ EXTATTR_NAMESPACE_SYSTEM = 2,
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
+ extattr_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(mExtAttr, "IMPLEMENT", rb_str_freeze(rb_str_new_cstr("windows")));
724
+ }