Page Menu
Home
Musing Studio
Search
Configure Global Search
Log In
Files
F13777071
mksyscall_aix_ppc64.pl
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
14 KB
Subscribers
None
mksyscall_aix_ppc64.pl
View Options
#!/usr/bin/env perl
# Copyright 2018 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
# This program reads a file containing function prototypes
# (like syscall_aix.go) and generates system call bodies.
# The prototypes are marked by lines beginning with "//sys"
# and read like func declarations if //sys is replaced by func, but:
# * The parameter lists must give a name for each argument.
# This includes return parameters.
# * The parameter lists must give a type for each argument:
# the (x, y, z int) shorthand is not allowed.
# * If the return parameter is an error number, it must be named err.
# * If go func name needs to be different than its libc name,
# * or the function is not in libc, name could be specified
# * at the end, after "=" sign, like
# //sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error) = libsocket.getsockopt
# This program will generate three files and handle both gc and gccgo implementation:
# - zsyscall_aix_ppc64.go: the common part of each implementation (error handler, pointer creation)
# - zsyscall_aix_ppc64_gc.go: gc part with //go_cgo_import_dynamic and a call to syscall6
# - zsyscall_aix_ppc64_gccgo.go: gccgo part with C function and conversion to C type.
# The generated code looks like this
#
# zsyscall_aix_ppc64.go
# func asyscall(...) (n int, err error) {
# // Pointer Creation
# r1, e1 := callasyscall(...)
# // Type Conversion
# // Error Handler
# return
# }
#
# zsyscall_aix_ppc64_gc.go
# //go:cgo_import_dynamic libc_asyscall asyscall "libc.a/shr_64.o"
# //go:linkname libc_asyscall libc_asyscall
# var asyscall syscallFunc
#
# func callasyscall(...) (r1 uintptr, e1 Errno) {
# r1, _, e1 = syscall6(uintptr(unsafe.Pointer(&libc_asyscall)), "nb_args", ... )
# return
# }
#
# zsyscall_aix_ppc64_ggcgo.go
# /*
# int asyscall(...)
#
# */
# import "C"
#
# func callasyscall(...) (r1 uintptr, e1 Errno) {
# r1 = uintptr(C.asyscall(...))
# e1 = syscall.GetErrno()
# return
# }
use
strict
;
my
$
cmdline
=
"mksyscall_aix_ppc64.pl "
.
join
(
' '
,
@
ARGV
);
my
$
errors
=
0
;
my
$
_
32
bit
=
""
;
my
$
tags
=
""
;
#
build
tags
my
$
aix
=
0
;
my
$
solaris
=
0
;
binmode
STDOUT
;
if
(
$
ARGV
[
0
]
eq
"-b32"
)
{
$
_
32
bit
=
"big-endian"
;
shift
;
}
elsif
(
$
ARGV
[
0
]
eq
"-l32"
)
{
$
_
32
bit
=
"little-endian"
;
shift
;
}
if
(
$
ARGV
[
0
]
eq
"-aix"
)
{
$
aix
=
1
;
shift
;
}
if
(
$
ARGV
[
0
]
eq
"-tags"
)
{
shift
;
$
tags
=
$
ARGV
[
0
];
shift
;
}
if
(
$
ARGV
[
0
]
=~
/^-/
)
{
print
STDERR
"usage: mksyscall_aix.pl [-b32 | -l32] [-tags x,y] [file ...]\n"
;
exit
1
;
}
sub
parseparamlist
(
$
)
{
my
(
$
list
)
=
@
_
;
$
list
=~
s/^\s*//
;
$
list
=~
s/\s*
$
//
;
if
(
$
list
eq
""
)
{
return
();
}
return
split
(
/\s*
,
\s*/
,
$
list
);
}
sub
parseparam
(
$
)
{
my
(
$
p
)
=
@
_
;
if
(
$
p
!
~
/^
(
\
S
*
)
(
\
S
*
)
$
/
)
{
print
STDERR
"$ARGV:$.: malformed parameter: $p\n"
;
$
errors
=
1
;
return
(
"xx"
,
"int"
);
}
return
(
$
1
,
$
2
);
}
my
$
package
=
""
;
# GCCGO
my
$
textgccgo
=
""
;
my
$
c_extern
=
"/*\n#include <stdint.h>\n"
;
# GC
my
$
textgc
=
""
;
my
$
dynimports
=
""
;
my
$
linknames
=
""
;
my
@vars
=
();
# COMMUN
my
$
textcommon
=
""
;
while
(
<>
)
{
chomp
;
s/\s+/
/
g
;
s/^\s+//
;
s/\s+
$
//
;
$
package
=
$
1
if
!
$
package
&&
/^
package
(
\
S
+
)
$
/
;
my
$
nonblock
=
/^\/\/sysnb
/
;
next
if
!
/^\/\/sys
/
&&
!
$
nonblock
;
#
Line
must
be
of
the
form
#
func
Open
(
path
string
,
mode
int
,
perm
int
)
(
fd
int
,
err
error
)
#
Split
into
name
,
in
params
,
out
params
.
if
(!
/^\/\/
sys
(
nb
)
?
(
\w+
)
\
(([
^
()]
*
)
\
)
\s*
(
?:\
(([
^
()]
+
)
\
))
?\s*
(
?:=\s*
(
?:
(
\w*
)
\.
)
?
(
\w*
))
?
$
/
)
{
print
STDERR
"$ARGV:$.: malformed //sys declaration\n"
;
$
errors
=
1
;
next
;
}
my
(
$
nb
,
$
func
,
$
in
,
$
out
,
$
modname
,
$
sysname
)
=
(
$
1
,
$
2
,
$
3
,
$
4
,
$
5
,
$
6
);
#
Split
argument
lists
on
comma
.
my
@in
=
parseparamlist
(
$
in
);
my
@out
=
parseparamlist
(
$
out
);
$
in
=
join
(
', '
,
@in
);
$
out
=
join
(
', '
,
@out
);
if
(
$
sysname
eq
""
)
{
$
sysname
=
"$func"
;
}
my
$
onlyCommon
=
0
;
if
(
$
func
eq
"readlen"
||
$
func
eq
"writelen"
||
$
func
eq
"FcntlInt"
||
$
func
eq
"FcntlFlock"
)
{
#
This
function
call
another
syscall
which
is
already
implemented
.
#
Therefore
,
the
gc
and
gccgo
part
must
not
be
generated
.
$
onlyCommon
=
1
}
#
Try
in
vain
to
keep
people
from
editing
this
file
.
#
The
theory
is
that
they
jump
into
the
middle
of
the
file
#
without
reading
the
header
.
$
textcommon
.
=
"// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"
;
if
(!
$
onlyCommon
)
{
$
textgccgo
.
=
"// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"
;
$
textgc
.
=
"// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"
;
}
#
Check
if
value
return
,
err
return
available
my
$
errvar
=
""
;
my
$
retvar
=
""
;
my
$
rettype
=
""
;
foreach
my
$
p
(
@out
)
{
my
(
$
name
,
$
type
)
=
parseparam
(
$
p
);
if
(
$
type
eq
"error"
)
{
$
errvar
=
$
name
;
}
else
{
$
retvar
=
$
name
;
$
rettype
=
$
type
;
}
}
$
sysname
=~
s/
([
a
-
z
])([
A
-
Z
])
/
$
{
1
}
_
$
2
/
g
;
$
sysname
=~
y
/
A
-
Z
/
a
-
z/
;
#
All
libc
functions
are
lowercase
.
#
GCCGO
Prototype
return
type
my
$
C_rettype
=
""
;
if
(
$
rettype
eq
"unsafe.Pointer"
)
{
$
C_rettype
=
"uintptr_t"
;
}
elsif
(
$
rettype
eq
"uintptr"
)
{
$
C_rettype
=
"uintptr_t"
;
}
elsif
(
$
rettype
=~
/^
_
/
)
{
$
C_rettype
=
"uintptr_t"
;
}
elsif
(
$
rettype
eq
"int"
)
{
$
C_rettype
=
"int"
;
}
elsif
(
$
rettype
eq
"int32"
)
{
$
C_rettype
=
"int"
;
}
elsif
(
$
rettype
eq
"int64"
)
{
$
C_rettype
=
"long long"
;
}
elsif
(
$
rettype
eq
"uint32"
)
{
$
C_rettype
=
"unsigned int"
;
}
elsif
(
$
rettype
eq
"uint64"
)
{
$
C_rettype
=
"unsigned long long"
;
}
else
{
$
C_rettype
=
"int"
;
}
if
(
$
sysname
eq
"exit"
)
{
$
C_rettype
=
"void"
;
}
#
GCCGO
Prototype
arguments
type
my
@c_in
=
();
foreach
my
$
i
(
0
..
$
#in
)
{
my
(
$
name
,
$
type
)
=
parseparam
(
$
in
[
$
i
]);
if
(
$
type
=~
/^\*/
)
{
push
@c_in
,
"uintptr_t"
;
}
elsif
(
$
type
eq
"string"
)
{
push
@c_in
,
"uintptr_t"
;
}
elsif
(
$
type
=~
/^\
[
\
](.
*
)
/
)
{
push
@c_in
,
"uintptr_t"
,
"size_t"
;
}
elsif
(
$
type
eq
"unsafe.Pointer"
)
{
push
@c_in
,
"uintptr_t"
;
}
elsif
(
$
type
eq
"uintptr"
)
{
push
@c_in
,
"uintptr_t"
;
}
elsif
(
$
type
=~
/^
_
/
)
{
push
@c_in
,
"uintptr_t"
;
}
elsif
(
$
type
eq
"int"
)
{
if
((
$
i
==
0
||
$
i
==
2
)
&&
$
func
eq
"fcntl"
){
#
These
fcntl
arguments
needs
to
be
uintptr
to
be
able
to
call
FcntlInt
and
FcntlFlock
push
@c_in
,
"uintptr_t"
;
}
else
{
push
@c_in
,
"int"
;
}
}
elsif
(
$
type
eq
"int32"
)
{
push
@c_in
,
"int"
;
}
elsif
(
$
type
eq
"int64"
)
{
push
@c_in
,
"long long"
;
}
elsif
(
$
type
eq
"uint32"
)
{
push
@c_in
,
"unsigned int"
;
}
elsif
(
$
type
eq
"uint64"
)
{
push
@c_in
,
"unsigned long long"
;
}
else
{
push
@c_in
,
"int"
;
}
}
if
(!
$
onlyCommon
){
#
GCCGO
Prototype
Generation
#
Imports
of
system
calls
from
libc
$
c_extern
.
=
"$C_rettype $sysname"
;
my
$
c_in
=
join
(
', '
,
@c_in
);
$
c_extern
.
=
"($c_in);\n"
;
}
#
GC
Library
name
if
(
$
modname
eq
""
)
{
$
modname
=
"libc.a/shr_64.o"
;
}
else
{
print
STDERR
"$func: only syscall using libc are available\n"
;
$
errors
=
1
;
next
;
}
my
$
sysvarname
=
"libc_${sysname}"
;
if
(!
$
onlyCommon
){
#
GC
Runtime
import
of
function
to
allow
cross
-
platform
builds
.
$
dynimports
.
=
"//go:cgo_import_dynamic ${sysvarname} ${sysname} \"$modname\"\n"
;
#
GC
Link
symbol
to
proc
address
variable
.
$
linknames
.
=
"//go:linkname ${sysvarname} ${sysvarname}\n"
;
#
GC
Library
proc
address
variable
.
push
@vars
,
$
sysvarname
;
}
my
$
strconvfunc
=
"BytePtrFromString"
;
my
$
strconvtype
=
"*byte"
;
#
Go
function
header
.
if
(
$
out
ne
""
)
{
$
out
=
" ($out)"
;
}
if
(
$
textcommon
ne
""
)
{
$
textcommon
.
=
"\n"
}
$
textcommon
.
=
sprintf
"func %s(%s)%s {\n"
,
$
func
,
join
(
', '
,
@in
),
$
out
;
#
Prepare
arguments
to
call
.
my
@argscommun
=
();
#
Arguments
in
the
commun
part
my
@argscall
=
();
#
Arguments
for
call
prototype
my
@argsgc
=
();
#
Arguments
for
gc
call
(
with
syscall6
)
my
@argsgccgo
=
();
#
Arguments
for
gccgo
call
(
with
C
.
name_of_syscall
)
my
$
n
=
0
;
my
$
arg_n
=
0
;
foreach
my
$
p
(
@in
)
{
my
(
$
name
,
$
type
)
=
parseparam
(
$
p
);
if
(
$
type
=~
/^\*/
)
{
push
@argscommun
,
"uintptr(unsafe.Pointer($name))"
;
push
@argscall
,
"$name uintptr"
;
push
@argsgc
,
"$name"
;
push
@argsgccgo
,
"C.uintptr_t($name)"
;
}
elsif
(
$
type
eq
"string"
&&
$
errvar
ne
""
)
{
$
textcommon
.
=
"\tvar _p$n $strconvtype\n"
;
$
textcommon
.
=
"\t_p$n, $errvar = $strconvfunc($name)\n"
;
$
textcommon
.
=
"\tif $errvar != nil {\n\t\treturn\n\t}\n"
;
push
@argscommun
,
"uintptr(unsafe.Pointer(_p$n))"
;
push
@argscall
,
"_p$n uintptr "
;
push
@argsgc
,
"_p$n"
;
push
@argsgccgo
,
"C.uintptr_t(_p$n)"
;
$
n++
;
}
elsif
(
$
type
eq
"string"
)
{
print
STDERR
"$ARGV:$.: $func uses string arguments, but has no error return\n"
;
$
textcommon
.
=
"\tvar _p$n $strconvtype\n"
;
$
textcommon
.
=
"\t_p$n, $errvar = $strconvfunc($name)\n"
;
$
textcommon
.
=
"\tif $errvar != nil {\n\t\treturn\n\t}\n"
;
push
@argscommun
,
"uintptr(unsafe.Pointer(_p$n))"
;
push
@argscall
,
"_p$n uintptr"
;
push
@argsgc
,
"_p$n"
;
push
@argsgccgo
,
"C.uintptr_t(_p$n)"
;
$
n++
;
}
elsif
(
$
type
=~
/^\
[
\
](.
*
)
/
)
{
#
Convert
slice
into
pointer
,
length
.
#
Have
to
be
careful
not
to
take
address
of
&a
[
0
]
if
len
==
0
:
#
pass
nil
in
that
case
.
$
textcommon
.
=
"\tvar _p$n *$1\n"
;
$
textcommon
.
=
"
\tif
len
(
$
name
)
>
0
{
\n\t\t_p$n
=
\&
$
name\
[
0
]
\n\t
}
\n
";
push @argscommun, "
uintptr
(
unsafe
.
Pointer
(
_
p$n
))
", "
len
(
$
name
)
";
push @argscall, "
_
p$n
uintptr
", "
_
lenp$n
int
";
push @argsgc, "
_
p$n
", "
uintptr
(
_
lenp$n
)
";
push @argsgccgo, "
C
.
uintptr_t
(
_
p$n
)
", "
C
.
size_t
(
_
lenp$n
)
";
$n++;
} elsif($type eq "
int64
" && $_32bit ne "") {
print STDERR "
$
ARGV
:
$
.
:
$
func
uses
int64
with
32
bits
mode
.
Case
not
yet
implemented\n
";
# if($_32bit eq "
big
-
endian
") {
# push @args, "
uintptr
(
$
name
>>
32
)
", "
uintptr
(
$
name
)
";
# } else {
# push @args, "
uintptr
(
$
name
)
", "
uintptr
(
$
name
>>
32
)
";
# }
# $n++;
} elsif($type eq "
bool
") {
print STDERR "
$
ARGV
:
$
.
:
$
func
uses
bool
.
Case
not
yet
implemented\n
";
# $text .= "
\tvar
_
p$n
uint32\n
";
# $text .= "
\tif
$
name
{
\n\t\t_p$n
=
1
\n\t
}
else
{
\n\t\t_p$n
=
0
\n\t
}
\n
";
# push @args, "
_
p$n
";
# $n++;
} elsif($type =~ /^_/ ||$type eq "
unsafe
.
Pointer
") {
push @argscommun, "
uintptr
(
$
name
)
";
push @argscall, "
$
name
uintptr
";
push @argsgc, "
$
name
";
push @argsgccgo, "
C
.
uintptr_t
(
$
name
)
";
} elsif($type eq "
int
") {
if (($arg_n == 0 || $arg_n == 2) && ($func eq "
fcntl
" || $func eq "
FcntlInt
" || $func eq "
FcntlFlock
")) {
# These fcntl arguments need to be uintptr to be able to call FcntlInt and FcntlFlock
push @argscommun, "
uintptr
(
$
name
)
";
push @argscall, "
$
name
uintptr
";
push @argsgc, "
$
name
";
push @argsgccgo, "
C
.
uintptr_t
(
$
name
)
";
} else {
push @argscommun, "
$
name
";
push @argscall, "
$
name
int
";
push @argsgc, "
uintptr
(
$
name
)
";
push @argsgccgo, "
C
.
int
(
$
name
)
";
}
} elsif($type eq "
int32
") {
push @argscommun, "
$
name
";
push @argscall, "
$
name
int32
";
push @argsgc, "
uintptr
(
$
name
)
";
push @argsgccgo, "
C
.
int
(
$
name
)
";
} elsif($type eq "
int64
") {
push @argscommun, "
$
name
";
push @argscall, "
$
name
int64
";
push @argsgc, "
uintptr
(
$
name
)
";
push @argsgccgo, "
C
.
longlong
(
$
name
)
";
} elsif($type eq "
uint32
") {
push @argscommun, "
$
name
";
push @argscall, "
$
name
uint32
";
push @argsgc, "
uintptr
(
$
name
)
";
push @argsgccgo, "
C
.
uint
(
$
name
)
";
} elsif($type eq "
uint64
") {
push @argscommun, "
$
name
";
push @argscall, "
$
name
uint64
";
push @argsgc, "
uintptr
(
$
name
)
";
push @argsgccgo, "
C
.
ulonglong
(
$
name
)
";
} elsif($type eq "
uintptr
") {
push @argscommun, "
$
name
";
push @argscall, "
$
name
uintptr
";
push @argsgc, "
$
name
";
push @argsgccgo, "
C
.
uintptr_t
(
$
name
)
";
} else {
push @argscommun, "
int
(
$
name
)
";
push @argscall, "
$
name
int
";
push @argsgc, "
uintptr
(
$
name
)
";
push @argsgccgo, "
C
.
int
(
$
name
)
";
}
$arg_n++;
}
my $nargs = @argsgc;
# COMMUN function generation
my $argscommun = join(', ', @argscommun);
my $callcommun = "
call$sysname
(
$
argscommun
)
";
my @ret = ("
_
", "
_
");
my $body = "";
my $do_errno = 0;
for(my $i=0; $i<@out; $i++) {
my $p = $out[$i];
my ($name, $type) = parseparam($p);
my $reg = "";
if($name eq "
err
") {
$reg = "
e1
";
$ret[1] = $reg;
$do_errno = 1;
} else {
$reg = "
r0
";
$ret[0] = $reg;
}
if($type eq "
bool
") {
$reg = "
$
reg
!
=
0
";
}
if($reg ne "
e1
") {
$body .= "
\t$name
=
$
type
(
$
reg
)
\n
";
}
}
if ($ret[0] eq "
_
" && $ret[1] eq "
_
") {
$textcommon .= "
\t$callcommun\n
";
} else {
$textcommon .= "
\t$ret
[
0
],
$
ret
[
1
]
:=
$
callcommun\n
";
}
$textcommon .= $body;
if ($do_errno) {
$textcommon .= "
\tif
e1
!
=
0
{
\n
";
$textcommon .= "
\t\terr
=
errnoErr
(
e1
)
\n
";
$textcommon .= "
\t
}
\n
";
}
$textcommon .= "
\treturn\n
";
$textcommon .= "
}
\n
";
if ($onlyCommon){
next
}
# CALL Prototype
my $callProto = sprintf "
func
call
%s(%s) (r1 uintptr, e1 Errno) {\n", $sysname, join(', ', @argscall);
#
GC
function
generation
my
$
asm
=
"syscall6"
;
if
(
$
nonblock
)
{
$
asm
=
"rawSyscall6"
;
}
if
(
@argsgc
<=
6
)
{
while
(
@argsgc
<
6
)
{
push
@argsgc
,
"0"
;
}
}
else
{
print
STDERR
"$ARGV:$.: too many arguments to system call\n"
;
}
my
$
argsgc
=
join
(
', '
,
@argsgc
);
my
$
callgc
=
"$asm(uintptr(unsafe.Pointer(&$sysvarname)), $nargs, $argsgc)"
;
$
textgc
.
=
$
callProto
;
$
textgc
.
=
"\tr1, _, e1 = $callgc\n"
;
$
textgc
.
=
"\treturn\n}\n"
;
#
GCCGO
function
generation
my
$
argsgccgo
=
join
(
', '
,
@argsgccgo
);
my
$
callgccgo
=
"C.$sysname($argsgccgo)"
;
$
textgccgo
.
=
$
callProto
;
$
textgccgo
.
=
"\tr1 = uintptr($callgccgo)\n"
;
$
textgccgo
.
=
"\te1 = syscall.GetErrno()\n"
;
$
textgccgo
.
=
"\treturn\n}\n"
;
}
if
(
$
errors
)
{
exit
1
;
}
# Print zsyscall_aix_ppc64.go
open
(
my
$
fcommun
,
'>'
,
'zsyscall_aix_ppc64.go'
);
my
$
tofcommun
=
<<
EOF
;
//
$
cmdline
//
Code
generated
by
the
command
above
;
see
README
.
md
.
DO
NOT
EDIT
.
//
+
build
$
tags
package
$
package
import
(
"unsafe"
)
EOF
$
tofcommun
.
=
"import \"golang.org/x/sys/unix\"\n"
if
$
package
ne
"unix"
;
$
tofcommun
.
=<<
EOF
;
$
textcommon
EOF
print
$
fcommun
$
tofcommun
;
# Print zsyscall_aix_ppc64_gc.go
open
(
my
$
fgc
,
'>'
,
'zsyscall_aix_ppc64_gc.go'
);
my
$
tofgc
=
<<
EOF
;
//
$
cmdline
//
Code
generated
by
the
command
above
;
see
README
.
md
.
DO
NOT
EDIT
.
//
+
build
$
tags
//
+
build
!
gccgo
package
$
package
import
(
"unsafe"
)
EOF
$
tofgc
.
=
"import \"golang.org/x/sys/unix\"\n"
if
$
package
ne
"unix"
;
my
$
vardecls
=
"\t"
.
join
(
",\n\t"
,
@vars
);
$
vardecls
.
=
" syscallFunc"
;
$
tofgc
.
=<<
EOF
;
$
dynimports
$
linknames
type
syscallFunc
uintptr
var
(
$
vardecls
)
//
Implemented
in
runtime
/
syscall_aix
.
go
.
func
rawSyscall6
(
trap
,
nargs
,
a1
,
a2
,
a3
,
a4
,
a5
,
a6
uintptr
)
(
r1
,
r2
uintptr
,
err
Errno
)
func
syscall6
(
trap
,
nargs
,
a1
,
a2
,
a3
,
a4
,
a5
,
a6
uintptr
)
(
r1
,
r2
uintptr
,
err
Errno
)
$
textgc
EOF
print
$
fgc
$
tofgc
;
# Print zsyscall_aix_ppc64_gc.go
open
(
my
$
fgccgo
,
'>'
,
'zsyscall_aix_ppc64_gccgo.go'
);
my
$
tofgccgo
=
<<
EOF
;
//
$
cmdline
//
Code
generated
by
the
command
above
;
see
README
.
md
.
DO
NOT
EDIT
.
//
+
build
$
tags
//
+
build
gccgo
package
$
package
$
c_extern
*/
import
"C"
import
(
"syscall"
)
EOF
$
tofgccgo
.
=
"import \"golang.org/x/sys/unix\"\n"
if
$
package
ne
"unix"
;
$
tofgccgo
.
=<<
EOF
;
$
textgccgo
EOF
print
$
fgccgo
$
tofgccgo
;
exit
0
;
File Metadata
Details
Attached
Mime Type
text/x-perl
Expires
Wed, Jan 28, 9:04 PM (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3608948
Attached To
rWCLI writeas-cli
Event Timeline
Log In to Comment