Formatting a disk in macOS programmatically using DiskManagement.framework - objective-c

I have a task to implement disk formatting functionality in my code.
I am against the use of command line wrappers (e.g. diskutil), as they are slow and unreliable.
I'm importing this private framework: /System/Library/PrivateFrameworks/DiskManagement.framework
And the following headers: DMManager.h, DMEraseDisk.h, DMFilesystem.h (GitHub Repo)
I have almost everything ready, but there is one problem that I can not overcome:
Calling the eraseDisk method in DMEraseDisk freezes the application.
At the same time, the disk is formatted successfully, I just need to mount it manually.
#import <Foundation/Foundation.h>
#import <DiskArbitration/DiskArbitration.h>
#import "DiskManagement/DMManager.h"
#import "DiskManagement/DMEraseDisk.h"
#import "DiskManagement/DMFilesystem.h"
int main(int argc, const char * argv[]) {
#autoreleasepool {
/* From the public DiskArbitration.h */
DASessionRef diskSession = DASessionCreate(nil);
DADiskRef currentDisk = DADiskCreateFromBSDName(NULL, diskSession, "disk9s1");
/* From DiskManagement.framework private headers (DMManager.h, DMEraseDisk.h, DMFilesystem.h) */
DMManager *dmManager = [DMManager sharedManager];
DMEraseDisk *diskEraser = [[DMEraseDisk alloc] initWithManager:dmManager];
/* Getting available file systems for a given device */
NSArray *availableFilesystems = [DMEraseDisk eraseTypesForDisk:currentDisk];
printf("Available File Systems for this device:\n");
for (DMFilesystem *availableFilesystem in availableFilesystems) {
printf("[Type:] %s\n", [[availableFilesystem filesystemType] UTF8String]);
printf("[Personality:] %s\n", [[availableFilesystem filesystemPersonality] UTF8String]);
printf("---\n");
}
/* (Type: msdos, Personality: MS-DOS FAT32) */
DMFilesystem *selectedFilesystem = [availableFilesystems objectAtIndex:2];
/*
(Formatting this device to MS-DOS FAT32)
Formats successfully, but stops here
and the code after this function is not executed further
*/
[diskEraser
eraseDisk: currentDisk
synchronous: YES // Won't work if set to NO (even with CFRunLoopRun())
filesystem: selectedFilesystem
bootable: YES
name: #"RESOPHIE"
doNewfs: YES
doBooterCleanup: NO
];
printf("I will never show up :(\n");
}
return 0;
}
How can I make the code continue to execute after calling eraseDisk method?

Related

JNI header missing in Objective-C

I have a file.c in my project which has #include <jni.h> header file. What is the process to include this header file in project or macOS?
Let's say you have following code
#include "jni.h"
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
#autoreleasepool {
JNIEnv *env;
JavaVM *jvm;
JavaVMInitArgs vm_args;
JavaVMOption options[3];
options[0].optionString = "-Djava.class.path=_HERE_GOES_LOCATION_OF_JNICOOKBOK_/jnicookbook/recipeNo051/target";
vm_args.options = options;
vm_args.ignoreUnrecognized = 0;
vm_args.version = JNI_VERSION_1_8;
vm_args.nOptions = 1;
int status = JNI_CreateJavaVM (&jvm, (void **) &env, &vm_args);
if (status < 0 || !env) {
printf ("Error - JVM creation failed\n");
return 1;
}
jclass cls_Main = (*env)->FindClass (env, "recipeNo051/Main");
jmethodID method_displayMessage = (*env)->GetStaticMethodID (env, cls_Main, "displayMessage", "()V");
(*env)->CallStaticVoidMethod(env, cls_Main, method_displayMessage);
(*jvm)->DestroyJavaVM( jvm );
}
return 0;
}
in order to run it you will need
location of libjvm.dylib
location of headers
location of compiled Java classes that are called from main.m
Let's start with libs and headers. You have to make sure that following paths are searched for includes (note that I am using jdk-11.0.4):
/Library/Java/JavaVirtualMachines/jdk-11.0.4.jdk/Contents/Home/include
/Library/Java/JavaVirtualMachines/jdk-11.0.4.jdk/Contents/Home/include/darwin/
You have to make sure that following path is added to Library Search Path and to Runpath Search Paths
/Library/Java/JavaVirtualMachines/jdk-11.0.4.jdk/Contents/Home/lib/server
You should have settings like that:
Make sure you are linking your code with libjvm.dylib. Add it inside Build Phases
where you can specify it's location by choosing Add Other...
Run your code, but! Make sure to ignore SIGSEGV before calling method JNI_CreateJavaVM. You can ignore it inside lldb console
(lldb) process handle --pass true --stop false SIGSEGV
After you continue, you can see your JVM instance calling classes from the recipeNo051.
Source code of class: recipeNo051/Main can be found here: https://github.com/mkowsiak/jnicookbook/tree/master/recipes/recipeNo051
Update
step by step instructions: http://www.owsiak.org/running-jni-based-code-inside-xcode/
video tutorial: https://youtu.be/WEA-3uI7Y18

How to get diskutil info output in a cocoa application

Is there a way to programmatically get the same information that diskutil info / | grep "Free Space" gives you? (For obvious reasons I'd rather have a better way to do this than just parsing the result of that command.)
Currently I'm using statfs; however, it was brought to my attention that the space this reports is not always accurate, because OS X also places temporary files such as Time Machine snapshots on the drive. These files automatically get deleted if space is running out, and the OS does not report the usage of these files. In other words, statfs often gives a lower number of free space than diskutil info or looking at the disk information in Finder.
You can use popen(3):
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *f;
char info[256];
f = popen("/usr/sbin/diskutil info /", "r");
if (f == NULL) {
perror("Failed to run diskutil");
exit(0);
}
while (fgets(info, sizeof(info), f) != NULL) {
printf("%s", info);
}
pclose(f);
return 0;
}
EDIT
Sorry, I didn't read the question carefully. You can also use the Disk Arbitration Framework. There's also some sample code that might be helpful (FSMegaInfo).
UPDATE
I took a look at the output from otool -L $(which diskutil) and it seems that it's using a private framework called DiskManagement.framework. After looking at the output from class-dump I saw there's a volumeFreeSpaceForDisk:error: method. So the sizes I got from diskutil -info / and FSMegaInfo FSGetVolumeInfo / and my tool were:
diskutil: 427031642112 Bytes
my tool: volumeFreeSpaceForDisk: 427031642112
FSMegaInfo: freeBytes = 427031642112 (397 GB)
I also observed that the sizes differ (with a few KB) every time I ran one of the tools and also that diskutil is dividing by 1000 and FSMegaInfo is dividing by 1024, so the size in GB will be always different (same reason as with df -h and df -H and diskutil - base 10 and base 2).
Here's my sample tool:
#import <Foundation/Foundation.h>
#import "DiskManagement.h"
#import <DiskArbitration/DADisk.h>
int main(int argc, char *argv[])
{
int err;
const char * bsdName = "disk0s2";
DASessionRef session;
DADiskRef disk;
CFDictionaryRef descDict;
session = NULL;
disk = NULL;
descDict = NULL;
if (err == 0) {session = DASessionCreate(NULL); if (session == NULL) {err = EINVAL;}}
if (err == 0) {disk = DADiskCreateFromBSDName(NULL, session, bsdName); if (disk == NULL) {err = EINVAL;}}
if (err == 0) {descDict = DADiskCopyDescription(disk); if (descDict == NULL) {err = EINVAL;}}
DMManager *dmMan = [DMManager sharedManager];
NSLog(#"blockSizeForDisk: %#", [dmMan blockSizeForDisk:disk error:nil]);
NSLog(#"totalSizeForDisk: %#", [dmMan totalSizeForDisk:disk error:nil]);
NSLog(#"volumeTotalSizeForDisk: %#", [dmMan volumeTotalSizeForDisk:disk error:nil]);
NSLog(#"volumeFreeSpaceForDisk: %#", [dmMan volumeFreeSpaceForDisk:disk error:nil]);
return 0;
}
You can obtain the DiskManagement.h by running class-dump /System/Library/PrivateFrameworks/DiskManagement.framework/Versions/Current/DiskManagement > DiskManagement.h and you can link to that framework by including the private frameworks path using -F/System/Library/PrivateFrameworks/ and add -framework.
Compile:
clang -g tool.m -F/System/Library/PrivateFrameworks/ -framework Foundation -framework DiskArbitration -framework DiskManagement -o tool
UPDATE2:
You can also take a look here and here. If the FSMegaInfo sample is not working for you, then you can just stat the /Volumes/.MobileBackups and subtract it's size from what you get from statfs("/", &stats).

iOS - how to call a C function?

I would like to use this function to help monitor memory:
void print_free_memory ()
{
mach_port_t host_port;
mach_msg_type_number_t host_size;
vm_size_t pagesize;
host_port = mach_host_self();
host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
host_page_size(host_port, &pagesize);
vm_statistics_data_t vm_stat;
if (host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size) != KERN_SUCCESS)
NSLog(#"Failed to fetch vm statistics");
/* Stats in bytes */
natural_t mem_used = (vm_stat.active_count +
vm_stat.inactive_count +
vm_stat.wire_count) * pagesize;
natural_t mem_free = vm_stat.free_count * pagesize;
natural_t mem_total = mem_used + mem_free;
NSLog(#"used: %u free: %u total: %u", mem_used, mem_free, mem_total);
}
A. Where do I put this function in my Xcode project?
B. How do I call it? Obviously I'd like to set up to continuously monitor memory.
A. Where do I put this function in my Xcode project?
Put the definition in a separate .c file, and a declaration in a separate header file.
PrintFreeMem.h
extern void print_free_memory();
PrintFreeMem.c
#include "PrintFreeMem.h"
void print_free_memory() {
// Your implementation
}
B. How do I call it?
You can call it the way you call regular C functions, after including its header file:
#include "PrintFreeMem.h"
-(void)myMethod {
...
print_free_memory();
}
You can do the declaration in the header file and write this function in the implementation file or you can simply put the function in the implementation file but then function can be called only from the lines below
print_free_memory ();
Hope this works

AudioFileOpen not opening with read/write permissions

I am trying to programmatically optimize AAC files (.m4a) that were created by software other than iTunes or afconvert using AudioFileOptimize().
However, when I attempt to do an AudioFileOpen with read/write permission and it gives me an error. I can only open with read permissions even if I manually set the POSIX permissions to 0666 (r/w for everyone.)
I'm using the AudioFileOpenUrl method described here and get back 'prm?' (kAudioFilePermissionsError).
Here is the code that is throwing the error:
#import <Cocoa/Cocoa.h>
#import <CoreFoundation/CoreFoundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import "CAXException.h"
int main(int argc, const char * argv[])
{
int result = 0;
try {
//optimize the output file:
AudioFileID outfile;
NSURL *outputFileURL = [NSURL fileURLWithPath:#"/DIR/PATH TO.m4a"];
OSStatus err = AudioFileOpenURL((__bridge CFURLRef)outputFileURL, kAudioFileReadWritePermission, 0, &outfile);
XThrowIfError(err, "AudioFileOpenURL");
err = AudioFileOptimize(outfile); //Optimize the file
XThrowIfError(err, "AudioFileOptimize");
err = AudioFileClose(outfile);
XThrowIfError(err, "AudioFileClose");
} catch (CAXException e) {
char str[32];
printf ("CAXException thrown from CoreAudioConverter.mm. : %s, %s\n", e.FormatError(str), e.mOperation);
result = 1;
} catch (...) {
result = 1;
}
return result;
}
This outputs:
CAXException thrown from CoreAudioConverter.mm. : 'prm?', AudioFileOpenURL
If anyone can help, that would be AWESOME!
Side-note, has anyone done any work programmatically adding streaming hints to aac files?
Thanks!
AudioFile Services will not allow Write access to certain files:
There is however a limitation when working with MPEG-4 file types which include; .mp4 (kAudioFileMPEG4Type), .m4a (kAudioFileM4AType), .3g2 (kAudioFile3GP2Type) and .3gp (kAudioFile3GPType).
AudioFile does not currently support writing to existing files of these types.
See:
http://developer.apple.com/library/mac/#qa/qa1676/_index.html

How to write a Linux Driver, that only forwards file operations?

I need to implement a Linux Kernel Driver, that (in the first step) only forwards all file operations to another file (in later steps, this should be managed and manipulated, but I don't want to discuss this here).
My idea is the following, but when reading, the kernel crashes:
static struct {
struct file *file;
char *file_name;
int open;
} file_out_data = {
.file_name = "/any_file",
.open = 0,
};
int memory_open(struct inode *inode, struct file *filp) {
PRINTK("<1>open memory module\n");
/*
* We don't want to talk to two processes at the same time
*/
if (file_out_data.open)
return -EBUSY;
/*
* Initialize the message
*/
Message_Ptr = Message;
try_module_get(THIS_MODULE);
file_out_data.file = filp_open(file_out_data.file_name, filp->f_flags, filp->f_mode); //here should be another return handling in case of fail
file_out_data.open++;
/* Success */
return 0;
}
int memory_release(struct inode *inode, struct file *filp) {
PRINTK("<1>release memory module\n");
/*
* We're now ready for our next caller
*/
file_out_data.open--;
filp_close(file_out_data.file,NULL);
module_put(THIS_MODULE);
/* Success */
return 0;
}
ssize_t memory_read(struct file *filp, char *buf,
size_t count, loff_t *f_pos) {
PRINTK("<1>read memory module \n");
ret=file_out_data.file->f_op->read(file_out_data.file,buf,count,f_pos); //corrected one, false one is to find in the history
return ret;
}
So, can anyone please tell me why?
Don't use set_fs() as there is no reason to do it.
Use file->f_fop->read() instead of the vfs_read. Take a look at the file and file_operations structures.
Why are you incrementing file_out_data.open twice and decrementing it once? This could cause you to use file_out_data.file after it has been closed.
You want to write memory in your file ou read?
Because you are reading and not writing...
possible i'm wrong