1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16use hilog_rust::{error, hilog, HiLogLabel, LogType};
17use libc::{__errno_location, c_char, c_uint, c_void};
18use std::ffi::{CStr, CString};
19use std::fs::File;
20use std::io::{BufRead, BufReader, Error, ErrorKind, Seek, SeekFrom};
21#[cfg(unix)]
22use std::os::unix::io::{FromRawFd, RawFd};
23use std::path::PathBuf;
24use std::ptr::null_mut;
25use std::{fs, mem};
26
27const LOG_LABEL: HiLogLabel = HiLogLabel {
28    log_type: LogType::LogCore,
29    domain: 0xD004388,
30    tag: "file_api",
31};
32
33/// Enumeration of `lseek` interface to seek within a file.
34#[repr(C)]
35#[allow(dead_code)]
36pub enum SeekPos {
37    Start,
38    Current,
39    End,
40}
41
42/// Enumeration of `mkdirs` interface to choose ways to create the direction.
43#[repr(C)]
44#[allow(dead_code)]
45pub enum MakeDirectionMode {
46    Single,
47    Multiple,
48}
49
50/// Structure for storing string and its effective length.
51#[repr(C)]
52pub struct Str {
53    /// C string.
54    pub str: *mut c_char,
55    /// The length of string.
56    pub len: c_uint,
57}
58
59pub(crate) unsafe fn error_control(err: Error) {
60    let errno_pos = __errno_location();
61    if let Some(raw) = err.raw_os_error() {
62        *errno_pos = raw;
63    } else {
64        match err.kind() {
65            ErrorKind::NotFound => *errno_pos = 2,
66            ErrorKind::PermissionDenied => *errno_pos = 13,
67            ErrorKind::AlreadyExists => *errno_pos = 17,
68            ErrorKind::InvalidInput => *errno_pos = 22,
69            ErrorKind::InvalidData => *errno_pos = 61,
70            _ => {
71                *errno_pos = 13900042;
72                error!(LOG_LABEL, "Unknown error is : {}", @public(err));
73            }
74        }
75    }
76}
77
78pub(crate) unsafe fn reader_iterator(path: *const c_char) -> Result<*mut c_void, Error> {
79    if path.is_null() {
80        return Err(Error::new(ErrorKind::InvalidInput, "Invalid input"));
81    }
82    let path = CStr::from_ptr(path);
83    let path = match path.to_str() {
84        Ok(p) => p,
85        Err(_) => {
86            return Err(Error::new(ErrorKind::InvalidInput, "Invalid input"));
87        }
88    };
89    let file = File::open(path)?;
90    let reader = BufReader::new(file);
91    Ok(Box::into_raw(Box::new(reader)) as *mut c_void)
92}
93
94pub(crate) unsafe fn drop_reader_iterator(iter: *mut c_void) {
95    if iter.is_null() {
96        return;
97    }
98    let reader = Box::from_raw(iter as *mut BufReader<File>);
99    drop(reader);
100}
101
102pub(crate) unsafe fn next_line(iter: *mut c_void) -> Result<*mut Str, Error> {
103    if iter.is_null() {
104        return Err(Error::new(ErrorKind::InvalidInput, "Invalid input"));
105    }
106    let reader = &mut *(iter as *mut BufReader<File>);
107    let mut line = String::new();
108    let len = reader.read_line(&mut line)? as c_uint;
109    if len > 0 {
110        let line_bytes = line.into_bytes();
111        let line = CString::from_vec_unchecked(line_bytes);
112        let item = Str {
113            str: line.into_raw(),
114            len,
115        };
116        Ok(Box::into_raw(Box::new(item)))
117    } else {
118        Ok(null_mut())
119    }
120}
121
122pub(crate) fn seek(fd: i32, offset: i64, pos: SeekPos) -> Result<u64, Error> {
123    let mut file = unsafe { File::from_raw_fd(fd as RawFd) };
124
125    let new_pos = match pos {
126        SeekPos::Start => file.seek(SeekFrom::Start(offset as u64)),
127        SeekPos::Current => file.seek(SeekFrom::Current(offset)),
128        SeekPos::End => file.seek(SeekFrom::End(offset)),
129    };
130
131    mem::forget(file);
132    new_pos
133}
134
135pub(crate) fn create_dir(path: *const c_char, mode: MakeDirectionMode) -> Result<(), Error> {
136    if path.is_null() {
137        return Err(Error::new(ErrorKind::InvalidInput, "Invalid input"));
138    }
139    let path = unsafe { CStr::from_ptr(path) };
140    let path = match path.to_str() {
141        Ok(p) => p,
142        Err(_) => {
143            return Err(Error::new(ErrorKind::InvalidInput, "Invalid input"));
144        }
145    };
146    match mode {
147        MakeDirectionMode::Single => fs::create_dir(path),
148        MakeDirectionMode::Multiple => fs::create_dir_all(path),
149    }
150}
151
152pub(crate) fn get_parent(fd: i32) -> Result<*mut Str, Error> {
153    let mut p = PathBuf::from("/proc/self/fd");
154    p.push(&fd.to_string());
155    let path = fs::read_link(&p)?;
156    match path.as_path().parent() {
157        None => {}
158        Some(parent) => {
159            if let Some(str) = parent.to_str() {
160                // When the return value of `Path::parent()` is `Some(s)`, `s` will not be empty
161                // string.
162                let par_path = CString::new(str).unwrap();
163                let len = par_path.as_bytes().len() as c_uint;
164                let item = Str {
165                    str: par_path.into_raw(),
166                    len,
167                };
168                return Ok(Box::into_raw(Box::new(item)));
169            }
170        }
171    }
172    Ok(null_mut())
173}
174
175pub(crate) unsafe fn cut_file_name(path: *const c_char, size: usize) -> *mut Str {
176    let path_str = match CStr::from_ptr(path).to_str() {
177        Ok(s) => s,
178        Err(_) => return std::ptr::null_mut(),
179    };
180    let len = path_str.chars().count();
181    let size = size.min(len);
182
183    let mut sliced_str = String::from(path_str);
184    for _ in 0..size {
185        let _ = sliced_str.pop();
186    }
187
188    let result = match CString::new(sliced_str.clone()) {
189        Ok(s) => Str {
190            str: s.into_raw(),
191            len: sliced_str.as_bytes().len() as c_uint,
192        },
193        Err(_) => return std::ptr::null_mut(),
194    };
195    Box::into_raw(Box::new(result))
196}
197