summaryrefslogtreecommitdiffstats
path: root/ACTIONS-README
blob: ede4f76d2f3bea96cd539ce11c16d466f8df2bbc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
                           MKSQUASHFS ACTIONS
                           ==================

The new Mksquashfs Actions code allows an "action" to be executed
on a file if one or more "tests" succeed.  If you're familiar
with the "find" command, then an action is similar to "-print",
and a test is similar to say "-name" or "-type".

Actions add greater flexibility when building images from sources.
They can be used to optimise compression, I/O performance, and they
also allow more control on the exclusion of files from the source, and
allow uid/gid and mode to be changed on a file basis.

1. Specification
================

Actions can be specified on the command line with the -action option.
They can also be put into a file, and added  with the -action-file
option.  If put into a file, there is one action per line.  But, lines
can be extended over many lines with continuation (\).

If you want to get a log of what actions were performed, and the values
returned by the tests for each file, you can use the -log-action option
for the command line and -log-action-file for action files.

Similarly there are -true-action (-true-action-file) and -false-action
(-false-action-file) options which log if the tests evaluated to TRUE,
and vice-versa:

2. Syntax
=========

An action consists of two parts, separated by an "@".  The action to
be executed is placed before the @, and one or more tests are
placed afer the @.  If the action or tests has an argument, it is
given in brackets.  Brackets are optional if no argument is needed,
e.g.

compressed()@name("filename")

compressed@name("filename")

do exactly the same thing.

Arguments can be either numeric or string, depending on the
action and test.

String arguments can be enclosed in double-quotes ("), to prevent the
parser from treating characters within it specially.  Within double-quotes
only '\' is treatedly specially, and only at the end of a line.  Special
characters can also be backslashed (\) to prevent interpretation by the
parser, e.g. the following is equivalent:

compressed@name(file\ name\ with\ \&&\ and\ spaces)

compressed@name("file name with && and spaces")

Numeric arguments are of the form [range]number[size], where
[range] is either

	'<' or '-', match on less than number
	'>' or '+', match on greater than number
	"" (nothing), match on exactly number

[size] is either:
	"" (nothing), number
	'k' or 'K',  number * 2^10
	'm' or 'M', number * 2^20
	'g' or 'G', number * 2^30

e.g. the following is equivalent:

compressed@filesize(-81920)
compressed@filesize(<80K)

Both will match on files less than 80K in size.

Characters which are treated specially by the parser are * ( ) && ||
! , @.  Plus whitespace (spaces and tabs).

Note: if the action is typed on the command line, then many special
characters will be evaluated by the shell, and you should always
check what is actually being passed to Mksquashfs.  If in doubt use
-action-file where the additional complexities of shell evaluation is
avoided.

For example this action line will work in an action file

compressed()@name("file name")

But, if typed on the command line, it will need to be:

% mksquashfs source image -action "compressed()@name(\"file name\")"


3. Logical operators
====================

Tests can be combined with the logical operators && (and), || (or) and
can be negated with the unary ! (not).  Expressions thus formed can also
be bracketed with "(" and ")", to create nested expressions.

Operators do not have precedence and are evaluated strictly left to
right.  To enforce precedence use brackets, e.g.

test1 && test2 || test3

will be evaluated as

(test1 && test2) || test3

&& and || are short-circuit operators, where the rhs (right hand side)
is only evaluated if the lhs (left hand side) has been insufficient
to determine the value.  For example in the above, test3 will only be
evaluated if (test1 && test2) evaluates to FALSE.

4. Test operators
=================

The following test operators are supported:

4.1 name(pattern)

Returns TRUE if the filename (basename without leading directory components)
matches pattern.  Pattern can have wildcards.

4.2 pathname(pattern)
---------------------

Returns TRUE if the full pathname of the file matches pattern.
Pattern can have wildcards.

4.3 subpathname(pattern)
------------------------

Returns TRUE if the <n> directory components of pattern match the first <n>
directory components of the pathname.

For example, if pattern has one component:

subpathname(dir1) will match "dir1/somefile", "dir1/dir2/somefile" etc.

If pattern had two components:

subpathname(dir1/dir2) will match ""dir1/dir2/somefile" etc.

Pattern can have wildcards.

4.4 filesize(value)
-------------------

Return TRUE if the size of the file is less than, equal to, or larger than
<value>, where <value> can be [<-]number, number, [>+]number.  Returns FALSE
on anything not a file.

4.5 dirsize(value)
------------------

Return TRUE if the size of the directory is less than, equal to, or larger than
<value>, where <value> can be [<-]number, number, [>+]number.  Returns FALSE
on anything not a directory.

4.6 size(value)
---------------

Return TRUE if the size of the file is less than, equal to, or larger than
<value>, where <value> can be [<-]number, number, [>+]number.  Works on any
file type.

4.7 inode(value)
----------------

Return TRUE if the inode number is less than, equal to, or larger than
<value>, where <value> can be [<-]number, number, [>+]number.

4.8 nlink(value)
----------------

Return TRUE if the nlink count is less than, equal to, or larger than
<value>, where <value> can be [<-]number, number, [>+]number.

4.9 fileblocks(value)
---------------------

Return TRUE if the size of the file in blocks (512 bytes) is less than, equal
to, or larger than <value>, where <value> can be [<-]number, number, [>+]number.
Returns FALSE on anything not a file.

4.10 dirblocks(value)
---------------------

Return TRUE if the size of the directory in blocks (512 bytes) is less than,
equal to, or larger than <value>, where <value> can be [<-]number, number,
[>+]number.  Returns FALSE on anything not a directory.

4.11 blocks(value)
------------------

Return TRUE if the size of the file in blocks (512 bytes) is less than, equal
to, or larger than <value>, where <value> can be [<-]number, number, [>+]number.
Works on any file type.

4.12 uid(value)
---------------
Return TRUE if the uid value is less than, equal to, or larger than
<value>, where <value> can be [<-]number, number, [>+]number.

4.13 gid(value)
---------------

Return TRUE if the gid value is less than, equal to, or larger than
<value>, where <value> can be [<-]number, number, [>+]number.

4.14 user(string)
-----------------

Return TRUE if the file owner matches <string>.

4.15 group(string)
------------------

Return TRUE if the file group matches <string>.

4.16 depth(value)
-----------------

Return TRUE if file is at depth less than, equal to, or larger than <value>,
where <value> can be [<-]number, number, [>+]number.  Top level directory is
depth 1.

4.17 dircount(value)
--------------------

Return TRUE if the number of files in the directory is less than, equal to, or
larger than <value>, where <value> can be [<-]number, number, [>+]number.
Returns FALSE on anything not a directory.

4.18 filesize_range(minimum, maximum)
-------------------------------------

Return TRUE if the size of the file is within the range [<minimum>, <maximum>]
inclusive.  Returns FALSE on anything not a file.

4.19 dirsize_range(minimum, maximum)
------------------------------------

Return TRUE if the size of the directory is within the rang
[<minimum>, <maximum>] inclusive.  Returns FALSE on anything not a directory.

4.20 size_range(minimum, maximum)
---------------------------------

Return TRUE if the size of the file is within the range [<minimum>, <maximum>]
inclusive.  Works on any file type.

4.21 inode_range(minimum, maximum)
----------------------------------

Return TRUE if the inode number is within the range [<minimum>, <maximum>]
inclusive.

4.22 fileblocks_range(minimum, maximum)
---------------------------------------

Return TRUE if the size of the file in blocks (512 bytes) is within the range
[<minimum>, <maximum>] inclusive.  Returns FALSE on anything not a file.

4.23 dirblocks_range(minimum, maximum)
--------------------------------------

Return TRUE if the size of the directory in blocks (512 bytes) is within the
range [<minimum>, <maximum>] inclusive.  Returns FALSE on anything not a
directory.

4.24 blocks_range(minimum, maximum)
-----------------------------------

Return TRUE if the size of the file in blocks (512 bytes) is within the range
[<minimum>, <maximum>] inclusive.  Works on any file type.

4.25 uid_range(minimum, maximum)
--------------------------------

Return TRUE if the file uid is within the range [<minimum>, <maximum>]
inclusive.

4.26 gid_range(minimum, maximum)
--------------------------------

Return TRUE if the file gid is within the range [<minimum>, <maximum>]
inclusive.

4.27 depth_range(minimum, maximum)
----------------------------------

Return TRUE if file is at depth within the range [<minimum>, <maximum>].
Top level directory is depth 1.

4.28 dircount_range(minimum, maximum)
-------------------------------------

Returns TRUE is the number of files in the directory is within the range
[<minimum>, <maximum>].  Returns FALSE on anything not a directory.

4.29 type(c)
------------

Returns TRUE if the file matches type <c>.  <c> can be
	f - regular file
	d - directory
	l - symbolic link
	c - character device
	b - block device
	p - Named Pipe / FIFO
	s - socket


4.30 perm(mode)
---------------

Return TRUE if file permissions match <mode>.  <Mode> is the same as
find's -perm option:

	perm(mode) - TRUE if file's permission bits are exactly <mode>.
		   <mode> can be octal or symbolic.

	perm(-mode) - TRUE if all <mode> permission bits are set for this file.
		    <mode> can be octal or symbolic.

	perm(/mode) - TRUE if any <mode> permission bits are set for this file.
		    <mode> can be octal or symbolic.

	The symbolic mode is of the format [ugoa]*[[+-=]PERMS]+
		PERMS = [rwxXst]+ or [ugo]
		and can be repeated separated with commas.

Examples:

perm(0644) match on a file with permissions exactly rw-r--r--.
perm(u=rw,go=r) as above, but expressed symbolically.

perm(/222) match on a file which is writable for any of user, group, other,
perm(/u=w,g=w,o=w) as above but expressed symbolically,
perm(/ugo=w) as above but specified more concisely.

4.31 file(string)
-----------------

Execute "file command" on file, and return TRUE if the output
matches the substring <string>, for example

file(ASCII text) will return TRUE if the file is ASCII text.

Note, this is an expensive test, and should only be run if the file
has matched a number of other short-circuit tests.

4.32 exists()
-------------

Test if the file pointed to by the symbolic link exists within the
output filesystem, that is, whether the symbolic link has a relative
path and the relative path can be resolved to an entry within the
output filesystem.

If the file isn't a symbolic link then the test always returns TRUE.

4.33 absolute()
---------------

Test if the symbolic link is absolute, which by definition means
it points outside of the output filesystem (unless it is to be mounted
as root).  If the file isn't a symbolic link then the test always returns
FALSE.

4.34 readlink(expression)
-------------------------

Follow or dereference the symbolic link, and evaluate <expression> in
the context of the file pointed to by the symbolic link.  All inode
attributes, pathname, name and depth all refer to the dereferenced
file.

If the symbolic link cannot be dereferenced because it points to something
not in the output filesystem (see exists() function above), then FALSE is
returned.  If the file is not a symbolic link, the result is the same as
<expression>, i.e. readlink(<expression>) == <expression>.

Examples:

readlink("name(*.[ch])") returns TRUE if the file referenced by the symbolic
link matches *.[ch].

Obviously, expressions created with && || etc. can be specified.

readlink("depth(1) && filesize(<20K)") returns TRUE if the file referenced
by the symbolic link is a regular file less than 20K in size and in the
top level directory.

Note: in the above tests the embedded expression to be evaluated is enclosed
in double-quotes ("), this is to prevent the special characters being
evaluated by the parser when parsed at the top-level.  Readlink causes
re-evaluation of the embedded string.

4.36 eval(path, expression)
---------------------------

Follow <path> (arg1), and evaluate the <expression> (arg2) in the
context of the file discovered by following <path>.  All inode attributes,
pathname, name and depth all refer to the file discovered by following
<path>.

This test operation allows you to add additional context to the evaluation
of the file being scanned, such as "if the current file is XXX, test if the
parent is YYY, and then do ...".  Often times you need or want to test
a combination of file status.

The <path> can be absolute (in which case it is from the root directory of the
output filesystem), or it can be relative to the current file.  Obviously
relative paths are more useful.

If the file referenced by <path> does not exist in the output filesystem,
then FALSE is returned.

Examples of usage:

1. If a directory matches pattern, check that it contains a ".git" directory.
   This allows you to exclude git repositories, with a double check that it is
   a git repository by checking for the .git subdirectory.

   prune@name(*linux*) && type(d) && eval(.git, "type(d)")

   This action will match on any directory named *linux*, and exclude it if
   it contains a .git subdirectory.


2. If a file matches a pattern, check that the parent directory matches
   another pattern.  This allows you to delete files if and only if they
   are in a particular directory.

   prune@name(*.[ch]) && eval(.., "name(*linux*)")

   This action will delete *.[ch] files, but, only if they are in a directory
   matching *linux*.

4.37 false
----------

Always returns FALSE.

4.38 true
---------

Always returns TRUE.

5. Actions
==========

An action is something which is done (or applied) to a file if the expression
(made up of the above test operators) returns TRUE.

Different actions are applied in separate phases or gated, rather than being
applied all at once.  This is to ensure that you know what the overall
state of the filesystem is when an action is applied.  Or to put it another
way, if you have an action that depends on another action having already been
processed (for the entire filesystem), you'll want to know that is how
they will be applied.

5.1 Actions applied at source filesystem reading (stage 1)
----------------------------------------------------------

5.1.1 exclude()
---------------

This action excludes all files and directories where the expression
returns TRUE.

Obviously this action allows much greater control over which files are
excluded than the current name/pathname matching.

Examples:

1. Exclude any files/directories belonging to user phillip

exclude@user(phillip)

2. Exclude any regular files larger than 1M

exclude@filesize(>1M)

3. Only archive files/directories to a depth of 3

exclude@depth(>3)

4. As above but also exclude directories at the depth of 3
   (which will be empty due to the above exclusion)

exclude@depth(>3) || (depth(3) && type(d))

Which obviously reduces to

exclude@depth(3) && type(d)

Note: the following tests do not work in stage 1, and so they can't be
used in the exclude() action (see prune() action for explanation and
alternative).

	dircount()
	dircount_range()
	exists()
	absolute()
	readlink()
	eval()

5.2 Actions applied at directory scanning (stage 2)
---------------------------------------------------

5.2.1 fragment(name)
--------------------

Place all files matching the expression into a specialised fragment
named <name>.  This can increase compression and/or improve
I/O by placing similar fragments together.

Examples:

1. fragment(cfiles)@name(*.[ch])

Place all C files into special fragments reserved for them.

2. fragment(phillip)@user(phillip)

Place all files owned by user Phillip into special fragments.

5.2.2 fragments()
-----------------

Tell Mksquashfs to use fragment packing for the files matching the
expression.

For obvious reasons this should be used in conjunction with the global
Mksquashfs option -no-fragments.  By default all files are packed into
fragments if they're less than the block size.

5.2.3 no-fragments()
--------------------

Tell Mksquashfs to not pack the files matching the expression into
fragments.

This can be used where you want to optimise I/O latency by not packing
certain files into fragments.

5.2.4 tailend()
---------------

Tell Mksquashfs to use tail-end packing for the files matching the
expression.  Normally Mksquashfs does not pack tail-ends into fragments,
as it may affect I/O performance because it may produce more disk head
seeking.

But tail-end packing can increase compression.  Additionally with modern
solid state media, seeking is not such a major issue anymore.

5.2.5. no-tailend()
-------------------

Tell Mksquashfs not to use tail-end packing for the files matching the
exppression.

For obvious reasons this should be used in conjuction with the global
Mksquashfs option -always-use-fragments.  By default tail-ends are not
packed into fragments.

5.2.6 compressed()
------------------

Tell Mksquashfs to compress the fies matching the expression.

For obvious reasons this should be used in conjunction with the global
Mksquashfs options -noD and -noF.  File are by default compressed.

5.2.7 uncompressed()
--------------------

Tell Mksquashfs to not compress the files matching the expression.

This action obviously can be used to avoid compressing already compressed
files (XZ, GZIP etc.).

5.2.8 uid(uid or user)
----------------------

Set the ownership of the files matching the expression to uid (if arg1
is a number) or user (if arg1 is a string).

5.2.9 gid(gid or group)
-----------------------

Set the group of the files matching the expression to gid (if arg1
is a number) or group (if arg1 is a string).

5.2.10 guid(uid/user, gid/group)
--------------------------------

Set the uid/user and gid/group of the files matching the expression.

5.2.11 chmod(mode)
------------------

Mode can be octal, or symbolic.

If Mode is Octal, the permission bits are set to the octal value.

If Mode is Symbolic, permissions can be Set, Added or Removed.

The symbolic mode is of the format [ugoa]*[[+-=]PERMS]+
	PERMS = [rwxXst]+ or [ugo]
	and the above sequence can be repeated separated with commas.

A combination of the letters ugoa, specify which permission bits will
be affected, u means user, g means group, o means other, and a
means all or ugo.

The next letter is +, - or =.  The letter + means add to the existing
permission bits, - means remove the bits from the existing permission
bits, and = means set the permission bits.

The permission bits (PERMS) are a combination of [rwxXst] which
sets/adds/removes those bits for the specified ugoa combination. They
can alternatively be u, g or o, which takes the permission bits from the
user, group or other of the file respectively.

Examples:

1. chmod(u+r)

Adds the read permission to user.

2. chmod(ug+rw)

Adds the read and write permissions to both user and group.

3. chmod(u=rw,go=r)

Sets the permissions to rw-r--r--, which is eqivalent to

4. chmod(644)

5. cgmod(ug=o)

Sets the user and group permissions to the permissions for other.

5.3 Actions applied at second directory scan (stage 3)
------------------------------------------------------

5.3.1 prune()

The prune() action deletes the file or directory (and everything
underneath it) that matches the expression.  In that respect it is
identical to the exclude() action, except that it takes place in the
third stage, rather than the first stage.  There are a number of
reasons to have a prune() action in addition to an exclude()
action.

1. In the first stage Mksquashfs is building an in-memory representation
   of the filesystem to be compressed.  At that point some of the tests
   don't work because they rely on an in-memory representation having been
   built.

   So the following tests don't work in stage 1, and so they can't be
   used in the exclude() action.

	dircount()
	dircount_range()
	exists()
	absolute()
	readlink()
	eval()

   If you want to use these tests, you have to use the prune() action.

2. Many exclusion/pruning operations may only be easily applied after
   transformation actions have been applied in stages 1 & 2.

   For example, you may change the ownership and permissions of
   matching files in stage 2, and then want to delete files based on
   some criteria which relies on this having taken place.

5.4. Actions applied at third directory scan (stage 4)
------------------------------------------------------

5.4.1 empty(reason)

The empty() action deletes any directory which matches the expression,
and which is also empty for <reason>.  <reason> is one of "excluded",
"source" and "all".  If no argument is given, empty() defaults to "all".

The reason "excluded" means the directory has become empty due to
the exclude() or prune() actions or by exclusion on the command line.
The reason "source" means the directory was empty in the source filesystem.
The reason "all" means it is empty for either one of the above two reasons.

This action is often useful when exclusion has produced an empty
directory, or a hierarchy of directories which are empty but for a
sub-directory which is empty but for a sub-directory until an
empty directory is reached.

Example

1. Exclude any file which isn't a directory, and then clean-up
   any directories which are empty as a result.

	exclude@!type(d)
	empty(excluded)@true

This will produce an empty filesystem, unless there were some
directories that were originally empty.

Changing the empty action to

	exclude@!type(d)
	empty@true

Will produce an empty filesystem.

5.5 Actions performed at filesystem creation (stage 6)
------------------------------------------------------

5.5.1 xattrs-exclude(regex)

The xattrs-exclude action excludes any xattr names matching <regex>.  <regex> is
a POSIX regular expression, e.g. xattrs-exclude("^user.")  excludes xattrs from
the user namespace.

5.5.2 xattrs-include(regex)

The xattrs-include action includes any xattr names matching <regex>.  <regex> is
a POSIX regular expression, e.g. -xattrs-include("^user.") includes xattrs from
the user namespace.

5.5.3 xattrs-add(name=val)

The xattrs-add action adds the xattr <name> with contents <val>.  If an user
xattr it can be added to regular files and directories (see man 7 xattr).
Otherwise it can be added to all files.

The extended attribute value by default will be treated as binary (i.e. an
uninterpreted byte sequence), but it can be prefixed with 0s, where it will be
treated as base64 encoded, or prefixed with 0x, where it will be treated as
hexidecimal.

Obviously using base64 or hexidecimal allows values to be used that cannot be
entered on the command line such as non-printable characters etc.  But it
renders the string non-human readable.  To keep readability and to allow
non-printable characters to be entered, the 0t prefix is supported.  This
encoding is similar to binary encoding, except backslashes are specially
treated, and a backslash followed by three octal digits can be used to encode
any ASCII character, which obviously can be used to encode non-printable values.

The following four actions are equivalent

-xattrs-add("user.comment=hello world")
-xattrs-add("user.comment=0saGVsbG8gd29ybGQ=")
-xattrs-add("user.comment=0x68656c6c6f20776f726c64")
-xattrs-add("user.comment=0thello world")

Obviously in the above example there are no non-printable characters and so
the 0t prefixed string is identical to the first line.  The following three
actions are identical, but where the space has been replaced by the
non-printable NUL '\0' (null character).

-xattrs-add("user.comment=0thello\000world")
-xattrs-add("user.comment=0saGVsbG8Ad29ybGQ=")
-xattrs-add("user.comment=0x68656c6c6f00776f726c64")