Skip to content

Commit ccaf748

Browse files
authored
Merge pull request #213 from cfergeau/fddup
dup() golang FDs in obj-C code
2 parents 15b346c + 84a9a94 commit ccaf748

8 files changed

Lines changed: 141 additions & 10 deletions

network.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,14 +192,20 @@ func NewFileHandleNetworkDeviceAttachment(file *os.File) (*FileHandleNetworkDevi
192192
return nil, err
193193
}
194194

195+
nserrPtr := newNSErrorAsNil()
196+
195197
attachment := &FileHandleNetworkDeviceAttachment{
196198
pointer: objc.NewPointer(
197199
C.newVZFileHandleNetworkDeviceAttachment(
198200
C.int(file.Fd()),
201+
&nserrPtr,
199202
),
200203
),
201204
mtu: 1500, // The default MTU is 1500.
202205
}
206+
if err := newNSError(nserrPtr); err != nil {
207+
return nil, err
208+
}
203209
objc.SetFinalizer(attachment, func(self *FileHandleNetworkDeviceAttachment) {
204210
objc.Release(self)
205211
})

serial_console.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,19 @@ func NewFileHandleSerialPortAttachment(read, write *os.File) (*FileHandleSerialP
4949
return nil, err
5050
}
5151

52+
nserrPtr := newNSErrorAsNil()
5253
attachment := &FileHandleSerialPortAttachment{
5354
pointer: objc.NewPointer(
5455
C.newVZFileHandleSerialPortAttachment(
5556
C.int(read.Fd()),
5657
C.int(write.Fd()),
58+
&nserrPtr,
5759
),
5860
),
5961
}
62+
if err := newNSError(nserrPtr); err != nil {
63+
return nil, err
64+
}
6065
objc.SetFinalizer(attachment, func(self *FileHandleSerialPortAttachment) {
6166
objc.Release(self)
6267
})

storage_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package vz_test
22

33
import (
44
"log"
5+
"os"
6+
"os/exec"
57
"path/filepath"
68
"strings"
79
"testing"
@@ -103,3 +105,92 @@ func TestBlockDeviceWithCacheAndSyncMode(t *testing.T) {
103105
t.Fatalf("want state %v but got %v", vz.VirtualMachineStateRunning, got)
104106
}
105107
}
108+
109+
func TestBlockDeviceStorageDeviceAttachmentError(t *testing.T) {
110+
if vz.Available(14) {
111+
t.Skip("vz.NewDiskBlockDeviceStorageDeviceAttachment is supported from macOS 14")
112+
}
113+
114+
f, err := os.Create(filepath.Join(t.TempDir(), "empty"))
115+
if err != nil {
116+
t.Fatal(err)
117+
}
118+
f.Close()
119+
_, err = vz.NewDiskBlockDeviceStorageDeviceAttachment(f, false, vz.DiskSynchronizationModeNone)
120+
if err == nil {
121+
t.Fatal("did not get an error with invalid file descriptor")
122+
}
123+
}
124+
125+
func TestBlockDeviceWithDeviceAttachment(t *testing.T) {
126+
if vz.Available(12) {
127+
t.Skip("vz.NewDiskImageStorageDeviceAttachmentWithCacheAndSync is supported from macOS 12")
128+
}
129+
130+
devPath := ""
131+
container := newVirtualizationMachine(t,
132+
func(vmc *vz.VirtualMachineConfiguration) error {
133+
dir := t.TempDir()
134+
path := filepath.Join(dir, "disk.img")
135+
if err := vz.CreateDiskImage(path, 512); err != nil {
136+
t.Fatal(err)
137+
}
138+
cmd := exec.Command("hdiutil", "attach", "-imagekey", "diskimage-class=CRawDiskImage", "-nomount", path)
139+
output, err := cmd.Output()
140+
if err != nil {
141+
t.Fatalf("failed to attach disk image: %v", err)
142+
}
143+
144+
outputStr := string(output)
145+
lines := strings.Split(outputStr, "\n")
146+
if len(lines) == 0 || !strings.HasPrefix(lines[0], "/dev/") {
147+
log.Printf("[%s]\n", lines)
148+
t.Fatalf("unexpected output from `hdiutil attach`")
149+
}
150+
if len(lines) != 0 && strings.HasPrefix(lines[0], "/dev/") {
151+
devPath = strings.TrimSpace(lines[0])
152+
}
153+
154+
var attachment *vz.DiskBlockDeviceStorageDeviceAttachment
155+
{
156+
dev, err := os.Open(devPath)
157+
if err != nil {
158+
t.Fatal(err)
159+
}
160+
161+
attachment, err = vz.NewDiskBlockDeviceStorageDeviceAttachment(dev, false, vz.DiskSynchronizationModeNone)
162+
if err != nil {
163+
t.Fatal(err)
164+
}
165+
if err := dev.Close(); err != nil {
166+
log.Printf("failed to close %s: %s\n", devPath, err)
167+
}
168+
169+
}
170+
171+
config, err := vz.NewVirtioBlockDeviceConfiguration(attachment)
172+
if err != nil {
173+
t.Fatal(err)
174+
}
175+
vmc.SetStorageDevicesVirtualMachineConfiguration([]vz.StorageDeviceConfiguration{
176+
config,
177+
})
178+
return nil
179+
},
180+
)
181+
t.Cleanup(func() {
182+
if err := container.Shutdown(); err != nil {
183+
log.Println(err)
184+
}
185+
cmd := exec.Command("hdiutil", "detach", devPath)
186+
if err := cmd.Run(); err != nil {
187+
log.Printf("hdiutil detach %s failed: %s\n", devPath, err)
188+
}
189+
})
190+
191+
vm := container.VirtualMachine
192+
193+
if got := vm.State(); vz.VirtualMachineStateRunning != got {
194+
t.Fatalf("want state %v but got %v", vz.VirtualMachineStateRunning, got)
195+
}
196+
}

virtualization_11.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,15 @@ void setStorageDevicesVZVirtualMachineConfiguration(void *config,
8686
void *storageDevicesVZVirtualMachineConfiguration(void *config);
8787

8888
/* Configurations */
89-
void *newVZFileHandleSerialPortAttachment(int readFileDescriptor, int writeFileDescriptor);
89+
void *newVZFileHandleSerialPortAttachment(int readFileDescriptor, int writeFileDescriptor, void **error);
9090
void *newVZFileSerialPortAttachment(const char *filePath, bool shouldAppend, void **error);
9191
void *newVZVirtioConsoleDeviceSerialPortConfiguration(void *attachment);
9292
void *VZBridgedNetworkInterface_networkInterfaces(void);
9393
const char *VZBridgedNetworkInterface_identifier(void *networkInterface);
9494
const char *VZBridgedNetworkInterface_localizedDisplayName(void *networkInterface);
9595
void *newVZBridgedNetworkDeviceAttachment(void *networkInterface);
9696
void *newVZNATNetworkDeviceAttachment(void);
97-
void *newVZFileHandleNetworkDeviceAttachment(int fileDescriptor);
97+
void *newVZFileHandleNetworkDeviceAttachment(int fileDescriptor, void **error);
9898
void *newVZVirtioNetworkDeviceConfiguration(void *attachment);
9999
void setNetworkDevicesVZMACAddress(void *config, void *macAddress);
100100
void *newVZVirtioEntropyDeviceConfiguration(void);

virtualization_11.m

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -448,13 +448,20 @@ void setStorageDevicesVZVirtualMachineConfiguration(void *config,
448448
@discussion
449449
Each file descriptor must a valid.
450450
*/
451-
void *newVZFileHandleSerialPortAttachment(int readFileDescriptor, int writeFileDescriptor)
451+
void *newVZFileHandleSerialPortAttachment(int readFileDescriptor, int writeFileDescriptor, void **error)
452452
{
453453
if (@available(macOS 11, *)) {
454454
VZFileHandleSerialPortAttachment *ret;
455455
@autoreleasepool {
456-
NSFileHandle *fileHandleForReading = [[NSFileHandle alloc] initWithFileDescriptor:readFileDescriptor];
457-
NSFileHandle *fileHandleForWriting = [[NSFileHandle alloc] initWithFileDescriptor:writeFileDescriptor];
456+
NSFileHandle *fileHandleForReading = newFileHandleDupFd(readFileDescriptor, error);
457+
if (error != nil) {
458+
return nil;
459+
}
460+
461+
NSFileHandle *fileHandleForWriting = newFileHandleDupFd(writeFileDescriptor, error);
462+
if (error != nil) {
463+
return nil;
464+
}
458465
ret = [[VZFileHandleSerialPortAttachment alloc]
459466
initWithFileHandleForReading:fileHandleForReading
460467
fileHandleForWriting:fileHandleForWriting];
@@ -602,12 +609,15 @@ void setStorageDevicesVZVirtualMachineConfiguration(void *config,
602609
@see VZNetworkDeviceConfiguration
603610
@see VZVirtioNetworkDeviceConfiguration
604611
*/
605-
void *newVZFileHandleNetworkDeviceAttachment(int fileDescriptor)
612+
void *newVZFileHandleNetworkDeviceAttachment(int fileDescriptor, void **error)
606613
{
607614
if (@available(macOS 11, *)) {
608615
VZFileHandleNetworkDeviceAttachment *ret;
609616
@autoreleasepool {
610-
NSFileHandle *fileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fileDescriptor];
617+
NSFileHandle *fileHandle = newFileHandleDupFd(fileDescriptor, error);
618+
if (fileHandle == nil) {
619+
return nil;
620+
}
611621
ret = [[VZFileHandleNetworkDeviceAttachment alloc] initWithFileHandle:fileHandle];
612622
}
613623
return ret;

virtualization_14.m

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
@param error If not nil, assigned with the error if the initialization failed.
3131
@return An initialized `VZDiskBlockDeviceStorageDeviceAttachment` or nil if there was an error.
3232
@discussion
33-
The file handle is retained by the disk attachment.
33+
The file handle is dup()’ed by this function.
3434
The handle must be open when the virtual machine starts.
3535
3636
The `readOnly` parameter affects how the disk is exposed to the guest operating system
@@ -41,7 +41,10 @@
4141
{
4242
#ifdef INCLUDE_TARGET_OSX_14
4343
if (@available(macOS 14, *)) {
44-
NSFileHandle *fileHandle = [[NSFileHandle alloc] initWithFileDescriptor:fileDescriptor];
44+
NSFileHandle *fileHandle = newFileHandleDupFd(fileDescriptor, error);
45+
if (fileHandle == nil) {
46+
return nil;
47+
}
4548
return [[VZDiskBlockDeviceStorageDeviceAttachment alloc]
4649
initWithFileHandle:fileHandle
4750
readOnly:(BOOL)readOnly

virtualization_helper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#import <Foundation/Foundation.h>
55

66
NSDictionary *dumpProcessinfo();
7+
NSFileHandle *newFileHandleDupFd(int fileDescriptor, void **error);
78

89
#define RAISE_REASON_MESSAGE \
910
"This may possibly be a bug due to library handling errors.\n" \

virtualization_helper.m

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,19 @@
2727
@"Min Required OS Version" : @__MAC_OS_X_VERSION_MIN_REQUIRED,
2828
#endif
2929
};
30-
}
30+
}
31+
32+
NSFileHandle *newFileHandleDupFd(int fileDescriptor, void **error)
33+
{
34+
int dupedFd = dup(fileDescriptor);
35+
if (dupedFd < 0) {
36+
if (error != nil) {
37+
*error = [NSError errorWithDomain:NSPOSIXErrorDomain
38+
code:errno
39+
userInfo:nil];
40+
}
41+
return nil;
42+
}
43+
44+
return [[NSFileHandle alloc] initWithFileDescriptor:dupedFd closeOnDealloc:true];
45+
}

0 commit comments

Comments
 (0)