From edcc89a752232f817712cbeb30610da76304edbd Mon Sep 17 00:00:00 2001 From: Sangbum Kim Date: Sun, 21 Jan 2024 20:40:19 +0900 Subject: [PATCH] =?UTF-8?q?rust=20=EB=B2=84=EC=A0=84=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 6 +- .gitignore | 210 ++++++++++++++++++++++++- Cargo.toml | 27 ++++ Dockerfile | 36 +++-- Dockerfile.debian | 38 +++++ c/.dockerignore | 2 + c/.gitignore | 2 + c/Dockerfile | 16 ++ CMakeLists.txt => c/src/CMakeLists.txt | 0 LICENSE => c/src/LICENSE | 0 README.md => c/src/README.md | 0 config.h.in => c/src/config.h.in | 0 lgtm.yml => c/src/lgtm.yml | 0 setcap.c => c/src/setcap.c | 0 setcap-static | Bin 0 -> 62312 bytes src/args.rs | 133 ++++++++++++++++ src/caps.rs | 129 +++++++++++++++ src/link.rs | 17 ++ src/link_gcc.rs | 26 +++ src/main.rs | 104 ++++++++++++ 20 files changed, 732 insertions(+), 14 deletions(-) create mode 100644 Cargo.toml create mode 100644 Dockerfile.debian create mode 100644 c/.dockerignore create mode 100644 c/.gitignore create mode 100644 c/Dockerfile rename CMakeLists.txt => c/src/CMakeLists.txt (100%) rename LICENSE => c/src/LICENSE (100%) rename README.md => c/src/README.md (100%) rename config.h.in => c/src/config.h.in (100%) rename lgtm.yml => c/src/lgtm.yml (100%) rename setcap.c => c/src/setcap.c (100%) create mode 100755 setcap-static create mode 100644 src/args.rs create mode 100644 src/caps.rs create mode 100644 src/link.rs create mode 100644 src/link_gcc.rs create mode 100644 src/main.rs diff --git a/.dockerignore b/.dockerignore index ce1b641..fc71d7d 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,4 @@ * -!CMakeLists.txt -!setcap.c -!config.h.in +!src +!Cargo.toml +!Cargo.lock \ No newline at end of file diff --git a/.gitignore b/.gitignore index 00a6291..b6a8ccb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,208 @@ -/.vscode -/build +# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,intellij+all,windows,linux,macos,rust,rust-analyzer +# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,intellij+all,windows,linux,macos,rust,rust-analyzer + +### Intellij+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. + +.idea/* + +!.idea/codeStyles +!.idea/runConfigurations + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +### rust-analyzer ### +# Can be generated by other build systems other than cargo (ex: bazelbuild/rust_rules) +rust-project.json + + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,intellij+all,windows,linux,macos,rust,rust-analyzer + +/target \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..0144999 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "setcap-static" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libc = { version = "0.2", default-features = false } +libc-print = "0.1.22" + +[profile.dev] +# This isn't required for development builds, but makes development +# build behavior match release builds. To enable unwinding panics +# during development, simply remove this line. +panic = "abort" + +[profile.release] +#opt-level = 'z' # Optimize for size. +lto = true # Enable Link Time Optimization +codegen-units = 1 # Reduce number of codegen units to increase optimizations. +panic = 'abort' # Abort on panic +strip = true # Strip symbols from binary* +debug-assertions = false +debug = false +rpath = false +incremental = false diff --git a/Dockerfile b/Dockerfile index 0defbac..2976b95 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,29 @@ -FROM harbor.repository.lb.home.dc.internal.amuz.es/infrastructure/alpine-base:3.19-latest AS build -RUN apk add --no-cache cmake make musl-dev gcc libcap-static libcap-dev -WORKDIR /build -COPY . . -RUN \ - cmake -S . -B build -DCMAKE_BUILD_TYPE=MinSizeRel && \ - cmake --build build --config MinSizeRel && \ - strip build/setcap-static +#syntax=docker/dockerfile:1 + +## +## Build +## +FROM rust:1-alpine3.19 AS build +LABEL org.opencontainers.image.authors="Sangbum Kim " + +# set the workdir and copy the source into it +WORKDIR /app +COPY . /app + +ENV RUSTFLAGS='-C link-arg=-s -C link-arg=-fuse-ld=lld' + +RUN set -x && \ + apk add --no-cache \ + libcap-static \ + libcap-dev \ + lld \ + musl-dev &&\ + cargo build --release && \ + ldd target/release/setcap-static + +# RUN --mount=type=bind,rw,source=.,target=/host \ +# cp -avf target/release/setcap-static /host/setcap-static + FROM scratch -COPY --from=build /build/build/setcap-static /setcap-static +COPY --from=build /app/target/release/setcap-static /setcap-static diff --git a/Dockerfile.debian b/Dockerfile.debian new file mode 100644 index 0000000..e7903e7 --- /dev/null +++ b/Dockerfile.debian @@ -0,0 +1,38 @@ +#syntax=docker/dockerfile:1 + +## +## Build +## +FROM rust:1-slim AS build +LABEL org.opencontainers.image.authors="Sangbum Kim " + +# set the workdir and copy the source into it +WORKDIR /app +COPY . /app + +# ENV RUSTFLAGS='-C link-arg=-s -C linker=rust-lld -C link-arg=-fuse-ld=lld' +# ENV RUSTFLAGS='-C link-arg=-s -C link-arg=-fuse-ld=lld' +# ENV RUSTFLAGS='-C target-feature=+crt-static -C link-arg=-s -C link-args=-nostartfiles -C link-arg=-nostdlib' +# ENV RUSTFLAGS='-C target-feature=+crt-static -C link-arg=-s' +# ENV RUSTFLAGS='-C target-feature=+crt-static -C link-arg=-s' +# ENV RUSTFLAGS='-C target-feature=+crt-static -C link-arg=-static -C link-arg=-s' +# ENV RUSTFLAGS='-C target-feature=+crt-static -C link-arg=-static -C link-arg=-s -C link-arg=-fuse-ld=lld' +ENV RUSTFLAGS='-C target-feature=+crt-static -C link-arg=-s -C link-arg=-fuse-ld=lld' + +# do a release build +RUN set -x && \ + apt update && \ + apt install -y \ + # libcap2 \ + lld \ + libcap-dev \ + &&\ + cargo build --release && \ + ldd target/release/rsetcap + +RUN --mount=type=bind,rw,source=.,target=/host \ + cp -avf target/release/rsetcap /host/rsetcap + + +# FROM scratch +# COPY --from=build /build/build/setcap-static /setcap-static diff --git a/c/.dockerignore b/c/.dockerignore new file mode 100644 index 0000000..cfa9dff --- /dev/null +++ b/c/.dockerignore @@ -0,0 +1,2 @@ +* +!src \ No newline at end of file diff --git a/c/.gitignore b/c/.gitignore new file mode 100644 index 0000000..00a6291 --- /dev/null +++ b/c/.gitignore @@ -0,0 +1,2 @@ +/.vscode +/build diff --git a/c/Dockerfile b/c/Dockerfile new file mode 100644 index 0000000..79c3a98 --- /dev/null +++ b/c/Dockerfile @@ -0,0 +1,16 @@ +FROM harbor.repository.lb.home.dc.internal.amuz.es/infrastructure/alpine-base:3.19-latest AS build +RUN apk add --no-cache cmake make musl-dev gcc libcap-static libcap-dev +WORKDIR /build + +COPY src/ ./ + +RUN \ + cmake -S . -B build -DCMAKE_BUILD_TYPE=MinSizeRel && \ + cmake --build build --config MinSizeRel && \ + strip build/setcap-static + +RUN --mount=type=bind,rw,source=.,target=/host \ + cp -avf build/setcap-static /host/setcap-static + +FROM scratch +COPY --from=build /build/build/setcap-static /setcap-static diff --git a/CMakeLists.txt b/c/src/CMakeLists.txt similarity index 100% rename from CMakeLists.txt rename to c/src/CMakeLists.txt diff --git a/LICENSE b/c/src/LICENSE similarity index 100% rename from LICENSE rename to c/src/LICENSE diff --git a/README.md b/c/src/README.md similarity index 100% rename from README.md rename to c/src/README.md diff --git a/config.h.in b/c/src/config.h.in similarity index 100% rename from config.h.in rename to c/src/config.h.in diff --git a/lgtm.yml b/c/src/lgtm.yml similarity index 100% rename from lgtm.yml rename to c/src/lgtm.yml diff --git a/setcap.c b/c/src/setcap.c similarity index 100% rename from setcap.c rename to c/src/setcap.c diff --git a/setcap-static b/setcap-static new file mode 100755 index 0000000000000000000000000000000000000000..d1c7ddf229491654b16b574f02543d7a3fc0262c GIT binary patch literal 62312 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 Result { + assert!(argc > 0); + assert!(!argv.is_null()); + + let this = Args { + argc: argc as usize, + argv, + }; + + let args = this.as_slice(); + for idx in 0..this.argc { + let arg = *args.get_unchecked(idx); + if let Err(error) = c_str_to_rust(arg) { + return Err((idx, error)); + } + } + + Ok(this) + } + + #[inline(always)] + ///Unchecked version of `Args::new` + /// + ///Do it on your own risk + pub unsafe fn new_unchecked(argc: isize, argv: *const *const u8) -> Self { + Args { + argc: argc as usize, + argv, + } + } + + #[inline(always)] + ///Returns slice of raw C strings + pub fn as_slice(&self) -> &[*const u8] { + unsafe { core::slice::from_raw_parts(self.argv, self.argc) } + } + + #[inline(always)] + ///Retrieves string by index. + /// + ///No checks, 100% unsafe. + pub unsafe fn get_str_by_index(&self, index: usize) -> &str { + let elem = *self.as_slice().get_unchecked(index); + c_str_to_rust_unchecked(elem) + } + + pub unsafe fn get_cstr_by_index(&self, index: usize) -> &CStr { + let elem = *self.as_slice().get_unchecked(index); + CStr::from_ptr(elem as *const c_char) + } +} + +impl<'a> IntoIterator for &'a Args { + type Item = &'a str; + type IntoIter = IntoIter<'a>; + + #[inline(always)] + fn into_iter(self) -> Self::IntoIter { + Self::IntoIter { + inner: self, + index: 0, + } + } +} + +///Iterator over [Args](struct.Args.html) +/// +///Comparing to normal iterators can be iterated back and forth. +pub struct IntoIter<'a> { + inner: &'a Args, + index: usize, +} + +impl<'a> Iterator for IntoIter<'a> { + type Item = &'a str; + + fn next(&mut self) -> Option { + if self.index >= self.inner.argc { + return None; + } + + let elem = unsafe { self.inner.get_str_by_index(self.index) }; + self.index += 1; + Some(elem) + } + + #[inline(always)] + fn size_hint(&self) -> (usize, Option) { + let count = self.inner.argc - self.index; + (count, Some(count)) + } + + #[inline(always)] + fn count(self) -> usize { + self.inner.argc - self.index + } +} + +///Converts C string to Rust's, verifying it is UTF-8 +/// +///It is UB to pass non-C string as it requires \0 +unsafe fn c_str_to_rust(ptr: *const u8) -> Result<&'static str, core::str::Utf8Error> { + let len = libc::strlen(ptr as *const i8); + let parts = core::slice::from_raw_parts(ptr, len); + core::str::from_utf8(parts) +} + +///Converts C string to Rust's one assuming it is UTF-8 +/// +///It is UB to pass non-C string as it requires \0 +unsafe fn c_str_to_rust_unchecked(ptr: *const u8) -> &'static str { + let len = libc::strlen(ptr as *const i8); + let parts = core::slice::from_raw_parts(ptr, len); + core::str::from_utf8_unchecked(parts) +} diff --git a/src/caps.rs b/src/caps.rs new file mode 100644 index 0000000..1702bd1 --- /dev/null +++ b/src/caps.rs @@ -0,0 +1,129 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +use libc::{c_char, c_int, c_uint, c_void, uid_t}; + +#[repr(i32)] +pub(crate) enum Value { + CAP_CHOWN, + CAP_DAC_OVERRIDE, + CAP_DAC_READ_SEARCH, + CAP_FOWNER, + CAP_FSETID, + CAP_KILL, + CAP_SETGID, + CAP_SETUID, + CAP_SETPCAP, + CAP_LINUX_IMMUTABLE, + CAP_NET_BIND_SERVICE, + CAP_NET_BROADCAST, + CAP_NET_ADMIN, + CAP_NET_RAW, + CAP_IPC_LOCK, + CAP_IPC_OWNER, + CAP_SYS_MODULE, + CAP_SYS_RAWIO, + CAP_SYS_CHROOT, + CAP_SYS_PTRACE, + CAP_SYS_PACCT, + CAP_SYS_ADMIN, + CAP_SYS_BOOT, + CAP_SYS_NICE, + CAP_SYS_RESOURCE, + CAP_SYS_TIME, + CAP_SYS_TTY_CONFIG, + CAP_MKNOD, + CAP_LEASE, + CAP_AUDIT_WRITE, + CAP_AUDIT_CONTROL, + CAP_SETFCAP, + CAP_MAC_OVERRIDE, + CAP_MAC_ADMIN, + CAP_SYSLOG, + CAP_WAKE_ALARM, + CAP_BLOCK_SUSPEND, + CAP_AUDIT_READ, + CAP_PERFMON, + CAP_BPF, + CAP_CHECKPOINT_RESTORE, +} + +impl Into for Value { + fn into(self) -> cap_value_t { + self as cap_value_t + } +} + +#[repr(u32)] +pub(crate) enum Flag { + CAP_EFFECTIVE, + CAP_PERMITTED, + CAP_INHERITABLE, +} + +impl Into for Flag { + fn into(self) -> cap_flag_t { + self as cap_flag_t + } +} + +#[repr(u32)] +pub(crate) enum FlagValue { + CAP_CLEAR, + CAP_SET, +} + +impl Into for FlagValue { + fn into(self) -> cap_flag_value_t { + self as cap_flag_value_t + } +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub(crate) struct cap_t { + pub __user_cap_header_struct: cap_user_header_t, + pub __user_cap_data_struct: [cap_user_data_t; 2usize], +} + +#[repr(C)] +#[derive(Copy, Clone)] + +pub(crate) struct cap_user_header_t { + pub version: u32, + pub pid: ::libc::c_int, +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub(crate) struct cap_user_data_t { + pub effective: c_uint, + pub permitted: c_uint, + pub inheritable: c_uint, +} + +pub(crate) type cap_flag_t = c_uint; +pub(crate) type cap_value_t = c_int; +pub(crate) type cap_flag_value_t = c_uint; + +// #[link(name = "cap", kind = "dylib")] +#[link(name = "cap", kind = "static")] +extern "C" { + pub(crate) fn cap_get_proc() -> *mut cap_t; + pub(crate) fn cap_from_text(arg1: *const c_char) -> *mut cap_t; + pub(crate) fn cap_set_nsowner(target_caps: *mut cap_t, uid: uid_t) -> c_int; + pub(crate) fn cap_set_flag( + arg1: *mut cap_t, + arg2: cap_flag_t, + arg3: ::libc::c_int, + arg4: *const cap_value_t, + arg5: cap_flag_value_t, + ) -> ::libc::c_int; + pub(crate) fn cap_set_proc(arg1: *const cap_t) -> c_int; + pub(crate) fn cap_set_file(arg1: *const c_char, arg2: *const cap_t) -> c_int; + pub(crate) fn cap_free(arg1: *mut c_void) -> c_int; +} diff --git a/src/link.rs b/src/link.rs new file mode 100644 index 0000000..864bcd5 --- /dev/null +++ b/src/link.rs @@ -0,0 +1,17 @@ +#![allow(non_camel_case_types, dead_code)] + +use libc::FILE; + +#[link(name = "c")] +extern "C" { + static stdin: *mut FILE; + static stdout: *mut FILE; + static stderr: *mut FILE; +} + +#[panic_handler] +#[inline(never)] +#[cfg(not(test))] +unsafe fn panic_handler(_: &core::panic::PanicInfo) -> ! { + libc::exit(2) +} diff --git a/src/link_gcc.rs b/src/link_gcc.rs new file mode 100644 index 0000000..ab7fa5b --- /dev/null +++ b/src/link_gcc.rs @@ -0,0 +1,26 @@ +#![allow(non_camel_case_types, dead_code)] + +use libc::{c_int, exit, FILE}; + +#[repr(C, align(16))] +struct f128 { + a: [u8; 16], +} + +#[no_mangle] +extern "C" fn __letf2(_a: f128, _b: f128) -> c_int { + 0 +} + +#[no_mangle] +extern "C" fn __unordtf2(_a: f128, _b: f128) -> c_int { + 0 +} + +#[no_mangle] +unsafe extern "C" fn _Unwind_Resume() -> ! { + exit(2) +} + +#[no_mangle] +extern "C" fn __gcc_personality_v0() {} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..ca96d08 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,104 @@ +#![no_std] +#![no_main] + +mod args; +mod caps; +mod link; + +use args::Args; +use libc::{c_int, c_void, unlink, EXIT_FAILURE, EXIT_SUCCESS}; +use libc_print::std_name::eprintln; + +#[no_mangle] +unsafe extern "C" fn main(argc: c_int, argv: *const *const u8) -> c_int { + match argc { + 3 => (), + 1 => return EXIT_SUCCESS, + _ => { + eprintln!("Usage: setcap capabilities filename"); + return EXIT_FAILURE; + } + } + + let arg = match Args::new(argc as isize, argv) { + Ok(arg) => arg, + Err((valid_up_to, err)) => { + match err.error_len() { + Some(err_len) => eprintln!( + "invalid utf-8 sequence of {} bytes from index {}", + err_len, valid_up_to + ), + None => eprintln!("incomplete utf-8 byte sequence from index {}", valid_up_to), + }; + return EXIT_FAILURE; + } + }; + + let my_caps = caps::cap_get_proc(); + + if my_caps.is_null() { + eprintln!("cap_get_proc"); + return EXIT_FAILURE; + } + + let target_caps = caps::cap_from_text(arg.get_cstr_by_index(1).as_ptr()); + + if target_caps.is_null() { + eprintln!( + "cap_from_text: failed to parse \"{}\"", + arg.get_str_by_index(1), + ); + caps::cap_free(my_caps as *mut c_void); + return EXIT_FAILURE; + } + + let ret = caps::cap_set_nsowner(target_caps, 0); + if ret != 0 { + eprintln!("cap_set_nsowner: {}", ret); + caps::cap_free(my_caps as *mut c_void); + caps::cap_free(target_caps as *mut c_void); + return EXIT_FAILURE; + } + + let flag: caps::cap_value_t = caps::Value::CAP_SETFCAP.into(); + let ret = caps::cap_set_flag( + my_caps, + caps::Flag::CAP_EFFECTIVE.into(), + 1, + &flag, + caps::FlagValue::CAP_SET.into(), + ); + + if ret != 0 { + eprintln!("cap_set_flag(CAP_SETFCAP): {}", ret); + caps::cap_free(my_caps as *mut c_void); + caps::cap_free(target_caps as *mut c_void); + return EXIT_FAILURE; + } + + let ret = caps::cap_set_proc(my_caps); + if ret != 0 { + eprintln!("cap_set_proc: {}", ret); + caps::cap_free(my_caps as *mut c_void); + caps::cap_free(target_caps as *mut c_void); + return EXIT_FAILURE; + } + + caps::cap_free(my_caps as *mut c_void); + + let ret = caps::cap_set_file(arg.get_cstr_by_index(2).as_ptr(), target_caps); + if ret != 0 { + eprintln!("cap_set_file: {}", ret); + caps::cap_free(target_caps as *mut c_void); + return EXIT_FAILURE; + } + + caps::cap_free(target_caps as *mut c_void); + + _ = match arg.get_cstr_by_index(0).to_bytes() { + [b'/', b'!', ..] => unlink(arg.get_cstr_by_index(0).as_ptr()), + _ => 0, + }; + + EXIT_SUCCESS +}