Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
L
lxc
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Chen Yisong
lxc
Commits
5384e99d
Commit
5384e99d
authored
Jan 26, 2018
by
Tycho Andersen
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
rename am_unpriv to am_host_unpriv
Signed-off-by:
Tycho Andersen
<
tycho@tycho.ws
>
parent
4692c01a
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
29 additions
and
29 deletions
+29
-29
lxccontainer.c
src/lxc/lxccontainer.c
+9
-9
network.c
src/lxc/network.c
+2
-2
start.c
src/lxc/start.c
+2
-2
aufs.c
src/lxc/storage/aufs.c
+4
-4
btrfs.c
src/lxc/storage/btrfs.c
+2
-2
overlay.c
src/lxc/storage/overlay.c
+6
-6
storage.c
src/lxc/storage/storage.c
+3
-3
utils.h
src/lxc/utils.h
+1
-1
No files found.
src/lxc/lxccontainer.c
View file @
5384e99d
...
@@ -2678,7 +2678,7 @@ static bool has_snapshots(struct lxc_container *c)
...
@@ -2678,7 +2678,7 @@ static bool has_snapshots(struct lxc_container *c)
static
bool
do_destroy_container
(
struct
lxc_conf
*
conf
)
{
static
bool
do_destroy_container
(
struct
lxc_conf
*
conf
)
{
int
ret
;
int
ret
;
if
(
am_unpriv
())
{
if
(
am_
host_
unpriv
())
{
ret
=
userns_exec_full
(
conf
,
storage_destroy_wrapper
,
conf
,
ret
=
userns_exec_full
(
conf
,
storage_destroy_wrapper
,
conf
,
"storage_destroy_wrapper"
);
"storage_destroy_wrapper"
);
if
(
ret
<
0
)
if
(
ret
<
0
)
...
@@ -2800,7 +2800,7 @@ static bool container_destroy(struct lxc_container *c,
...
@@ -2800,7 +2800,7 @@ static bool container_destroy(struct lxc_container *c,
if
(
ret
<
0
||
(
size_t
)
ret
>=
len
)
if
(
ret
<
0
||
(
size_t
)
ret
>=
len
)
goto
out
;
goto
out
;
if
(
am_unpriv
())
if
(
am_
host_
unpriv
())
ret
=
userns_exec_1
(
conf
,
lxc_unlink_exec_wrapper
,
path
,
ret
=
userns_exec_1
(
conf
,
lxc_unlink_exec_wrapper
,
path
,
"lxc_unlink_exec_wrapper"
);
"lxc_unlink_exec_wrapper"
);
else
else
...
@@ -2819,7 +2819,7 @@ static bool container_destroy(struct lxc_container *c,
...
@@ -2819,7 +2819,7 @@ static bool container_destroy(struct lxc_container *c,
ret
=
snprintf
(
path
,
len
,
"%s/%s"
,
p1
,
c
->
name
);
ret
=
snprintf
(
path
,
len
,
"%s/%s"
,
p1
,
c
->
name
);
if
(
ret
<
0
||
(
size_t
)
ret
>=
len
)
if
(
ret
<
0
||
(
size_t
)
ret
>=
len
)
goto
out
;
goto
out
;
if
(
am_unpriv
())
if
(
am_
host_
unpriv
())
ret
=
userns_exec_full
(
conf
,
lxc_rmdir_onedev_wrapper
,
path
,
ret
=
userns_exec_full
(
conf
,
lxc_rmdir_onedev_wrapper
,
path
,
"lxc_rmdir_onedev_wrapper"
);
"lxc_rmdir_onedev_wrapper"
);
else
else
...
@@ -3602,7 +3602,7 @@ static struct lxc_container *do_lxcapi_clone(struct lxc_container *c, const char
...
@@ -3602,7 +3602,7 @@ static struct lxc_container *do_lxcapi_clone(struct lxc_container *c, const char
}
}
}
}
if
(
am_unpriv
())
{
if
(
am_
host_
unpriv
())
{
if
(
chown_mapped_root
(
newpath
,
c
->
lxc_conf
)
<
0
)
{
if
(
chown_mapped_root
(
newpath
,
c
->
lxc_conf
)
<
0
)
{
ERROR
(
"Error chowning %s to container root"
,
newpath
);
ERROR
(
"Error chowning %s to container root"
,
newpath
);
goto
out
;
goto
out
;
...
@@ -3680,7 +3680,7 @@ static struct lxc_container *do_lxcapi_clone(struct lxc_container *c, const char
...
@@ -3680,7 +3680,7 @@ static struct lxc_container *do_lxcapi_clone(struct lxc_container *c, const char
data
.
c1
=
c2
;
data
.
c1
=
c2
;
data
.
flags
=
flags
;
data
.
flags
=
flags
;
data
.
hookargs
=
hookargs
;
data
.
hookargs
=
hookargs
;
if
(
am_unpriv
())
if
(
am_
host_
unpriv
())
ret
=
userns_exec_full
(
c
->
lxc_conf
,
clone_update_rootfs_wrapper
,
ret
=
userns_exec_full
(
c
->
lxc_conf
,
clone_update_rootfs_wrapper
,
&
data
,
"clone_update_rootfs_wrapper"
);
&
data
,
"clone_update_rootfs_wrapper"
);
else
else
...
@@ -4355,7 +4355,7 @@ static bool add_remove_device_node(struct lxc_container *c, const char *src_path
...
@@ -4355,7 +4355,7 @@ static bool add_remove_device_node(struct lxc_container *c, const char *src_path
static
bool
do_lxcapi_add_device_node
(
struct
lxc_container
*
c
,
const
char
*
src_path
,
const
char
*
dest_path
)
static
bool
do_lxcapi_add_device_node
(
struct
lxc_container
*
c
,
const
char
*
src_path
,
const
char
*
dest_path
)
{
{
if
(
am_unpriv
())
{
if
(
am_
host_
unpriv
())
{
ERROR
(
NOT_SUPPORTED_ERROR
,
__FUNCTION__
);
ERROR
(
NOT_SUPPORTED_ERROR
,
__FUNCTION__
);
return
false
;
return
false
;
}
}
...
@@ -4366,7 +4366,7 @@ WRAP_API_2(bool, lxcapi_add_device_node, const char *, const char *)
...
@@ -4366,7 +4366,7 @@ WRAP_API_2(bool, lxcapi_add_device_node, const char *, const char *)
static
bool
do_lxcapi_remove_device_node
(
struct
lxc_container
*
c
,
const
char
*
src_path
,
const
char
*
dest_path
)
static
bool
do_lxcapi_remove_device_node
(
struct
lxc_container
*
c
,
const
char
*
src_path
,
const
char
*
dest_path
)
{
{
if
(
am_unpriv
())
{
if
(
am_
host_
unpriv
())
{
ERROR
(
NOT_SUPPORTED_ERROR
,
__FUNCTION__
);
ERROR
(
NOT_SUPPORTED_ERROR
,
__FUNCTION__
);
return
false
;
return
false
;
}
}
...
@@ -4382,7 +4382,7 @@ static bool do_lxcapi_attach_interface(struct lxc_container *c,
...
@@ -4382,7 +4382,7 @@ static bool do_lxcapi_attach_interface(struct lxc_container *c,
pid_t
init_pid
;
pid_t
init_pid
;
int
ret
=
0
;
int
ret
=
0
;
if
(
am_unpriv
())
{
if
(
am_
host_
unpriv
())
{
ERROR
(
NOT_SUPPORTED_ERROR
,
__FUNCTION__
);
ERROR
(
NOT_SUPPORTED_ERROR
,
__FUNCTION__
);
return
false
;
return
false
;
}
}
...
@@ -4421,7 +4421,7 @@ static bool do_lxcapi_detach_interface(struct lxc_container *c,
...
@@ -4421,7 +4421,7 @@ static bool do_lxcapi_detach_interface(struct lxc_container *c,
int
ret
;
int
ret
;
pid_t
pid
,
pid_outside
;
pid_t
pid
,
pid_outside
;
if
(
am_unpriv
())
{
if
(
am_
host_
unpriv
())
{
ERROR
(
NOT_SUPPORTED_ERROR
,
__FUNCTION__
);
ERROR
(
NOT_SUPPORTED_ERROR
,
__FUNCTION__
);
return
false
;
return
false
;
}
}
...
...
src/lxc/network.c
View file @
5384e99d
...
@@ -2449,7 +2449,7 @@ int lxc_network_move_created_netdev_priv(const char *lxcpath, const char *lxcnam
...
@@ -2449,7 +2449,7 @@ int lxc_network_move_created_netdev_priv(const char *lxcpath, const char *lxcnam
char
ifname
[
IFNAMSIZ
];
char
ifname
[
IFNAMSIZ
];
struct
lxc_list
*
iterator
;
struct
lxc_list
*
iterator
;
if
(
am_unpriv
())
if
(
am_
host_
unpriv
())
return
0
;
return
0
;
lxc_list_for_each
(
iterator
,
network
)
{
lxc_list_for_each
(
iterator
,
network
)
{
...
@@ -2487,7 +2487,7 @@ int lxc_create_network_unpriv(const char *lxcpath, const char *lxcname,
...
@@ -2487,7 +2487,7 @@ int lxc_create_network_unpriv(const char *lxcpath, const char *lxcname,
{
{
struct
lxc_list
*
iterator
;
struct
lxc_list
*
iterator
;
if
(
!
am_unpriv
())
if
(
!
am_
host_
unpriv
())
return
0
;
return
0
;
lxc_list_for_each
(
iterator
,
network
)
{
lxc_list_for_each
(
iterator
,
network
)
{
...
...
src/lxc/start.c
View file @
5384e99d
...
@@ -648,11 +648,11 @@ struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf,
...
@@ -648,11 +648,11 @@ struct lxc_handler *lxc_init_handler(const char *name, struct lxc_conf *conf,
memset
(
handler
,
0
,
sizeof
(
*
handler
));
memset
(
handler
,
0
,
sizeof
(
*
handler
));
/* Note that am_unpriv() checks the effective uid. We probably don't
/* Note that am_
host_
unpriv() checks the effective uid. We probably don't
* care if we are real root only if we are running as root so this
* care if we are real root only if we are running as root so this
* should be fine.
* should be fine.
*/
*/
handler
->
am_root
=
!
am_unpriv
();
handler
->
am_root
=
!
am_
host_
unpriv
();
handler
->
data_sock
[
0
]
=
handler
->
data_sock
[
1
]
=
-
1
;
handler
->
data_sock
[
0
]
=
handler
->
data_sock
[
1
]
=
-
1
;
handler
->
conf
=
conf
;
handler
->
conf
=
conf
;
handler
->
lxcpath
=
lxcpath
;
handler
->
lxcpath
=
lxcpath
;
...
...
src/lxc/storage/aufs.c
View file @
5384e99d
...
@@ -89,7 +89,7 @@ int aufs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new,
...
@@ -89,7 +89,7 @@ int aufs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new,
if
(
mkdir_p
(
new
->
dest
,
0755
)
<
0
)
if
(
mkdir_p
(
new
->
dest
,
0755
)
<
0
)
return
-
1
;
return
-
1
;
if
(
am_unpriv
()
&&
chown_mapped_root
(
new
->
dest
,
conf
)
<
0
)
if
(
am_
host_
unpriv
()
&&
chown_mapped_root
(
new
->
dest
,
conf
)
<
0
)
WARN
(
"Failed to update ownership of %s"
,
new
->
dest
);
WARN
(
"Failed to update ownership of %s"
,
new
->
dest
);
if
(
strcmp
(
orig
->
type
,
"dir"
)
==
0
)
{
if
(
strcmp
(
orig
->
type
,
"dir"
)
==
0
)
{
...
@@ -116,7 +116,7 @@ int aufs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new,
...
@@ -116,7 +116,7 @@ int aufs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new,
free
(
delta
);
free
(
delta
);
return
-
1
;
return
-
1
;
}
}
if
(
am_unpriv
()
&&
chown_mapped_root
(
delta
,
conf
)
<
0
)
if
(
am_
host_
unpriv
()
&&
chown_mapped_root
(
delta
,
conf
)
<
0
)
WARN
(
"Failed to update ownership of %s"
,
delta
);
WARN
(
"Failed to update ownership of %s"
,
delta
);
// the src will be 'aufs:lowerdir:upperdir'
// the src will be 'aufs:lowerdir:upperdir'
...
@@ -157,13 +157,13 @@ int aufs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new,
...
@@ -157,13 +157,13 @@ int aufs_clonepaths(struct lxc_storage *orig, struct lxc_storage *new,
free
(
ndelta
);
free
(
ndelta
);
return
-
1
;
return
-
1
;
}
}
if
(
am_unpriv
()
&&
chown_mapped_root
(
ndelta
,
conf
)
<
0
)
if
(
am_
host_
unpriv
()
&&
chown_mapped_root
(
ndelta
,
conf
)
<
0
)
WARN
(
"Failed to update ownership of %s"
,
ndelta
);
WARN
(
"Failed to update ownership of %s"
,
ndelta
);
struct
rsync_data_char
rdata
;
struct
rsync_data_char
rdata
;
rdata
.
src
=
odelta
;
rdata
.
src
=
odelta
;
rdata
.
dest
=
ndelta
;
rdata
.
dest
=
ndelta
;
if
(
am_unpriv
())
if
(
am_
host_
unpriv
())
ret
=
userns_exec_full
(
conf
,
lxc_rsync_delta_wrapper
,
ret
=
userns_exec_full
(
conf
,
lxc_rsync_delta_wrapper
,
&
rdata
,
"lxc_rsync_delta_wrapper"
);
&
rdata
,
"lxc_rsync_delta_wrapper"
);
else
else
...
...
src/lxc/storage/btrfs.c
View file @
5384e99d
...
@@ -434,7 +434,7 @@ bool btrfs_create_clone(struct lxc_conf *conf, struct lxc_storage *orig,
...
@@ -434,7 +434,7 @@ bool btrfs_create_clone(struct lxc_conf *conf, struct lxc_storage *orig,
/* rsync the contents from source to target */
/* rsync the contents from source to target */
data
.
orig
=
orig
;
data
.
orig
=
orig
;
data
.
new
=
new
;
data
.
new
=
new
;
if
(
am_unpriv
())
{
if
(
am_
host_
unpriv
())
{
ret
=
userns_exec_full
(
conf
,
lxc_storage_rsync_exec_wrapper
,
ret
=
userns_exec_full
(
conf
,
lxc_storage_rsync_exec_wrapper
,
&
data
,
"lxc_storage_rsync_exec_wrapper"
);
&
data
,
"lxc_storage_rsync_exec_wrapper"
);
if
(
ret
<
0
)
{
if
(
ret
<
0
)
{
...
@@ -466,7 +466,7 @@ bool btrfs_create_snapshot(struct lxc_conf *conf, struct lxc_storage *orig,
...
@@ -466,7 +466,7 @@ bool btrfs_create_snapshot(struct lxc_conf *conf, struct lxc_storage *orig,
if
(
ret
<
0
&&
errno
!=
ENOENT
)
if
(
ret
<
0
&&
errno
!=
ENOENT
)
return
false
;
return
false
;
if
(
am_unpriv
())
{
if
(
am_
host_
unpriv
())
{
struct
rsync_data_char
args
;
struct
rsync_data_char
args
;
args
.
src
=
orig
->
src
;
args
.
src
=
orig
->
src
;
...
...
src/lxc/storage/overlay.c
View file @
5384e99d
...
@@ -73,7 +73,7 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char
...
@@ -73,7 +73,7 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char
return
-
1
;
return
-
1
;
}
}
if
(
am_unpriv
())
{
if
(
am_
host_
unpriv
())
{
ret
=
chown_mapped_root
(
new
->
dest
,
conf
);
ret
=
chown_mapped_root
(
new
->
dest
,
conf
);
if
(
ret
<
0
)
if
(
ret
<
0
)
WARN
(
"Failed to update ownership of %s"
,
new
->
dest
);
WARN
(
"Failed to update ownership of %s"
,
new
->
dest
);
...
@@ -120,7 +120,7 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char
...
@@ -120,7 +120,7 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char
return
-
1
;
return
-
1
;
}
}
if
(
am_unpriv
())
{
if
(
am_
host_
unpriv
())
{
ret
=
chown_mapped_root
(
delta
,
conf
);
ret
=
chown_mapped_root
(
delta
,
conf
);
if
(
ret
<
0
)
if
(
ret
<
0
)
WARN
(
"Failed to update ownership of %s"
,
delta
);
WARN
(
"Failed to update ownership of %s"
,
delta
);
...
@@ -153,7 +153,7 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char
...
@@ -153,7 +153,7 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char
return
-
1
;
return
-
1
;
}
}
if
(
am_unpriv
())
{
if
(
am_
host_
unpriv
())
{
ret
=
chown_mapped_root
(
work
,
conf
);
ret
=
chown_mapped_root
(
work
,
conf
);
if
(
ret
<
0
)
if
(
ret
<
0
)
WARN
(
"Failed to update ownership of %s"
,
work
);
WARN
(
"Failed to update ownership of %s"
,
work
);
...
@@ -224,7 +224,7 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char
...
@@ -224,7 +224,7 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char
return
-
1
;
return
-
1
;
}
}
if
(
am_unpriv
())
{
if
(
am_
host_
unpriv
())
{
ret
=
chown_mapped_root
(
ndelta
,
conf
);
ret
=
chown_mapped_root
(
ndelta
,
conf
);
if
(
ret
<
0
)
if
(
ret
<
0
)
WARN
(
"Failed to update ownership of %s"
,
WARN
(
"Failed to update ownership of %s"
,
...
@@ -265,7 +265,7 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char
...
@@ -265,7 +265,7 @@ int ovl_clonepaths(struct lxc_storage *orig, struct lxc_storage *new, const char
return
-
1
;
return
-
1
;
}
}
if
(
am_unpriv
())
{
if
(
am_
host_
unpriv
())
{
ret
=
chown_mapped_root
(
work
,
conf
);
ret
=
chown_mapped_root
(
work
,
conf
);
if
(
ret
<
0
)
if
(
ret
<
0
)
WARN
(
"Failed to update ownership of %s"
,
work
);
WARN
(
"Failed to update ownership of %s"
,
work
);
...
@@ -960,7 +960,7 @@ static int ovl_do_rsync(const char *src, const char *dest,
...
@@ -960,7 +960,7 @@ static int ovl_do_rsync(const char *src, const char *dest,
rdata
.
src
=
(
char
*
)
src
;
rdata
.
src
=
(
char
*
)
src
;
rdata
.
dest
=
(
char
*
)
dest
;
rdata
.
dest
=
(
char
*
)
dest
;
if
(
am_unpriv
())
if
(
am_
host_
unpriv
())
ret
=
userns_exec_full
(
conf
,
lxc_rsync_exec_wrapper
,
&
rdata
,
ret
=
userns_exec_full
(
conf
,
lxc_rsync_exec_wrapper
,
&
rdata
,
"lxc_rsync_exec_wrapper"
);
"lxc_rsync_exec_wrapper"
);
else
else
...
...
src/lxc/storage/storage.c
View file @
5384e99d
...
@@ -406,7 +406,7 @@ struct lxc_storage *storage_copy(struct lxc_container *c, const char *cname,
...
@@ -406,7 +406,7 @@ struct lxc_storage *storage_copy(struct lxc_container *c, const char *cname,
if
(
!
bdevtype
&&
!
keepbdevtype
&&
snap
&&
!
strcmp
(
orig
->
type
,
"dir"
))
if
(
!
bdevtype
&&
!
keepbdevtype
&&
snap
&&
!
strcmp
(
orig
->
type
,
"dir"
))
bdevtype
=
"overlay"
;
bdevtype
=
"overlay"
;
if
(
am_unpriv
()
&&
!
unpriv_snap_allowed
(
orig
,
bdevtype
,
snap
,
maybe_snap
))
{
if
(
am_
host_
unpriv
()
&&
!
unpriv_snap_allowed
(
orig
,
bdevtype
,
snap
,
maybe_snap
))
{
ERROR
(
"Unsupported snapshot type
\"
%s
\"
for unprivileged users"
,
ERROR
(
"Unsupported snapshot type
\"
%s
\"
for unprivileged users"
,
bdevtype
?
bdevtype
:
"(null)"
);
bdevtype
?
bdevtype
:
"(null)"
);
goto
on_error_put_orig
;
goto
on_error_put_orig
;
...
@@ -505,7 +505,7 @@ struct lxc_storage *storage_copy(struct lxc_container *c, const char *cname,
...
@@ -505,7 +505,7 @@ struct lxc_storage *storage_copy(struct lxc_container *c, const char *cname,
else
else
src_no_prefix
=
lxc_storage_get_path
(
new
->
src
,
new
->
type
);
src_no_prefix
=
lxc_storage_get_path
(
new
->
src
,
new
->
type
);
if
(
am_unpriv
())
{
if
(
am_
host_
unpriv
())
{
ret
=
chown_mapped_root
(
src_no_prefix
,
c
->
lxc_conf
);
ret
=
chown_mapped_root
(
src_no_prefix
,
c
->
lxc_conf
);
if
(
ret
<
0
)
if
(
ret
<
0
)
WARN
(
"Failed to chown
\"
%s
\"
"
,
new
->
src
);
WARN
(
"Failed to chown
\"
%s
\"
"
,
new
->
src
);
...
@@ -518,7 +518,7 @@ struct lxc_storage *storage_copy(struct lxc_container *c, const char *cname,
...
@@ -518,7 +518,7 @@ struct lxc_storage *storage_copy(struct lxc_container *c, const char *cname,
/* rsync the contents from source to target */
/* rsync the contents from source to target */
data
.
orig
=
orig
;
data
.
orig
=
orig
;
data
.
new
=
new
;
data
.
new
=
new
;
if
(
am_unpriv
())
if
(
am_
host_
unpriv
())
ret
=
userns_exec_full
(
c
->
lxc_conf
,
ret
=
userns_exec_full
(
c
->
lxc_conf
,
lxc_storage_rsync_exec_wrapper
,
&
data
,
lxc_storage_rsync_exec_wrapper
,
&
data
,
"lxc_storage_rsync_exec_wrapper"
);
"lxc_storage_rsync_exec_wrapper"
);
...
...
src/lxc/utils.h
View file @
5384e99d
...
@@ -427,7 +427,7 @@ extern int lxc_strmunmap(void *addr, size_t length);
...
@@ -427,7 +427,7 @@ extern int lxc_strmunmap(void *addr, size_t length);
/* initialize rand with urandom */
/* initialize rand with urandom */
extern
int
randseed
(
bool
);
extern
int
randseed
(
bool
);
inline
static
bool
am_unpriv
(
void
)
inline
static
bool
am_
host_
unpriv
(
void
)
{
{
FILE
*
f
;
FILE
*
f
;
uid_t
user
,
host
,
count
;
uid_t
user
,
host
,
count
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment