6 #include "squashfuse.h"
13 #define NULL ((void *) 0)
22 extern int _binary_runtime_start
;
23 extern int _binary_runtime_size
;
26 const char *argp_program_version
=
29 const char *argp_program_bug_address
=
30 "<probono@puredarwin.org>";
33 "appimagetool -- Generate, extract, and inspect AppImages";
35 /* This structure is used by main to communicate with parse_opt. */
38 char *args
[2]; /* SOURCE and DESTINATION */
39 int verbose
; /* The -v flag */
40 int list
; /* The -l flag */
41 char *dumpfile
; /* Argument for -d */
45 static struct argp_option options
[] =
47 {"verbose", 'v', 0, 0, "Produce verbose output"},
49 "List files in SOURCE AppImage"},
50 {"dump", 'd', "FILE", 0,
51 "Dump FILE from SOURCE AppImage to stdout"},
57 parse_opt (int key
, char *arg
, struct argp_state
*state
)
59 struct arguments
*arguments
= state
->input
;
64 arguments
->verbose
= 1;
70 arguments
->dumpfile
= arg
;
73 if (state
->arg_num
>= 3)
77 arguments
->args
[state
->arg_num
] = arg
;
80 if (state
->arg_num
< 1)
86 return ARGP_ERR_UNKNOWN
;
92 static char args_doc
[] = "SOURCE {DESTINATION}";
95 static struct argp argp
= {options
, parse_opt
, args_doc
, doc
};
98 // #####################################################################
101 static void die(const char *msg
) {
102 fprintf(stderr
, "%s\n", msg
);
106 int is_directory(const char *path
) {
108 if (stat(path
, &statbuf
) != 0)
110 return S_ISDIR(statbuf
.st_mode
);
113 int is_regular_file(const char *path
)
115 struct stat path_stat
;
116 stat(path
, &path_stat
);
117 return S_ISREG(path_stat
.st_mode
);
120 /* Function that prints the contents of a squashfs file
121 * using libsquashfuse (#include "squashfuse.h") */
122 int sfs_ls(char* image
) {
123 sqfs_err err
= SQFS_OK
;
127 if ((err
= sqfs_open_image(&fs
, image
, 0)))
128 die("sqfs_open_image error");
130 if ((err
= sqfs_traverse_open(&trv
, &fs
, sqfs_inode_root(&fs
))))
131 die("sqfs_traverse_open error");
132 while (sqfs_traverse_next(&trv
, &err
)) {
134 printf("%s\n", trv
.path
);
138 die("sqfs_traverse_next error");
139 sqfs_traverse_close(&trv
);
141 sqfs_fd_close(fs
.fd
);
145 /* Generate a squashfs filesystem using mksquashfs on the $PATH */
146 int sfs_mksquashfs(char *source
, char *destination
) {
147 pid_t parent
= getpid();
151 // error, failed to fork()
153 } else if (pid
> 0) {
155 waitpid(pid
, &status
, 0);
158 execlp("mksquashfs", "mksquashfs", source
, destination
, "-root-owned", "-noappend", (char *)0);
159 perror("execlp"); // execvp() returns only on error
160 return(-1); // exec never returns
165 /* Generate a squashfs filesystem
166 * The following would work if we link to mksquashfs.o after we renamed
167 * main() to mksquashfs_main() in mksquashfs.c but we don't want to actually do
168 * this because squashfs-tools is not under a permissive license
169 int sfs_mksquashfs(char *source, char *destination) {
171 child_argv[0] = NULL;
172 child_argv[1] = source;
173 child_argv[2] = destination;
174 child_argv[3] = "-root-owned";
175 child_argv[4] = "-noappend";
176 mksquashfs_main(5, child_argv);
180 // #####################################################################
182 int main (int argc
, char **argv
)
185 /* Initialize binreloc so that we always know where we live */
187 if (br_init (&error
) == 0) {
188 printf ("Warning: binreloc failed to initialize (error code %d)\n", error
);
190 printf ("This tool is located at %s\n", br_find_exe_dir(NULL
));
192 struct arguments arguments
;
194 /* Set argument defaults */
196 arguments
.verbose
= 0;
197 arguments
.dumpfile
= NULL
;
199 /* Where the magic happens */
200 argp_parse (&argp
, argc
, argv
, 0, 0, &arguments
);
202 /* If in list mode */
204 sfs_ls(arguments
.args
[0]);
208 /* If in dumpfile mode */
209 if (arguments
.dumpfile
){
210 fprintf (stdout
, "%s from the AppImage %s should be dumped to stdout\n", arguments
.dumpfile
, arguments
.args
[0]);
211 die("To be implemented");
214 /* Print argument values */
215 if (arguments
.verbose
)
216 fprintf (stdout
, "Original SOURCE = %s\nOriginal DESTINATION = %s\n",
220 /* If the first argument is a directory, then we assume that we should package it */
221 if(is_directory(arguments
.args
[0])){
223 char source
[PATH_MAX
];
224 realpath(arguments
.args
[0], source
);
225 if (arguments
.args
[1]) {
226 destination
= arguments
.args
[1];
228 /* No destination has been specified, to let's construct one
229 * TODO: Find out the architecture and use a $VERSION that might be around in the env */
230 destination
= basename(br_strcat(source
, ".AppImage"));
231 fprintf (stdout
, "DESTINATION not specified, so assuming %s\n", destination
);
233 fprintf (stdout
, "%s should be packaged as %s\n", arguments
.args
[0], destination
);
235 /* mksquashfs can currently not start writing at an offset,
236 * so we need a tempfile. https://github.com/plougher/squashfs-tools/pull/13
237 * should hopefully change that. */
239 fprintf (stderr
, "Generating squashfs...\n");
240 tempfile
= br_strcat(destination
, ".temp");
241 int result
= sfs_mksquashfs(source
, tempfile
);
243 die("sfs_mksquashfs error");
245 fprintf (stderr
, "Generating AppImage...\n");
246 FILE *fpsrc
= fopen(tempfile
, "rb");
248 die("Not able to open the tempfile for reading, aborting");
250 FILE *fpdst
= fopen(destination
, "w");
252 die("Not able to open the destination file for writing, aborting");
255 /* runtime is embedded into this executable
256 * http://stupefydeveloper.blogspot.de/2008/08/cc-embed-binary-data-into-elf.html */
257 int size
= (int)&_binary_runtime_size
;
258 char *data
= (char *)&_binary_runtime_start
;
259 if (arguments
.verbose
)
260 printf("Size of the embedded runtime: %d bytes\n", size
);
261 /* Where to store updateinformation. Fixed offset preferred for easy manipulation
262 * after the fact. Proposal: 4 KB at the end of the 128 KB padding.
263 * Hence, offset 126976, max. 4096 bytes long.
264 * Possibly we might want to store additional information in the future.
265 * Assuming 4 blocks of 4096 bytes each.
267 if(size
> 128*1024-4*4096-2){
268 die("Size of the embedded runtime is too large, aborting");
270 // printf("%s", data);
271 fwrite(data
, size
, 1, fpdst
);
273 if(ftruncate(fileno(fpdst
), 128*1024) != 0) {
274 die("Not able to write padding to destination file, aborting");
277 fseek (fpdst
, 0, SEEK_END
);
282 fread(&byte
, sizeof(char), 1, fpsrc
);
283 fwrite(&byte
, sizeof(char), 1, fpdst
);
289 fprintf (stderr
, "Marking the AppImage as executable...\n");
290 if (chmod (destination
, 0755) < 0) {
291 printf("Could not set executable bit, aborting\n");
294 if(unlink(tempfile
) != 0) {
295 die("Could not delete the tempfile, aborting");
297 fprintf (stderr
, "Success\n");
300 /* If the first argument is a regular file, then we assume that we should unpack it */
301 if(is_regular_file(arguments
.args
[0])){
302 fprintf (stdout
, "%s is a file, assuming it is an AppImage and should be unpacked\n", arguments
.args
[0]);
303 die("To be implemented");