io-afc 0.0.3.2

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,739 @@
1
+ //-*- coding: utf-8
2
+ //-*- vim: sw=4:ts=4:sts=4
3
+
4
+ #include "ioafc.h"
5
+ #include <ruby/intern.h>
6
+ #include <errno.h>
7
+ #include <sys/fcntl.h>
8
+
9
+
10
+
11
+ namespace {
12
+ VALUE rb_cIoAFC = rb_define_class_under(rb_cIO, "AFCBase", rb_cObject);
13
+ VALUE rb_cIoAFCFD = rb_define_class_under(rb_cIoAFC, "Descriptor", rb_cObject);
14
+ VALUE rb_eNoDevice = rb_define_class_under(rb_cIoAFC, "NoDevice", rb_eRuntimeError);
15
+ VALUE rb_eProtected = rb_define_class_under(rb_cIoAFC, "PasswordProtected", rb_eRuntimeError);
16
+ VALUE rb_eConnectFailed = rb_define_class_under(rb_cIoAFC, "ConnectFailed", rb_eRuntimeError);
17
+ }
18
+
19
+ static void free_dictionary(char** dict)
20
+ {
21
+ if (dict) {
22
+ for (int i = 0; dict[i]; ++i) {
23
+ ::free(dict[i]);
24
+ }
25
+ ::free(dict);
26
+ }
27
+ }
28
+
29
+ static afc_file_mode_t get_afc_file_mode(int flags)
30
+ {
31
+ switch (flags & O_ACCMODE) {
32
+ case O_RDONLY:
33
+ return AFC_FOPEN_RDONLY;
34
+
35
+ case O_WRONLY:
36
+ if ((flags & O_TRUNC) == O_TRUNC) return AFC_FOPEN_WRONLY;
37
+ if ((flags & O_APPEND) == O_APPEND) return AFC_FOPEN_APPEND;
38
+ return AFC_FOPEN_RW;
39
+
40
+ case O_RDWR:
41
+ if ((flags & O_TRUNC) == O_TRUNC) return AFC_FOPEN_WR;
42
+ if ((flags & O_APPEND) == O_APPEND) return AFC_FOPEN_RDAPPEND;
43
+ return AFC_FOPEN_RW;
44
+ }
45
+ rb_raise(rb_eArgError, "Invalid open mode(%d)", flags);
46
+ }
47
+
48
+
49
+ static void rb_raise_SystemCallError(int err)
50
+ {
51
+ VALUE e = INT2FIX(err);
52
+ rb_exc_raise(rb_class_new_instance(1, &e, rb_eSystemCallError));
53
+ }
54
+
55
+
56
+ static void rb_raise_SystemCallErrorAFC(afc_error_t err)
57
+ {
58
+ struct AFC_ERRMAP {
59
+ afc_error_t afc;
60
+ int sys;
61
+ };
62
+ static const AFC_ERRMAP map[] = {
63
+ {AFC_E_SUCCESS , 0},
64
+ {AFC_E_OP_HEADER_INVALID , EIO},
65
+ {AFC_E_NO_RESOURCES , EMFILE},
66
+ {AFC_E_READ_ERROR , ENOTDIR},
67
+ {AFC_E_WRITE_ERROR , EIO},
68
+ {AFC_E_UNKNOWN_PACKET_TYPE , EIO},
69
+ {AFC_E_INVALID_ARG , EINVAL},
70
+ {AFC_E_OBJECT_NOT_FOUND , ENOENT},
71
+ {AFC_E_OBJECT_IS_DIR , EISDIR},
72
+ {AFC_E_DIR_NOT_EMPTY , ENOTEMPTY},
73
+ {AFC_E_PERM_DENIED , EPERM},
74
+ {AFC_E_SERVICE_NOT_CONNECTED, ENXIO},
75
+ {AFC_E_OP_TIMEOUT , ETIMEDOUT},
76
+ {AFC_E_TOO_MUCH_DATA , EFBIG},
77
+ {AFC_E_END_OF_DATA , ENODATA},
78
+ {AFC_E_OP_NOT_SUPPORTED , ENOSYS},
79
+ {AFC_E_OBJECT_EXISTS , EEXIST},
80
+ {AFC_E_OBJECT_BUSY , EBUSY},
81
+ {AFC_E_NO_SPACE_LEFT , ENOSPC},
82
+ {AFC_E_OP_WOULD_BLOCK , EWOULDBLOCK},
83
+ {AFC_E_IO_ERROR , EIO},
84
+ {AFC_E_OP_INTERRUPTED , EINTR},
85
+ {AFC_E_OP_IN_PROGRESS , EALREADY},
86
+ {AFC_E_INTERNAL_ERROR , EIO},
87
+ {-1}
88
+ };
89
+ for (const AFC_ERRMAP* p = map; p->afc>=0; ++p) {
90
+ if (err==(p->afc)) {
91
+ rb_raise_SystemCallError(p->sys);
92
+ }
93
+ }
94
+ rb_raise_SystemCallError(EIO);
95
+ }
96
+
97
+
98
+ static inline void rb_raise_SystemCallErrorAFC_IF(afc_error_t err)
99
+ {
100
+ if (err!=AFC_E_SUCCESS) rb_raise_SystemCallErrorAFC(err);
101
+ }
102
+
103
+
104
+ static VALUE rb_array_from_dictionary(char** dict)
105
+ {
106
+ VALUE result = rb_ary_new();
107
+ for (int i=0; dict[i]; ++i) {
108
+ rb_ary_unshift(result, rb_str_new2(dict[i]));
109
+ }
110
+ free_dictionary(dict);
111
+ return result;
112
+ }
113
+
114
+
115
+
116
+ #pragma mark - IoAFC
117
+
118
+
119
+ IoAFC::IoAFC()
120
+ {
121
+ _blocksize = 4096;
122
+ _idev = NULL;
123
+ _control = NULL;
124
+ _house_arrest = NULL;
125
+ _port = NULL;
126
+ _afc = NULL;
127
+ }
128
+
129
+
130
+ IoAFC::~IoAFC()
131
+ {
132
+ try{
133
+ teardown();
134
+ } catch(...){}
135
+ }
136
+
137
+
138
+ void IoAFC::teardown()
139
+ {
140
+ DBG("- IO::AFC: %p\n", _afc);
141
+ if (_afc) afc_client_free(_afc);
142
+ if (_port) lockdownd_service_descriptor_free(_port);
143
+ if (_house_arrest) house_arrest_client_free(_house_arrest);
144
+ if (_control) lockdownd_client_free(_control);
145
+ if (_idev) idevice_free(_idev);
146
+ _afc = NULL;
147
+ _port = NULL;
148
+ _house_arrest = NULL;
149
+ _control = NULL;
150
+ _idev = NULL;
151
+ }
152
+
153
+
154
+ void IoAFC::init(VALUE rb_device_uuid, VALUE rb_appid)
155
+ {
156
+ const char* device_uuid = NULL;
157
+ const char* appid = NULL;
158
+ if (!NIL_P(rb_device_uuid)) {
159
+ device_uuid = rb_string_value_cstr(&rb_device_uuid);
160
+ }
161
+ if (!NIL_P(rb_appid)) {
162
+ appid = rb_string_value_cstr(&rb_appid);
163
+ }
164
+
165
+ if (device_uuid) {
166
+ _device_uuid.assign(device_uuid);
167
+ } else {
168
+ _device_uuid.erase();
169
+ }
170
+ if (appid) {
171
+ _service_name = HOUSE_ARREST_SERVICE_NAME;
172
+ _appid.assign(appid);
173
+ } else {
174
+ _service_name = AFC_SERVICE_NAME;
175
+ _appid.erase();
176
+ }
177
+
178
+ idevice_new(&_idev, device_uuid);
179
+ if (!_idev) {
180
+ teardown();
181
+ rb_raise(rb_eNoDevice, "No device found.");
182
+ }
183
+
184
+ lockdownd_error_t le = lockdownd_client_new_with_handshake(_idev, &_control, "Ruby::IoAFC");
185
+ if (le!=LOCKDOWN_E_SUCCESS) {
186
+ teardown();
187
+ if (le==LOCKDOWN_E_PASSWORD_PROTECTED) {
188
+ rb_raise(rb_eProtected, "Password protected.");
189
+ }
190
+ rb_raise(rb_eConnectFailed, "Failed to connect to lockdownd servce.");
191
+ }
192
+
193
+ le = lockdownd_start_service(_control, _service_name, &_port);
194
+ if ((le!=LOCKDOWN_E_SUCCESS) || (_port==0)) {
195
+ teardown();
196
+ rb_raise(rb_eConnectFailed, "Failed to start %s service.", _service_name);
197
+ }
198
+
199
+ if (appid) {
200
+ // check document foider?
201
+ house_arrest_client_new(_idev, _port, &_house_arrest);
202
+ if (!_house_arrest) {
203
+ teardown();
204
+ rb_raise(rb_eConnectFailed, "Failed to start document sharing service.");
205
+ }
206
+
207
+ house_arrest_error_t he = house_arrest_send_command(_house_arrest, "VendContainer", appid);
208
+ if (he!=HOUSE_ARREST_E_SUCCESS) {
209
+ teardown();
210
+ rb_raise(rb_eConnectFailed, "Failed to start document sharing service.");
211
+ }
212
+
213
+ plist_t dict = NULL;
214
+ he = house_arrest_get_result(_house_arrest, &dict);
215
+ if (he!=HOUSE_ARREST_E_SUCCESS) {
216
+ teardown();
217
+ rb_raise(rb_eConnectFailed, "Failed to get result from document sharing service.");
218
+ }
219
+ plist_t node = plist_dict_get_item(dict, "Error");
220
+ if (node) {
221
+ char *str = NULL;
222
+ plist_get_string_val(node, &str);
223
+ if (str) {
224
+ _errmsg.assign(str);
225
+ } else {
226
+ _errmsg.assign("(null)");
227
+ }
228
+ ::free(str);
229
+ plist_free(dict);
230
+ teardown();
231
+ rb_raise(rb_eConnectFailed, "Error: %s", _errmsg.c_str());
232
+ }
233
+ plist_free(dict);
234
+ }
235
+
236
+ if (_house_arrest) {
237
+ afc_client_new_from_house_arrest_client(_house_arrest, &_afc);
238
+ } else {
239
+ afc_client_new(_idev, _port, &_afc);
240
+ }
241
+
242
+ if (!_afc) {
243
+ teardown();
244
+ rb_raise(rb_eConnectFailed, "Failed to connect AFC.");
245
+ }
246
+
247
+ char** info_raw = NULL;
248
+ afc_error_t ae = afc_get_device_info(_afc, &info_raw);
249
+ if ((ae!=AFC_E_SUCCESS) || (info_raw==NULL)) {
250
+ teardown();
251
+ rb_raise(rb_eConnectFailed, "Failed to get device info.");
252
+ }
253
+ for (int i=0; info_raw[i]; i+=2) {
254
+ if (strcmp(info_raw[i],"FSBlockSize")!=0) {
255
+ _blocksize = atoi(info_raw[i+1]);
256
+ break;
257
+ }
258
+ }
259
+ free_dictionary(info_raw);
260
+ DBG("+ IO::AFC: %p\n", _afc);
261
+ }
262
+
263
+
264
+ uint64_t IoAFC::open(afc_client_t* afc, VALUE rb_path, VALUE rb_mode)
265
+ {
266
+ checkHandle();
267
+
268
+ uint64_t handle = 0;
269
+ afc_file_mode_t mode = get_afc_file_mode(NUM2INT(rb_mode));
270
+ afc_error_t ae = afc_file_open(_afc, rb_string_value_cstr(&rb_path), mode, &handle);
271
+ rb_raise_SystemCallErrorAFC_IF(ae);
272
+ *afc = _afc;
273
+ return handle;
274
+ }
275
+
276
+
277
+ VALUE IoAFC::test_closed()
278
+ {
279
+ return (_afc==NULL)? Qtrue: Qfalse;
280
+ }
281
+
282
+
283
+ VALUE IoAFC::close()
284
+ {
285
+ teardown();
286
+ return Qnil;
287
+ }
288
+
289
+
290
+ VALUE IoAFC::getattr(VALUE rb_path)
291
+ {
292
+ checkHandle();
293
+
294
+ char** info = NULL;
295
+ afc_error_t ae = afc_get_file_info(_afc, rb_string_value_cstr(&rb_path), &info);
296
+ rb_raise_SystemCallErrorAFC_IF(ae);
297
+ if (info==NULL) {
298
+ rb_raise_SystemCallError(ENOENT);
299
+ }
300
+ return rb_array_from_dictionary(info);
301
+ }
302
+
303
+
304
+
305
+ VALUE IoAFC::readdir(VALUE rb_path)
306
+ {
307
+ checkHandle();
308
+
309
+ char** dirs = NULL;
310
+ afc_error_t ae = afc_read_directory(_afc, rb_string_value_cstr(&rb_path), &dirs);
311
+ rb_raise_SystemCallErrorAFC_IF(ae);
312
+ if (dirs==NULL) {
313
+ rb_raise_SystemCallError(ENOENT);
314
+ }
315
+ return rb_array_from_dictionary(dirs);
316
+ }
317
+
318
+
319
+
320
+ VALUE IoAFC::utimens(VALUE rb_path, VALUE rb_time)
321
+ {
322
+ checkHandle();
323
+
324
+ struct timespec ts = rb_time_timespec(rb_time);
325
+ uint64_t mt = (uint64_t)ts.tv_sec * (uint64_t)1000000000 + (uint64_t)ts.tv_nsec;
326
+
327
+ afc_error_t ae = afc_set_file_time(_afc, rb_string_value_cstr(&rb_path), mt);
328
+ rb_raise_SystemCallErrorAFC_IF(ae);
329
+ return Qtrue;
330
+ }
331
+
332
+
333
+
334
+ VALUE IoAFC::statfs()
335
+ {
336
+ checkHandle();
337
+
338
+ char** info= NULL;
339
+ afc_error_t ae = afc_get_device_info(_afc, &info);
340
+ rb_raise_SystemCallErrorAFC_IF(ae);
341
+ if (info==NULL) {
342
+ rb_raise_SystemCallError(ENOENT);
343
+ }
344
+ return rb_array_from_dictionary(info);
345
+ }
346
+
347
+
348
+
349
+ VALUE IoAFC::truncate(VALUE rb_path, VALUE rb_size)
350
+ {
351
+ checkHandle();
352
+
353
+ afc_error_t ae = afc_truncate(_afc, rb_string_value_cstr(&rb_path), NUM2ULL(rb_size));
354
+ rb_raise_SystemCallErrorAFC_IF(ae);
355
+ return rb_size;
356
+ }
357
+
358
+
359
+
360
+ // afc.symlink("hello", "/tmp/world")
361
+ VALUE IoAFC::symlink(VALUE rb_target, VALUE rb_linkname)
362
+ {
363
+ checkHandle();
364
+
365
+ afc_error_t ae = afc_make_link(_afc, AFC_SYMLINK, rb_string_value_cstr(&rb_target), rb_string_value_cstr(&rb_linkname));
366
+ rb_raise_SystemCallErrorAFC_IF(ae);
367
+ return Qtrue;
368
+ }
369
+
370
+
371
+
372
+ // afc.link("/tmp/hello", "/tmp/world") #=> ln /tmp/hello /tmp/world
373
+ VALUE IoAFC::link(VALUE rb_target, VALUE rb_linkname)
374
+ {
375
+ checkHandle();
376
+
377
+ afc_error_t ae = afc_make_link(_afc, AFC_HARDLINK, rb_string_value_cstr(&rb_target), rb_string_value_cstr(&rb_linkname));
378
+ rb_raise_SystemCallErrorAFC_IF(ae);
379
+ return Qtrue;
380
+ }
381
+
382
+
383
+
384
+ VALUE IoAFC::unlink(VALUE rb_path)
385
+ {
386
+ checkHandle();
387
+
388
+ afc_error_t ae = afc_remove_path(_afc, rb_string_value_cstr(&rb_path));
389
+ rb_raise_SystemCallErrorAFC_IF(ae);
390
+ return Qtrue;
391
+ }
392
+
393
+
394
+
395
+ VALUE IoAFC::rename(VALUE rb_from, VALUE rb_to)
396
+ {
397
+ checkHandle();
398
+
399
+ afc_error_t ae = afc_rename_path(_afc, rb_string_value_cstr(&rb_from), rb_string_value_cstr(&rb_to));
400
+ rb_raise_SystemCallErrorAFC_IF(ae);
401
+ return Qtrue;
402
+ }
403
+
404
+
405
+
406
+ VALUE IoAFC::mkdir(VALUE rb_path)
407
+ {
408
+ checkHandle();
409
+
410
+ afc_error_t ae = afc_make_directory(_afc, rb_string_value_cstr(&rb_path));
411
+ rb_raise_SystemCallErrorAFC_IF(ae);
412
+ return Qtrue;
413
+ }
414
+
415
+
416
+
417
+ VALUE IoAFC::get_device_udid()
418
+ {
419
+ checkHandle();
420
+
421
+ char* udid = NULL;
422
+ lockdownd_error_t le = lockdownd_get_device_udid(_control, &udid);
423
+ if (le!=LOCKDOWN_E_SUCCESS) {
424
+ rb_raise(rb_eConnectFailed, "lockdownd_get_device_udid() failed - %d", le);
425
+ }
426
+ VALUE result = Qnil;
427
+ if (udid) {
428
+ result = rb_str_new2(udid);
429
+ ::free(udid);
430
+ }
431
+ return result;
432
+ }
433
+
434
+
435
+
436
+ VALUE IoAFC::get_evice_display_name()
437
+ {
438
+ checkHandle();
439
+
440
+ char* name = NULL;
441
+ lockdownd_error_t le = lockdownd_get_device_name(_control, &name);
442
+ if (le!=LOCKDOWN_E_SUCCESS) {
443
+ rb_raise(rb_eConnectFailed, "lockdownd_get_device_name() failed - %d", le);
444
+ }
445
+ VALUE result = Qnil;
446
+ if (name) {
447
+ result = rb_str_new2(name);
448
+ ::free(name);
449
+ }
450
+ return result;
451
+ }
452
+
453
+
454
+
455
+ static VALUE plist_string_to_rb_string(plist_t node)
456
+ {
457
+ char* s = NULL;
458
+ plist_get_string_val(node, &s);
459
+ if (s==NULL) {
460
+ return Qnil;
461
+ }
462
+ VALUE v = rb_str_new2(s);
463
+ ::free(s);
464
+ return v;
465
+ }
466
+
467
+ VALUE IoAFC::enum_applications()
468
+ {
469
+ checkHandle();
470
+
471
+ lockdownd_service_descriptor_t port = NULL;
472
+ lockdownd_error_t le = lockdownd_start_service(_control, INSTALLATION_PROXY_SERVICE_NAME, &port);
473
+ if ((le!=LOCKDOWN_E_SUCCESS) || (port==0)) {
474
+ rb_raise(rb_eConnectFailed, "Failed to start %s service.", INSTALLATION_PROXY_SERVICE_NAME);
475
+ }
476
+
477
+ instproxy_client_t client = NULL;
478
+ instproxy_error_t ie = instproxy_client_new(_idev, port, &client);
479
+ if ((ie!=INSTPROXY_E_SUCCESS) || (client==NULL)) {
480
+ lockdownd_service_descriptor_free(port);
481
+ rb_raise(rb_eRuntimeError, "instproxy_client_new() failed - %d", ie);
482
+ }
483
+
484
+ plist_t client_opts = instproxy_client_options_new();
485
+ if (client_opts==NULL) {
486
+ instproxy_client_free(client);
487
+ lockdownd_service_descriptor_free(port);
488
+ rb_raise(rb_eRuntimeError, "instproxy_client_options_new() failed");
489
+ }
490
+
491
+ plist_t apps = NULL;
492
+ instproxy_client_options_add(client_opts, "ApplicationType", "User", NULL);
493
+ ie = instproxy_browse(client, client_opts, &apps);
494
+ instproxy_client_options_free(client_opts);
495
+ instproxy_client_free(client);
496
+ lockdownd_service_descriptor_free(port);
497
+ if (ie!=INSTPROXY_E_SUCCESS) {
498
+ rb_raise(rb_eRuntimeError, "instproxy_browse() failed - %d", ie);
499
+ }
500
+
501
+ VALUE result = rb_hash_new();
502
+ if (apps!=NULL) {
503
+ uint32_t num = plist_array_get_size(apps);
504
+ VALUE rb_displayName = rb_str_new2("CFBundleDisplayName");
505
+ VALUE rb_sharing = rb_str_new2("UIFileSharingEnabled");
506
+
507
+ for (uint32_t i=0; i<num; ++i) {
508
+ plist_t app = plist_array_get_item(apps, i);
509
+ plist_t id = plist_dict_get_item(app, "CFBundleIdentifier");
510
+ plist_t name = plist_dict_get_item(app, "CFBundleDisplayName");
511
+ // plist_t type = plist_dict_get_item(app, "CFBundleDocumentTypes");
512
+ // if (plist_array_get_size(type)==0) {
513
+ // type = NULL;
514
+ // }
515
+ plist_t sharing = plist_dict_get_item(app, "UIFileSharingEnabled");
516
+ uint8_t b_sharing = false;
517
+ if (sharing) {
518
+ plist_type t = plist_get_node_type(sharing);
519
+ if (t==PLIST_BOOLEAN) {
520
+ plist_get_bool_val(sharing, &b_sharing);
521
+ } else if (t==PLIST_STRING) {
522
+ char* v = NULL;
523
+ plist_get_string_val(sharing, &v);
524
+ if ((strcmp(v, "YES")==0) || (strcmp(v, "true")==0)) {
525
+ b_sharing = true;
526
+ }
527
+ ::free(v);
528
+ }
529
+ }
530
+
531
+ VALUE h = rb_hash_new();
532
+ rb_hash_aset(h, rb_displayName, plist_string_to_rb_string(name));
533
+ rb_hash_aset(h, rb_sharing, (b_sharing)? Qtrue: Qfalse);
534
+ rb_hash_aset(result, plist_string_to_rb_string(id), h);
535
+ }
536
+ plist_free(apps);
537
+ }
538
+
539
+ return result;
540
+ }
541
+
542
+
543
+
544
+
545
+ #pragma mark - IoAFCDescriptor
546
+
547
+ IoAFCDescriptor::IoAFCDescriptor()
548
+ {
549
+ _afc = NULL;
550
+ _handle = 0;
551
+ }
552
+
553
+
554
+
555
+ IoAFCDescriptor::~IoAFCDescriptor()
556
+ {
557
+ try{
558
+ teardown();
559
+ } catch(...){}
560
+ }
561
+
562
+
563
+
564
+ void IoAFCDescriptor::teardown()
565
+ {
566
+ if (_afc) {
567
+ DBG("- IO::AFC::FD: %p, %llx\n", _afc, _handle);
568
+ afc_file_close(_afc, _handle);
569
+ _afc = NULL;
570
+ _handle = 0;
571
+ }
572
+ }
573
+
574
+
575
+
576
+ void IoAFCDescriptor::init(VALUE rb_afc, VALUE rb_path, VALUE rb_mode)
577
+ {
578
+ if (NIL_P(rb_afc)) {
579
+ rb_raise(rb_eRuntimeError, "Invalid AFC object.");
580
+ }
581
+ _handle = IoAFC::rb_open(rb_afc, &_afc, rb_path, rb_mode);
582
+ DBG("+ IO::AFC::FD: %p, %llx\n", _afc, _handle);
583
+ }
584
+
585
+
586
+
587
+ VALUE IoAFCDescriptor::test_closed()
588
+ {
589
+ return (_handle==0)? Qtrue: Qfalse;
590
+ }
591
+
592
+
593
+
594
+ VALUE IoAFCDescriptor::close()
595
+ {
596
+ teardown();
597
+ return Qnil;
598
+ }
599
+
600
+
601
+
602
+ #define MAX_READ_DATA_LENGTH (8192)
603
+ VALUE IoAFCDescriptor::read(VALUE rb_size)
604
+ {
605
+ checkHandle();
606
+
607
+ uint32_t size = (uint32_t)NUM2ULONG(rb_size);
608
+ if (size==0) {
609
+ return Qnil;
610
+ }
611
+ if (size>MAX_READ_DATA_LENGTH) {
612
+ size = MAX_READ_DATA_LENGTH;
613
+ }
614
+
615
+ char data[MAX_READ_DATA_LENGTH];
616
+ afc_error_t ae = afc_file_read(_afc, _handle, data, size, &size);
617
+ rb_raise_SystemCallErrorAFC_IF(ae);
618
+ return rb_str_new(data, size);
619
+ }
620
+
621
+
622
+
623
+ #define MAX_WRITE_DATA_LENGTH (65536*16)
624
+ VALUE IoAFCDescriptor::write(VALUE rb_data)
625
+ {
626
+ checkHandle();
627
+
628
+ if (NIL_P(rb_data)) {
629
+ return INT2FIX(0);
630
+ }
631
+ const char* data = rb_string_value_ptr(&rb_data);
632
+ off_t size = NUM2ULL(rb_str_length(rb_data));
633
+ if (size==0) {
634
+ return INT2FIX(0);
635
+ }
636
+
637
+ off_t wrote = 0;
638
+ while (size>0) {
639
+ uint32_t length = (size>MAX_WRITE_DATA_LENGTH)? MAX_WRITE_DATA_LENGTH: (uint32_t)size;
640
+ afc_error_t ae = afc_file_write(_afc, _handle, data, length, &length);
641
+ rb_raise_SystemCallErrorAFC_IF(ae);
642
+ if (length==0) {
643
+ rb_raise(rb_eRuntimeError, "Unexpected error: No data is written.");
644
+ }
645
+ data += length;
646
+ size -= length;
647
+ wrote += length;
648
+ }
649
+
650
+ return ULL2NUM(wrote);
651
+ }
652
+
653
+
654
+
655
+ VALUE IoAFCDescriptor::seek(VALUE rb_offset, VALUE rb_mode)
656
+ {
657
+ checkHandle();
658
+
659
+ afc_error_t ae = afc_file_seek(_afc, _handle, NUM2ULL(rb_offset), NUM2INT(rb_mode));
660
+ rb_raise_SystemCallErrorAFC_IF(ae);
661
+ return tell();
662
+ }
663
+
664
+
665
+
666
+ VALUE IoAFCDescriptor::tell()
667
+ {
668
+ checkHandle();
669
+
670
+ uint64_t pos = 0;
671
+ afc_error_t ae = afc_file_tell(_afc, _handle, &pos);
672
+ rb_raise_SystemCallErrorAFC_IF(ae);
673
+ return ULL2NUM(pos);
674
+ }
675
+
676
+
677
+
678
+ VALUE IoAFCDescriptor::ftruncate(VALUE rb_size)
679
+ {
680
+ checkHandle();
681
+
682
+ afc_error_t ae = afc_file_truncate(_afc, _handle, NUM2ULL(rb_size));
683
+ rb_raise_SystemCallErrorAFC_IF(ae);
684
+ return rb_size;
685
+ }
686
+
687
+
688
+
689
+
690
+ #pragma mark - Ruby method definition.
691
+
692
+ void IoAFC::define_class()
693
+ {
694
+ rb_define_alloc_func(rb_cIoAFC, (rb_alloc_func_t)rb_alloc);
695
+ rb_define_method(rb_cIoAFC, "initialize", RUBY_METHOD_FUNC(rb_init), 2);
696
+ rb_define_method(rb_cIoAFC, "closed?", RUBY_METHOD_FUNC(rb_test_closed), 0);
697
+ rb_define_method(rb_cIoAFC, "close", RUBY_METHOD_FUNC(rb_close), 0);
698
+ rb_define_method(rb_cIoAFC, "getattr", RUBY_METHOD_FUNC(rb_getattr), 1);
699
+ rb_define_method(rb_cIoAFC, "readdir", RUBY_METHOD_FUNC(rb_readdir), 1);
700
+ rb_define_method(rb_cIoAFC, "utimens", RUBY_METHOD_FUNC(rb_utimens), 2);
701
+ rb_define_method(rb_cIoAFC, "statfs", RUBY_METHOD_FUNC(rb_statfs), 0);
702
+ rb_define_method(rb_cIoAFC, "truncate", RUBY_METHOD_FUNC(rb_truncate), 2);
703
+ rb_define_method(rb_cIoAFC, "symlink", RUBY_METHOD_FUNC(rb_symlink), 2);
704
+ rb_define_method(rb_cIoAFC, "link", RUBY_METHOD_FUNC(rb_link), 2);
705
+ rb_define_method(rb_cIoAFC, "unlink", RUBY_METHOD_FUNC(rb_unlink), 1);
706
+ rb_define_method(rb_cIoAFC, "rename", RUBY_METHOD_FUNC(rb_rename), 2);
707
+ rb_define_method(rb_cIoAFC, "mkdir", RUBY_METHOD_FUNC(rb_mkdir), 1);
708
+
709
+ rb_define_method(rb_cIoAFC, "device_udid", RUBY_METHOD_FUNC(rb_get_device_udid), 0);
710
+ rb_define_method(rb_cIoAFC, "device_display_name", RUBY_METHOD_FUNC(rb_get_device_display_name), 0);
711
+ rb_define_method(rb_cIoAFC, "applications", RUBY_METHOD_FUNC(rb_enum_applications), 0);
712
+ }
713
+
714
+
715
+
716
+ void IoAFCDescriptor::define_class()
717
+ {
718
+ rb_define_alloc_func(rb_cIoAFCFD, (rb_alloc_func_t)rb_alloc);
719
+ rb_define_method(rb_cIoAFCFD, "initialize", RUBY_METHOD_FUNC(rb_init), 3);
720
+ rb_define_method(rb_cIoAFCFD, "closed?", RUBY_METHOD_FUNC(rb_test_closed), 0);
721
+ rb_define_method(rb_cIoAFCFD, "close", RUBY_METHOD_FUNC(rb_close), 0);
722
+ rb_define_method(rb_cIoAFCFD, "read", RUBY_METHOD_FUNC(rb_read), 1);
723
+ rb_define_method(rb_cIoAFCFD, "write", RUBY_METHOD_FUNC(rb_write), 1);
724
+ rb_define_method(rb_cIoAFCFD, "seek", RUBY_METHOD_FUNC(rb_seek), 2);
725
+ rb_define_method(rb_cIoAFCFD, "tell", RUBY_METHOD_FUNC(rb_tell), 0);
726
+ rb_define_method(rb_cIoAFCFD, "ftruncate", RUBY_METHOD_FUNC(rb_ftruncate), 1);
727
+ }
728
+
729
+
730
+
731
+
732
+ extern "C" {
733
+ void Init_afc_base();
734
+ }
735
+ void Init_afc_base()
736
+ {
737
+ IoAFC::define_class();
738
+ IoAFCDescriptor::define_class();
739
+ }