<commit_before>//! Tidy check to enforce various stylistic guidelines on the Rust codebase.
//!
//! Example checks are:
//!
//! * No lines over 100 characters.
//! * No tabs.
//! * No trailing whitespace.
//! * No CR characters.
//! * No `TODO` or `XXX` directives.
//! * No unexplained ` ```ignore ` or ` ```rust,ignore ` doc tests.
//!
//! A number of these checks can be opted-out of with various directives like
//! `// ignore-tidy-linelength`.

use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

const COLS: usize = 100;

const UNEXPLAINED_IGNORE_DOCTEST_INFO: &str = r#"unexplained "```ignore" doctest; try one:

* make the test actually pass, by adding necessary imports and declarations, or
* use "```text", if the code is not Rust code, or
* use "```compile_fail,Ennnn", if the code is expected to fail at compile time, or
* use "```should_panic", if the code is expected to fail at run time, or
* use "```no_run", if the code should type-check but not necessary linkable/runnable, or
* explain it like "```ignore (cannot-test-this-because-xxxx)", if the annotation cannot be avoided.

"#;

const LLVM_UNREACHABLE_INFO: &str = r"\
C++ code used llvm_unreachable, which triggers undefined behavior
when executed when assertions are disabled.
Use llvm::report_fatal_error for increased robustness.";

/// Parser states for `line_is_url`.
#[derive(PartialEq)]
#[allow(non_camel_case_types)]
enum LIUState {
    EXP_COMMENT_START,
    EXP_LINK_LABEL_OR_URL,
    EXP_URL,
    EXP_END,
}

/// Returns `true` if `line` appears to be a line comment containing an URL,
/// possibly with a Markdown link label in front, and nothing else.
/// The Markdown link label, if present, may not contain whitespace.
/// Lines of this form are allowed to be overlength, because Markdown
/// offers no way to split a line in the middle of a URL, and the lengths
/// of URLs to external references are beyond our control.
fn line_is_url(line: &str) -> bool {
    use self::LIUState::*;
    let mut state: LIUState = EXP_COMMENT_START;

    for tok in line.split_whitespace() {
        match (state, tok) {
            (EXP_COMMENT_START, "//") => state = EXP_LINK_LABEL_OR_URL,
            (EXP_COMMENT_START, "///") => state = EXP_LINK_LABEL_OR_URL,
            (EXP_COMMENT_START, "//!") => state = EXP_LINK_LABEL_OR_URL,

            (EXP_LINK_LABEL_OR_URL, w)
                if w.len() >= 4 && w.starts_with('[') && w.ends_with("]:")
                => state = EXP_URL,

            (EXP_LINK_LABEL_OR_URL, w)
                if w.starts_with("http://") || w.starts_with("https://")
                => state = EXP_END,

            (EXP_URL, w)
                if w.starts_with("http://") || w.starts_with("https://") || w.starts_with("../")
                => state = EXP_END,

            (_, _) => return false,
        }
    }

    state == EXP_END
}

/// Returns `true` if `line` is allowed to be longer than the normal limit.
/// Currently there is only one exception, for long URLs, but more
/// may be added in the future.
fn long_line_is_ok(line: &str) -> bool {
    if line_is_url(line) {
        return true;
    }

    false
}

pub fn check(path: &Path, bad: &mut bool) {
    let mut contents = String::new();
    super::walk(path, &mut super::filter_dirs, &mut |file| {
        let filename = file.file_name().unwrap().to_string_lossy();
        let extensions = [".rs", ".py", ".js", ".sh", ".c", ".cpp", ".h"];
        if extensions.iter().all(|e| !filename.ends_with(e)) ||
           filename.starts_with(".#") {
            return
        }

        contents.truncate(0);
        t!(t!(File::open(file), file).read_to_string(&mut contents));

        if contents.is_empty() {
            tidy_error!(bad, "{}: empty file", file.display());
        }

        let skip_cr = contents.contains("ignore-tidy-cr");
        let skip_tab = contents.contains("ignore-tidy-tab");
        let skip_length = contents.contains("ignore-tidy-linelength");
        let skip_end_whitespace = contents.contains("ignore-tidy-end-whitespace");
        let skip_copyright = contents.contains("ignore-tidy-copyright");
        let mut trailing_new_lines = 0;
        for (i, line) in contents.split('\n').enumerate() {
            let mut err = |msg: &str| {
                tidy_error!(bad, "{}:{}: {}", file.display(), i + 1, msg);
            };
            if !skip_length && line.chars().count() > COLS
                && !long_line_is_ok(line) {
                    err(&format!("line longer than {} chars", COLS));
            }
            if !skip_tab && line.contains('\t') {
                err("tab character");
            }
            if !skip_end_whitespace && (line.ends_with(' ') || line.ends_with('\t')) {
                err("trailing whitespace");
            }
            if !skip_cr && line.contains('\r') {
                err("CR character");
            }
            if filename != "style.rs" {
                if line.contains("TODO") {
                    err("TODO is deprecated; use FIXME")
                }
                if line.contains("//") && line.contains(" XXX") {
                    err("XXX is deprecated; use FIXME")
                }
            }
            if !skip_copyright && (line.starts_with("// Copyright") ||
                                   line.starts_with("# Copyright") ||
                                   line.starts_with("Copyright"))
                               && (line.contains("Rust Developers") ||
                                   line.contains("Rust Project Developers")) {
                err("copyright notices attributed to the Rust Project Developers are deprecated");
            }
            if line.ends_with("```ignore") || line.ends_with("```rust,ignore") {
                err(UNEXPLAINED_IGNORE_DOCTEST_INFO);
            }
            if filename.ends_with(".cpp") && line.contains("llvm_unreachable") {
                err(LLVM_UNREACHABLE_INFO);
            }
            if line.is_empty() {
                trailing_new_lines += 1;
            } else {
                trailing_new_lines = 0;
            }
        }
        match trailing_new_lines {
            0 => tidy_error!(bad, "{}: missing trailing newline", file.display()),
            1 | 2 => {}
            n => tidy_error!(bad, "{}: too many trailing newlines ({})", file.display(), n),
        };
    })
}
<commit_msg>Add a tidy check for leading trailing newlines<commit_after>//! Tidy check to enforce various stylistic guidelines on the Rust codebase.
//!
//! Example checks are:
//!
//! * No lines over 100 characters.
//! * No tabs.
//! * No trailing whitespace.
//! * No CR characters.
//! * No `TODO` or `XXX` directives.
//! * No unexplained ` ```ignore ` or ` ```rust,ignore ` doc tests.
//!
//! A number of these checks can be opted-out of with various directives like
//! `// ignore-tidy-linelength`.

use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

const COLS: usize = 100;

const UNEXPLAINED_IGNORE_DOCTEST_INFO: &str = r#"unexplained "```ignore" doctest; try one:

* make the test actually pass, by adding necessary imports and declarations, or
* use "```text", if the code is not Rust code, or
* use "```compile_fail,Ennnn", if the code is expected to fail at compile time, or
* use "```should_panic", if the code is expected to fail at run time, or
* use "```no_run", if the code should type-check but not necessary linkable/runnable, or
* explain it like "```ignore (cannot-test-this-because-xxxx)", if the annotation cannot be avoided.

"#;

const LLVM_UNREACHABLE_INFO: &str = r"\
C++ code used llvm_unreachable, which triggers undefined behavior
when executed when assertions are disabled.
Use llvm::report_fatal_error for increased robustness.";

/// Parser states for `line_is_url`.
#[derive(PartialEq)]
#[allow(non_camel_case_types)]
enum LIUState {
    EXP_COMMENT_START,
    EXP_LINK_LABEL_OR_URL,
    EXP_URL,
    EXP_END,
}

/// Returns `true` if `line` appears to be a line comment containing an URL,
/// possibly with a Markdown link label in front, and nothing else.
/// The Markdown link label, if present, may not contain whitespace.
/// Lines of this form are allowed to be overlength, because Markdown
/// offers no way to split a line in the middle of a URL, and the lengths
/// of URLs to external references are beyond our control.
fn line_is_url(line: &str) -> bool {
    use self::LIUState::*;
    let mut state: LIUState = EXP_COMMENT_START;

    for tok in line.split_whitespace() {
        match (state, tok) {
            (EXP_COMMENT_START, "//") => state = EXP_LINK_LABEL_OR_URL,
            (EXP_COMMENT_START, "///") => state = EXP_LINK_LABEL_OR_URL,
            (EXP_COMMENT_START, "//!") => state = EXP_LINK_LABEL_OR_URL,

            (EXP_LINK_LABEL_OR_URL, w)
                if w.len() >= 4 && w.starts_with('[') && w.ends_with("]:")
                => state = EXP_URL,

            (EXP_LINK_LABEL_OR_URL, w)
                if w.starts_with("http://") || w.starts_with("https://")
                => state = EXP_END,

            (EXP_URL, w)
                if w.starts_with("http://") || w.starts_with("https://") || w.starts_with("../")
                => state = EXP_END,

            (_, _) => return false,
        }
    }

    state == EXP_END
}

/// Returns `true` if `line` is allowed to be longer than the normal limit.
/// Currently there is only one exception, for long URLs, but more
/// may be added in the future.
fn long_line_is_ok(line: &str) -> bool {
    if line_is_url(line) {
        return true;
    }

    false
}

pub fn check(path: &Path, bad: &mut bool) {
    let mut contents = String::new();
    super::walk(path, &mut super::filter_dirs, &mut |file| {
        let filename = file.file_name().unwrap().to_string_lossy();
        let extensions = [".rs", ".py", ".js", ".sh", ".c", ".cpp", ".h"];
        if extensions.iter().all(|e| !filename.ends_with(e)) ||
           filename.starts_with(".#") {
            return
        }

        contents.truncate(0);
        t!(t!(File::open(file), file).read_to_string(&mut contents));

        if contents.is_empty() {
            tidy_error!(bad, "{}: empty file", file.display());
        }

        let skip_cr = contents.contains("ignore-tidy-cr");
        let skip_tab = contents.contains("ignore-tidy-tab");
        let skip_length = contents.contains("ignore-tidy-linelength");
        let skip_end_whitespace = contents.contains("ignore-tidy-end-whitespace");
        let skip_copyright = contents.contains("ignore-tidy-copyright");
        let mut leading_new_lines = false;
        let mut trailing_new_lines = 0;
        for (i, line) in contents.split('\n').enumerate() {
            let mut err = |msg: &str| {
                tidy_error!(bad, "{}:{}: {}", file.display(), i + 1, msg);
            };
            if !skip_length && line.chars().count() > COLS
                && !long_line_is_ok(line) {
                    err(&format!("line longer than {} chars", COLS));
            }
            if !skip_tab && line.contains('\t') {
                err("tab character");
            }
            if !skip_end_whitespace && (line.ends_with(' ') || line.ends_with('\t')) {
                err("trailing whitespace");
            }
            if !skip_cr && line.contains('\r') {
                err("CR character");
            }
            if filename != "style.rs" {
                if line.contains("TODO") {
                    err("TODO is deprecated; use FIXME")
                }
                if line.contains("//") && line.contains(" XXX") {
                    err("XXX is deprecated; use FIXME")
                }
            }
            if !skip_copyright && (line.starts_with("// Copyright") ||
                                   line.starts_with("# Copyright") ||
                                   line.starts_with("Copyright"))
                               && (line.contains("Rust Developers") ||
                                   line.contains("Rust Project Developers")) {
                err("copyright notices attributed to the Rust Project Developers are deprecated");
            }
            if line.ends_with("```ignore") || line.ends_with("```rust,ignore") {
                err(UNEXPLAINED_IGNORE_DOCTEST_INFO);
            }
            if filename.ends_with(".cpp") && line.contains("llvm_unreachable") {
                err(LLVM_UNREACHABLE_INFO);
            }
            if line.is_empty() {
                if i == 0 {
                    leading_new_lines = true;
                }
                trailing_new_lines += 1;
            } else {
                trailing_new_lines = 0;
            }
        }
        if leading_new_lines {
            tidy_error!(bad, "{}: leading newline", file.display());
        }
        match trailing_new_lines {
            0 => tidy_error!(bad, "{}: missing trailing newline", file.display()),
            1 | 2 => {}
            n => tidy_error!(bad, "{}: too many trailing newlines ({})", file.display(), n),
        };
    })
}
<|endoftext|>


<commit_before>use std::ops::{Deref, DerefMut};
use rustwlc::{Geometry, Size, Point};
use super::super::borders::Borders;
use ::render::{BaseDraw, Drawable, DrawErr};

/// Draws the borders around windows.
/// They are all of the same size, including the top.
pub struct BordersDraw {
    base: BaseDraw<Borders>
}

impl BordersDraw {
    pub fn new(base: BaseDraw<Borders>) -> Self {
        BordersDraw {
            base: base
        }
    }

    fn draw_left_border(mut self,
                        mut x: f64,
                        mut y: f64,
                        mut w: f64,
                        mut h: f64,
                        border_geometry: Geometry,
                        output_res: Size)
                        -> Result<Self, DrawErr<Borders>> {
        let title_size = Borders::title_bar_size() as f64;
        // yay clamping
        if x < 0.0 {
            w += x;
        }
        if y < 0.0 {
            h += y - title_size;
        }
        x = 0.0;
        y = title_size;
        if border_geometry.origin.x + border_geometry.size.w as i32 > output_res.w as i32 {
            let offset = (border_geometry.origin.x + border_geometry.size.w as i32)
                - output_res.w as i32;
            x += offset as f64;
        }
        if border_geometry.origin.y + border_geometry.size.h as i32 > output_res.h as i32 {
            let offset = (border_geometry.origin.y + border_geometry.size.h as i32)
                - output_res.h as i32;
            y += offset as f64;
        }
        self.base.rectangle(x, y, w, h);

        self.base = try!(self.base.check_cairo());
        self.base.fill();
        self.base = try!(self.base.check_cairo());
        Ok(self)
    }

    fn draw_right_border(mut self,
                         mut x: f64,
                         mut y: f64,
                         w: f64,
                         mut h: f64,
                         border_geometry: Geometry,
                         output_res: Size)
                         -> Result<Self, DrawErr<Borders>> {
        let title_size = Borders::title_bar_size() as f64;
        // yay clamping
        if border_geometry.origin.x < 0 {
            x += border_geometry.origin.x as f64;
        }
        if y < 0.0 {
            h += y - title_size;
        }
        y = title_size;
        if border_geometry.origin.x + border_geometry.size.w as i32 > output_res.w as i32 {
            let offset = (border_geometry.origin.x + border_geometry.size.w as i32)
                - output_res.w as i32;
            x += offset as f64;
        }
        if border_geometry.origin.y + border_geometry.size.h as i32 > output_res.h as i32 {
            let offset = (border_geometry.origin.y + border_geometry.size.h as i32)
                - output_res.h as i32;
            y += offset as f64;
        }
        self.base.rectangle(x, y, w, h);
        self.base = try!(self.base.check_cairo());
        self.base.fill();
        self.base = try!(self.base.check_cairo());
        Ok(self)
    }

    fn draw_title_bar(mut self,
                      mut x: f64,
                      _y: f64,
                      mut w: f64,
                      _h: f64,
                      border_geometry: Geometry,
                      output_res: Size) -> Result<Self, DrawErr<Borders>> {
        let title_size = Borders::title_bar_size() as f64;
        let title_color = self.base.inner().title_background_color();
        let title_font_color = self.base.inner().title_font_color();
        let title: String = self.inner().title().into();
        if x < 0.0 {
            w += x;
        }
        let mut title_x = Borders::thickness() as f64;
        let mut title_y = title_size;
        x = 0.0;
        let mut y = 0.0;
        if border_geometry.origin.x + border_geometry.size.w as i32 > output_res.w as i32 {
            let offset = (border_geometry.origin.x + border_geometry.size.w as i32)
                - output_res.w as i32;
            x += offset as f64;
            title_x += offset as f64;
        }
        if border_geometry.origin.y + border_geometry.size.h as i32 > output_res.h as i32 {
            let offset = (border_geometry.origin.y + border_geometry.size.h as i32)
                - output_res.h as i32;
            y += offset as f64;
            title_y += offset as f64;
        }
        // Draw background of title bar
        self.base.set_source_rgb(1.0, 0.0, 0.0);
        self.base.set_color_source(title_color);
        self.base.rectangle(x, y, w, title_size);
        self.base = try!(self.base.check_cairo());
        self.base.fill();
        self.base = try!(self.base.check_cairo());

        // Draw title text
        self.base.move_to(title_x, title_y);
        self.base = try!(self.base.check_cairo());
        self.base.set_color_source(title_font_color);
        self.base = try!(self.base.check_cairo());
        self.base.show_text(title.as_str());
        self.base = try!(self.base.check_cairo());
        Ok(self)
    }

    fn draw_top_border(mut self,
                         mut x: f64,
                         mut y: f64,
                         mut w: f64,
                         mut h: f64,
                         border_geometry: Geometry,
                         output_res: Size) -> Result<Self, DrawErr<Borders>> {
        let title_size = Borders::title_bar_size() as f64;
        // yay clamping
        if x < 0.0 {
            w += x;
        }
        if y < 0.0 {
            h += y;
        }
        x = 0.0;
        y = title_size;
        if border_geometry.origin.x + border_geometry.size.w as i32 > output_res.w as i32 {
            let offset = (border_geometry.origin.x + border_geometry.size.w as i32)
                - output_res.w as i32;
            x += offset as f64;
        }
        if border_geometry.origin.y + border_geometry.size.h as i32 > output_res.h as i32 {
            let offset = (border_geometry.origin.y + border_geometry.size.h as i32)
                - output_res.h as i32;
            y += offset as f64;
        }
        self.base.rectangle(x, y, w, h);
        self.base = try!(self.base.check_cairo());
        self.base.fill();
        self.base = try!(self.base.check_cairo());
        Ok(self)
    }

    fn draw_bottom_border(mut self,
                            mut x: f64,
                            mut y: f64,
                            mut w: f64,
                            h: f64,
                            border_geometry: Geometry,
                            output_res: Size)
                            -> Result<Self, DrawErr<Borders>> {
        // yay clamping
        if x < 0.0 {
            w += x;
        }
        if border_geometry.origin.y < 0 {
            y += border_geometry.origin.y as f64;
        }
        x = 0.0;
        if border_geometry.origin.x + border_geometry.size.w as i32 > output_res.w as i32 {
            let offset = (border_geometry.origin.x + border_geometry.size.w as i32)
                - output_res.w as i32;
            x += offset as f64;
        }
        if border_geometry.origin.y + border_geometry.size.h as i32 > output_res.h as i32 {
            let offset = (border_geometry.origin.y + border_geometry.size.h as i32)
                - output_res.h as i32;
            y += offset as f64;
        }
        self.base.rectangle(x, y, w, h);
        self.base = try!(self.base.check_cairo());
        self.base.fill();
        self.base = try!(self.base.check_cairo());
        Ok(self)
    }
}

impl Drawable<Borders> for BordersDraw {
    fn draw(mut self, view_g: Geometry) -> Result<Borders, DrawErr<Borders>> {
        let mut border_g = view_g;
        let thickness = Borders::thickness();
        let edge_thickness = thickness / 2;
        let output_res = self.inner().get_output().get_resolution()
            .expect("Could not get focused output's resolution");
        let title_size = Borders::title_bar_size();
        border_g.origin.x -= edge_thickness as i32;
        border_g.origin.y -= edge_thickness as i32;
        border_g.origin.y -= title_size as i32;
        border_g.size.w += thickness;
        border_g.size.h += thickness;
        border_g.size.h += title_size;

        self.base.set_source_rgba(0.0, 0.0, 0.0, 0.0);
        self.base.paint();
        let color = self.base.inner().color();
        self.base.set_color_source(color);

        let Size { w, h } = border_g.size;
        let Point { x, y } = border_g.origin;
        // basically after everything after this point is floating point arithmetic
        // whee!
        let (x, y, w, h) = (x as f64,
                            y as f64,
                            w as f64,
                            h as f64);
        let edge_thickness = edge_thickness as f64;
        self = self.draw_left_border(x, y, edge_thickness, h, border_g, output_res)?
        .draw_right_border(w - edge_thickness, y, edge_thickness, h, border_g, output_res)?
        .draw_top_border(x, y, w, edge_thickness, border_g, output_res)?
        .draw_bottom_border(x, h, w, -edge_thickness, border_g, output_res)?
        .draw_title_bar(x, y, w, edge_thickness, border_g, output_res)?;

        Ok(self.base.finish(border_g))
    }
}

impl Deref for BordersDraw {
    type Target = BaseDraw<Borders>;

    fn deref(&self) -> &BaseDraw<Borders> {
        &self.base
    }
}

impl DerefMut for BordersDraw {
    fn deref_mut(&mut self) -> &mut BaseDraw<Borders> {
        &mut self.base
    }
}
<commit_msg>Fixed where the title starts drawing<commit_after>use std::ops::{Deref, DerefMut};
use rustwlc::{Geometry, Size, Point};
use super::super::borders::Borders;
use ::render::{BaseDraw, Drawable, DrawErr};

/// Draws the borders around windows.
/// They are all of the same size, including the top.
pub struct BordersDraw {
    base: BaseDraw<Borders>
}

impl BordersDraw {
    pub fn new(base: BaseDraw<Borders>) -> Self {
        BordersDraw {
            base: base
        }
    }

    fn draw_left_border(mut self,
                        mut x: f64,
                        mut y: f64,
                        mut w: f64,
                        mut h: f64,
                        border_geometry: Geometry,
                        output_res: Size)
                        -> Result<Self, DrawErr<Borders>> {
        let title_size = Borders::title_bar_size() as f64;
        // yay clamping
        if x < 0.0 {
            w += x;
        }
        if y < 0.0 {
            h += y - title_size;
        }
        x = 0.0;
        y = title_size;
        if border_geometry.origin.x + border_geometry.size.w as i32 > output_res.w as i32 {
            let offset = (border_geometry.origin.x + border_geometry.size.w as i32)
                - output_res.w as i32;
            x += offset as f64;
        }
        if border_geometry.origin.y + border_geometry.size.h as i32 > output_res.h as i32 {
            let offset = (border_geometry.origin.y + border_geometry.size.h as i32)
                - output_res.h as i32;
            y += offset as f64;
        }
        self.base.rectangle(x, y, w, h);

        self.base = try!(self.base.check_cairo());
        self.base.fill();
        self.base = try!(self.base.check_cairo());
        Ok(self)
    }

    fn draw_right_border(mut self,
                         mut x: f64,
                         mut y: f64,
                         w: f64,
                         mut h: f64,
                         border_geometry: Geometry,
                         output_res: Size)
                         -> Result<Self, DrawErr<Borders>> {
        let title_size = Borders::title_bar_size() as f64;
        // yay clamping
        if border_geometry.origin.x < 0 {
            x += border_geometry.origin.x as f64;
        }
        if y < 0.0 {
            h += y - title_size;
        }
        y = title_size;
        if border_geometry.origin.x + border_geometry.size.w as i32 > output_res.w as i32 {
            let offset = (border_geometry.origin.x + border_geometry.size.w as i32)
                - output_res.w as i32;
            x += offset as f64;
        }
        if border_geometry.origin.y + border_geometry.size.h as i32 > output_res.h as i32 {
            let offset = (border_geometry.origin.y + border_geometry.size.h as i32)
                - output_res.h as i32;
            y += offset as f64;
        }
        self.base.rectangle(x, y, w, h);
        self.base = try!(self.base.check_cairo());
        self.base.fill();
        self.base = try!(self.base.check_cairo());
        Ok(self)
    }

    fn draw_title_bar(mut self,
                      mut x: f64,
                      _y: f64,
                      mut w: f64,
                      _h: f64,
                      border_geometry: Geometry,
                      output_res: Size) -> Result<Self, DrawErr<Borders>> {
        let title_size = Borders::title_bar_size() as f64;
        let title_color = self.base.inner().title_background_color();
        let title_font_color = self.base.inner().title_font_color();
        let title: String = self.inner().title().into();
        if x < 0.0 {
            w += x;
        }
        let mut title_x = Borders::thickness() as f64;
        let mut title_y = title_size - 5.0;
        x = 0.0;
        let mut y = 0.0;
        if border_geometry.origin.x + border_geometry.size.w as i32 > output_res.w as i32 {
            let offset = (border_geometry.origin.x + border_geometry.size.w as i32)
                - output_res.w as i32;
            x += offset as f64;
            title_x += offset as f64;
        }
        if border_geometry.origin.y + border_geometry.size.h as i32 > output_res.h as i32 {
            let offset = (border_geometry.origin.y + border_geometry.size.h as i32)
                - output_res.h as i32;
            y += offset as f64;
            title_y += offset as f64;
        }
        // Draw background of title bar
        self.base.set_source_rgb(1.0, 0.0, 0.0);
        self.base.set_color_source(title_color);
        self.base.rectangle(x, y, w, title_size);
        self.base = try!(self.base.check_cairo());
        self.base.fill();
        self.base = try!(self.base.check_cairo());

        // Draw title text
        self.base.move_to(title_x, title_y);
        self.base = try!(self.base.check_cairo());
        self.base.set_color_source(title_font_color);
        self.base = try!(self.base.check_cairo());
        self.base.show_text(title.as_str());
        self.base = try!(self.base.check_cairo());
        Ok(self)
    }

    fn draw_top_border(mut self,
                         mut x: f64,
                         mut y: f64,
                         mut w: f64,
                         mut h: f64,
                         border_geometry: Geometry,
                         output_res: Size) -> Result<Self, DrawErr<Borders>> {
        let title_size = Borders::title_bar_size() as f64;
        // yay clamping
        if x < 0.0 {
            w += x;
        }
        if y < 0.0 {
            h += y;
        }
        x = 0.0;
        y = title_size;
        if border_geometry.origin.x + border_geometry.size.w as i32 > output_res.w as i32 {
            let offset = (border_geometry.origin.x + border_geometry.size.w as i32)
                - output_res.w as i32;
            x += offset as f64;
        }
        if border_geometry.origin.y + border_geometry.size.h as i32 > output_res.h as i32 {
            let offset = (border_geometry.origin.y + border_geometry.size.h as i32)
                - output_res.h as i32;
            y += offset as f64;
        }
        self.base.rectangle(x, y, w, h);
        self.base = try!(self.base.check_cairo());
        self.base.fill();
        self.base = try!(self.base.check_cairo());
        Ok(self)
    }

    fn draw_bottom_border(mut self,
                            mut x: f64,
                            mut y: f64,
                            mut w: f64,
                            h: f64,
                            border_geometry: Geometry,
                            output_res: Size)
                            -> Result<Self, DrawErr<Borders>> {
        // yay clamping
        if x < 0.0 {
            w += x;
        }
        if border_geometry.origin.y < 0 {
            y += border_geometry.origin.y as f64;
        }
        x = 0.0;
        if border_geometry.origin.x + border_geometry.size.w as i32 > output_res.w as i32 {
            let offset = (border_geometry.origin.x + border_geometry.size.w as i32)
                - output_res.w as i32;
            x += offset as f64;
        }
        if border_geometry.origin.y + border_geometry.size.h as i32 > output_res.h as i32 {
            let offset = (border_geometry.origin.y + border_geometry.size.h as i32)
                - output_res.h as i32;
            y += offset as f64;
        }
        self.base.rectangle(x, y, w, h);
        self.base = try!(self.base.check_cairo());
        self.base.fill();
        self.base = try!(self.base.check_cairo());
        Ok(self)
    }
}

impl Drawable<Borders> for BordersDraw {
    fn draw(mut self, view_g: Geometry) -> Result<Borders, DrawErr<Borders>> {
        let mut border_g = view_g;
        let thickness = Borders::thickness();
        let edge_thickness = thickness / 2;
        let output_res = self.inner().get_output().get_resolution()
            .expect("Could not get focused output's resolution");
        let title_size = Borders::title_bar_size();
        border_g.origin.x -= edge_thickness as i32;
        border_g.origin.y -= edge_thickness as i32;
        border_g.origin.y -= title_size as i32;
        border_g.size.w += thickness;
        border_g.size.h += thickness;
        border_g.size.h += title_size;

        self.base.set_source_rgba(0.0, 0.0, 0.0, 0.0);
        self.base.paint();
        let color = self.base.inner().color();
        self.base.set_color_source(color);

        let Size { w, h } = border_g.size;
        let Point { x, y } = border_g.origin;
        // basically after everything after this point is floating point arithmetic
        // whee!
        let (x, y, w, h) = (x as f64,
                            y as f64,
                            w as f64,
                            h as f64);
        let edge_thickness = edge_thickness as f64;
        self = self.draw_left_border(x, y, edge_thickness, h, border_g, output_res)?
        .draw_right_border(w - edge_thickness, y, edge_thickness, h, border_g, output_res)?
        .draw_top_border(x, y, w, edge_thickness, border_g, output_res)?
        .draw_bottom_border(x, h, w, -edge_thickness, border_g, output_res)?
        .draw_title_bar(x, y, w, edge_thickness, border_g, output_res)?;

        Ok(self.base.finish(border_g))
    }
}

impl Deref for BordersDraw {
    type Target = BaseDraw<Borders>;

    fn deref(&self) -> &BaseDraw<Borders> {
        &self.base
    }
}

impl DerefMut for BordersDraw {
    fn deref_mut(&mut self) -> &mut BaseDraw<Borders> {
        &mut self.base
    }
}
<|endoftext|>


<commit_before>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// Core/lib/btree_map.rs
//! B-Tree map
//!
//! B-Trees are a more memory/cache efficient version of binary trees, storing up to `b` items
//! per node
use prelude::*;

pub struct BTreeMap<K: Ord,V>
{
	root_node: Option< Box< Node<K,V> > >,
	max_node_size: usize,	// aka 'b'
}

struct Node<K, V>
{
	values: Vec< Item<K,V> >,
	children: Vec< Node<K,V> >,
}
struct Item<K, V>
{
	key: K,
	val: V,
}

pub enum Entry<'a, K: 'a, V: 'a>
{
	Vacant(VacantEntry<'a,K,V>),
	Occupied(OccupiedEntry<'a,K,V>),
}
pub struct VacantEntry<'a, K:'a,V:'a> {
	root: &'a mut Node<K,V>,
	key: K,
}
pub struct OccupiedEntry<'a, K:'a,V:'a> {
	node: &'a mut Item<K,V>
}

impl<K: Ord,V> BTreeMap<K,V>
{
	pub fn new() -> BTreeMap<K,V> {
		BTreeMap::with_b(8)
	}

	pub fn with_b(b: usize) -> Self {
		BTreeMap {
			root_node: None,
			max_node_size: b,
		}
	}

	fn rebalance(&mut self)
	{
	}

	fn get_ptr<Q: ?Sized>(&self, key: &Q) -> Option<*mut V>
	where
		Q: Ord,
		K: ::lib::borrow::Borrow<Q>
	{
		let mut node = match self.root_node { Some(ref v) => &**v, None => return None };
		loop
		{
			match node.values.binary_search_by(|v| v.key.borrow().cmp(key))
			{
			Ok(idx) => return Some(&node.values[idx].val as *const _ as *mut _),
			Err(idx) => if idx <= node.children.len() {
					node = &node.children[idx];
				}
				else {
					return None;
				},
			}
		}
	}

	pub fn entry(&mut self, key: K) -> Entry<K,V> {
		unimplemented!()
	}

	pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<&V>
	where
		Q: Ord,
		K: ::lib::borrow::Borrow<Q>
	{
		unsafe { self.get_ptr(key).map(|x| &*x) }
	}
	pub fn get_mut<Q: ?Sized>(&mut self, key: &Q) -> Option<&mut V>
	where
		Q: Ord,
		K: ::lib::borrow::Borrow<Q>
	{
		unsafe { self.get_ptr(key).map(|x| &mut *x) }
	}
}

impl<K: Ord, V> Default for BTreeMap<K,V>
{
	fn default() -> BTreeMap<K,V> {
		BTreeMap::new()
	}
}

impl<'a,K,V> VacantEntry<'a,K,V> {
	pub fn insert(self, value: V) -> &'a mut V {
		unimplemented!()
	}
}

impl<'a,K,V> OccupiedEntry<'a,K,V> {
	pub fn get_mut(&mut self) -> &mut V {
		&mut self.node.val
	}
	pub fn into_mut(self) -> &'a mut V {
		&mut self.node.val
	}
}

<commit_msg>Kernel/BTreeMap - Misc tweaking (plans)<commit_after>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// Core/lib/btree_map.rs
//! B-Tree map
//!
//! B-Trees are a more memory/cache efficient version of binary trees, storing up to `b` items
//! per node
use prelude::*;

pub struct BTreeMap<K: Ord,V>
{
	root_node: Option< Box< Node<K,V> > >,
	max_node_size: usize,	// aka 'b'
}

struct Node<K, V>
{
	values: Vec< Item<K,V> >,
	children: Vec< Node<K,V> >,
}
struct Item<K, V>
{
	key: K,
	val: V,
}

pub enum Entry<'a, K: 'a, V: 'a>
{
	Vacant(VacantEntry<'a,K,V>),
	Occupied(OccupiedEntry<'a,K,V>),
}
pub struct VacantEntry<'a, K:'a,V:'a> {
	root: &'a mut Node<K,V>,
	key: K,
}
pub struct OccupiedEntry<'a, K:'a,V:'a> {
	node: &'a mut Item<K,V>
}

impl<K: Ord,V> BTreeMap<K,V>
{
	pub fn new() -> BTreeMap<K,V> {
		BTreeMap::with_b(8)
	}

	pub fn with_b(b: usize) -> Self {
		BTreeMap {
			root_node: None,
			max_node_size: b,
		}
	}

	fn rebalance(&mut self)
	{
	}

	fn get_ptr<Q: ?Sized>(&self, key: &Q) -> Option<*mut V>
	where
		Q: Ord,
		K: ::lib::borrow::Borrow<Q>
	{
		let mut node = match self.root_node { Some(ref v) => &**v, None => return None };
		loop
		{
			match node.values.binary_search_by(|v| v.key.borrow().cmp(key))
			{
			Ok(idx) => return Some(&node.values[idx].val as *const _ as *mut _),
			Err(idx) => if idx <= node.children.len() {
					node = &node.children[idx];
				}
				else {
					return None;
				},
			}
		}
	}

	pub fn entry(&mut self, key: K) -> Entry<K,V> {
		unimplemented!()
	}

	pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<&V>
	where
		Q: Ord,
		K: ::lib::borrow::Borrow<Q>
	{
		unsafe { self.get_ptr(key).map(|x| &*x) }
	}
	pub fn get_mut<Q: ?Sized>(&mut self, key: &Q) -> Option<&mut V>
	where
		Q: Ord,
		K: ::lib::borrow::Borrow<Q>
	{
		unsafe { self.get_ptr(key).map(|x| &mut *x) }
	}
}

impl<K: Ord, V> Default for BTreeMap<K,V>
{
	fn default() -> BTreeMap<K,V> {
		BTreeMap::new()
	}
}

impl<'a,K,V> VacantEntry<'a,K,V> {
	pub fn insert(self, value: V) -> &'a mut V {
		// 1. Allocate a slot (which may require splitting a node and hence rebalancing the tree)
		unimplemented!()
	}
}

impl<'a,K,V> OccupiedEntry<'a,K,V> {
	pub fn get_mut(&mut self) -> &mut V {
		&mut self.node.val
	}
	pub fn into_mut(self) -> &'a mut V {
		&mut self.node.val
	}
}

<|endoftext|>


<commit_before>use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::sync::{Lock, LockGuard, MappedLockGuard};
use rustc_index::vec::IndexVec;
use rustc_serialize as serialize;
use smallvec::SmallVec;

use crate::mir::{BasicBlock, BasicBlockData};

// Typically 95%+ of basic blocks have 4 or fewer predecessors.
pub type Predecessors = IndexVec<BasicBlock, SmallVec<[BasicBlock; 4]>>;

#[derive(Clone, Debug)]
pub struct PredecessorCache {
    cache: Lock<Option<Predecessors>>,
}

impl PredecessorCache {
    #[inline]
    pub fn new() -> Self {
        PredecessorCache { cache: Lock::new(None) }
    }

    #[inline]
    pub fn invalidate(&mut self) {
        *self.cache.get_mut() = None;
    }

    #[inline]
    pub fn compute(
        &self,
        basic_blocks: &IndexVec<BasicBlock, BasicBlockData<'_>>,
    ) -> MappedLockGuard<'_, Predecessors> {
        LockGuard::map(self.cache.lock(), |cache| {
            cache.get_or_insert_with(|| {
                let mut preds = IndexVec::from_elem(SmallVec::new(), basic_blocks);
                for (bb, data) in basic_blocks.iter_enumerated() {
                    if let Some(term) = &data.terminator {
                        for &succ in term.successors() {
                            preds[succ].push(bb);
                        }
                    }
                }

                preds
            })
        })
    }
}

impl serialize::Encodable for PredecessorCache {
    #[inline]
    fn encode<S: serialize::Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        serialize::Encodable::encode(&(), s)
    }
}

impl serialize::Decodable for PredecessorCache {
    #[inline]
    fn decode<D: serialize::Decoder>(d: &mut D) -> Result<Self, D::Error> {
        serialize::Decodable::decode(d).map(|_v: ()| Self::new())
    }
}

impl<CTX> HashStable<CTX> for PredecessorCache {
    #[inline]
    fn hash_stable(&self, _: &mut CTX, _: &mut StableHasher) {
        // do nothing
    }
}

CloneTypeFoldableAndLiftImpls! {
    PredecessorCache,
}
<commit_msg>Use a ref-counted pointer for ownership of the predecessor cache<commit_after>use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::sync::{Lock, Lrc};
use rustc_index::vec::IndexVec;
use rustc_serialize as serialize;
use smallvec::SmallVec;

use crate::mir::{BasicBlock, BasicBlockData};

// Typically 95%+ of basic blocks have 4 or fewer predecessors.
pub type Predecessors = IndexVec<BasicBlock, SmallVec<[BasicBlock; 4]>>;

#[derive(Clone, Debug)]
pub struct PredecessorCache {
    cache: Lock<Option<Lrc<Predecessors>>>,
}

impl PredecessorCache {
    #[inline]
    pub fn new() -> Self {
        PredecessorCache { cache: Lock::new(None) }
    }

    /// Invalidates the predecessor cache.
    ///
    /// Invalidating the predecessor cache requires mutating the MIR, which in turn requires a
    /// unique reference (`&mut`) to the `mir::Body`. Because of this, we can assume that all
    /// callers of `invalidate` have a unique reference to the MIR and thus to the predecessor
    /// cache. This means we don't actually need to take a lock when `invalidate` is called.
    #[inline]
    pub fn invalidate(&mut self) {
        *self.cache.get_mut() = None;
    }

    /// Returns a ref-counted smart pointer containing the predecessor graph for this MIR.
    ///
    /// We use ref-counting instead of a mapped `LockGuard` here to ensure that the lock for
    /// `cache` is only held inside this function. As long as no other locks are taken while
    /// computing the predecessor graph, deadlock is impossible.
    #[inline]
    pub fn compute(
        &self,
        basic_blocks: &IndexVec<BasicBlock, BasicBlockData<'_>>,
    ) -> Lrc<Predecessors> {
        Lrc::clone(self.cache.lock().get_or_insert_with(|| {
            let mut preds = IndexVec::from_elem(SmallVec::new(), basic_blocks);
            for (bb, data) in basic_blocks.iter_enumerated() {
                if let Some(term) = &data.terminator {
                    for &succ in term.successors() {
                        preds[succ].push(bb);
                    }
                }
            }

            Lrc::new(preds)
        }))
    }
}

impl serialize::Encodable for PredecessorCache {
    #[inline]
    fn encode<S: serialize::Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        serialize::Encodable::encode(&(), s)
    }
}

impl serialize::Decodable for PredecessorCache {
    #[inline]
    fn decode<D: serialize::Decoder>(d: &mut D) -> Result<Self, D::Error> {
        serialize::Decodable::decode(d).map(|_v: ()| Self::new())
    }
}

impl<CTX> HashStable<CTX> for PredecessorCache {
    #[inline]
    fn hash_stable(&self, _: &mut CTX, _: &mut StableHasher) {
        // do nothing
    }
}

CloneTypeFoldableAndLiftImpls! {
    PredecessorCache,
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{ffi::OsString,
          io,
          path::PathBuf,
          process::{self,
                    Command},
          ptr};

use winapi::{shared::minwindef::{DWORD,
                                 FALSE,
                                 LPDWORD},
             um::{handleapi,
                  processthreadsapi,
                  winnt::{HANDLE,
                          PROCESS_QUERY_LIMITED_INFORMATION,
                          PROCESS_TERMINATE}}};

use super::{OsSignal,
            Signal};
use crate::error::{Error,
                   Result};

const STILL_ACTIVE: u32 = 259;

pub type Pid = DWORD;
pub type SignalCode = DWORD;

impl OsSignal for Signal {
    fn from_signal_code(code: SignalCode) -> Option<Signal> { None }

    fn os_signal(&self) -> SignalCode { 0 }
}

pub fn become_command(command: PathBuf, args: Vec<OsString>) -> Result<()> {
    become_child_command(command, args)
}

/// Get process identifier of calling process.
pub fn current_pid() -> u32 { unsafe { processthreadsapi::GetCurrentProcessId() as u32 } }

pub fn handle_from_pid(pid: Pid) -> Option<HANDLE> {
    unsafe {
        let proc_handle = processthreadsapi::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION
                                                         | PROCESS_TERMINATE,
                                                         FALSE,
                                                         pid);

        // we expect this to happen if the process died
        // before OpenProcess completes
        if proc_handle == ptr::null_mut() {
            return None;
        } else {
            return Some(proc_handle);
        }
    }
}

/// Determines if a process is running with the given process identifier.
pub fn is_alive(pid: Pid) -> bool {
    match handle_from_pid(pid) {
        Some(handle) => {
            let exit_status = exit_status(handle).expect("Failed to get exit status");
            unsafe {
                let _ = handleapi::CloseHandle(handle);
            }
            exit_status == STILL_ACTIVE
        }
        None => false,
    }
}

pub fn signal(pid: Pid, signal: Signal) -> Result<()> {
    debug!("sending no-op(windows) signal {} to pid {}",
           signal.os_signal(),
           pid);
    Ok(())
}

/// Executes a command as a child process and exits with the child's exit code.
///
/// Note that if successful, this function will not return.
///
/// # Failures
///
/// * If the child process cannot be created
fn become_child_command(command: PathBuf, args: Vec<OsString>) -> Result<()> {
    debug!("Calling child process: ({:?}) {:?}",
           command.display(),
           &args);
    let status = Command::new(command).args(&args).status()?;
    // Let's honor the exit codes from the child process we finished running
    process::exit(status.code().unwrap())
}

fn exit_status(handle: HANDLE) -> Result<u32> {
    let mut exit_status: u32 = 0;

    unsafe {
        let ret = processthreadsapi::GetExitCodeProcess(handle, &mut exit_status as LPDWORD);
        if ret == 0 {
            return Err(Error::GetExitCodeProcessFailed(format!(
                "Failed to retrieve Exit Code: {}",
                io::Error::last_os_error()
            )));
        }
    }

    Ok(exit_status)
}
<commit_msg>Add missing type-conversion to Windows code<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{ffi::OsString,
          io,
          path::PathBuf,
          process::{self,
                    Command},
          ptr};

use winapi::{shared::minwindef::{DWORD,
                                 FALSE,
                                 LPDWORD},
             um::{handleapi,
                  processthreadsapi,
                  winnt::{HANDLE,
                          PROCESS_QUERY_LIMITED_INFORMATION,
                          PROCESS_TERMINATE}}};

use super::{OsSignal,
            Signal};
use crate::error::{Error,
                   Result};

const STILL_ACTIVE: u32 = 259;

pub type Pid = DWORD;
pub type SignalCode = DWORD;

impl OsSignal for Signal {
    fn from_signal_code(code: SignalCode) -> Option<Signal> { None }

    fn os_signal(&self) -> SignalCode { 0 }
}

pub fn become_command(command: PathBuf, args: &[OsString]) -> Result<()> {
    become_child_command(command, args)
}

/// Get process identifier of calling process.
pub fn current_pid() -> u32 { unsafe { processthreadsapi::GetCurrentProcessId() as u32 } }

pub fn handle_from_pid(pid: Pid) -> Option<HANDLE> {
    unsafe {
        let proc_handle = processthreadsapi::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION
                                                         | PROCESS_TERMINATE,
                                                         FALSE,
                                                         pid);

        // we expect this to happen if the process died
        // before OpenProcess completes
        if proc_handle == ptr::null_mut() {
            return None;
        } else {
            return Some(proc_handle);
        }
    }
}

/// Determines if a process is running with the given process identifier.
pub fn is_alive(pid: Pid) -> bool {
    match handle_from_pid(pid) {
        Some(handle) => {
            let exit_status = exit_status(handle).expect("Failed to get exit status");
            unsafe {
                let _ = handleapi::CloseHandle(handle);
            }
            exit_status == STILL_ACTIVE
        }
        None => false,
    }
}

pub fn signal(pid: Pid, signal: Signal) -> Result<()> {
    debug!("sending no-op(windows) signal {} to pid {}",
           signal.os_signal(),
           pid);
    Ok(())
}

/// Executes a command as a child process and exits with the child's exit code.
///
/// Note that if successful, this function will not return.
///
/// # Failures
///
/// * If the child process cannot be created
fn become_child_command(command: PathBuf, args: &[OsString]) -> Result<()> {
    debug!("Calling child process: ({:?}) {:?}",
           command.display(),
           &args);
    let status = Command::new(command).args(args).status()?;
    // Let's honor the exit codes from the child process we finished running
    process::exit(status.code().unwrap())
}

fn exit_status(handle: HANDLE) -> Result<u32> {
    let mut exit_status: u32 = 0;

    unsafe {
        let ret = processthreadsapi::GetExitCodeProcess(handle, &mut exit_status as LPDWORD);
        if ret == 0 {
            return Err(Error::GetExitCodeProcessFailed(format!(
                "Failed to retrieve Exit Code: {}",
                io::Error::last_os_error()
            )));
        }
    }

    Ok(exit_status)
}
<|endoftext|>


<commit_before>// This file is part of the uutils coreutils package.
//
// (c) Vsevolod Velichko <torkvemada@sorokdva.net>
// (c) Jian Zeng <anonymousknight96 AT gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (ToDO) NEWROOT Userspec pstatus

#[macro_use]
extern crate uucore;
use clap::{crate_version, App, Arg};
use std::ffi::CString;
use std::io::Error;
use std::path::Path;
use std::process::Command;
use uucore::display::Quotable;
use uucore::libc::{self, chroot, setgid, setgroups, setuid};
use uucore::{entries, InvalidEncodingHandling};

static ABOUT: &str = "Run COMMAND with root directory set to NEWROOT.";
static SYNTAX: &str = "[OPTION]... NEWROOT [COMMAND [ARG]...]";

mod options {
    pub const NEWROOT: &str = "newroot";
    pub const USER: &str = "user";
    pub const GROUP: &str = "group";
    pub const GROUPS: &str = "groups";
    pub const USERSPEC: &str = "userspec";
    pub const COMMAND: &str = "command";
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let args = args
        .collect_str(InvalidEncodingHandling::ConvertLossy)
        .accept_any();

    let matches = uu_app().get_matches_from(args);

    let default_shell: &'static str = "/bin/sh";
    let default_option: &'static str = "-i";
    let user_shell = std::env::var("SHELL");

    let newroot: &Path = match matches.value_of(options::NEWROOT) {
        Some(v) => Path::new(v),
        None => crash!(
            1,
            "Missing operand: NEWROOT\nTry '{} --help' for more information.",
            uucore::execution_phrase()
        ),
    };

    if !newroot.is_dir() {
        crash!(
            1,
            "cannot change root directory to {}: no such directory",
            newroot.quote()
        );
    }

    let commands = match matches.values_of(options::COMMAND) {
        Some(v) => v.collect(),
        None => vec![],
    };

    // TODO: refactor the args and command matching
    // See: https://github.com/uutils/coreutils/pull/2365#discussion_r647849967
    let command: Vec<&str> = match commands.len() {
        1 => {
            let shell: &str = match user_shell {
                Err(_) => default_shell,
                Ok(ref s) => s.as_ref(),
            };
            vec![shell, default_option]
        }
        _ => commands,
    };

    set_context(newroot, &matches);

    let pstatus = Command::new(command[0])
        .args(&command[1..])
        .status()
        .unwrap_or_else(|e| crash!(1, "Cannot exec: {}", e));

    if pstatus.success() {
        0
    } else {
        pstatus.code().unwrap_or(-1)
    }
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .usage(SYNTAX)
        .arg(
            Arg::with_name(options::NEWROOT)
                .hidden(true)
                .required(true)
                .index(1),
        )
        .arg(
            Arg::with_name(options::USER)
                .short("u")
                .long(options::USER)
                .help("User (ID or name) to switch before running the program")
                .value_name("USER"),
        )
        .arg(
            Arg::with_name(options::GROUP)
                .short("g")
                .long(options::GROUP)
                .help("Group (ID or name) to switch to")
                .value_name("GROUP"),
        )
        .arg(
            Arg::with_name(options::GROUPS)
                .short("G")
                .long(options::GROUPS)
                .help("Comma-separated list of groups to switch to")
                .value_name("GROUP1,GROUP2..."),
        )
        .arg(
            Arg::with_name(options::USERSPEC)
                .long(options::USERSPEC)
                .help(
                    "Colon-separated user and group to switch to. \
                     Same as -u USER -g GROUP. \
                     Userspec has higher preference than -u and/or -g",
                )
                .value_name("USER:GROUP"),
        )
        .arg(
            Arg::with_name(options::COMMAND)
                .hidden(true)
                .multiple(true)
                .index(2),
        )
}

fn set_context(root: &Path, options: &clap::ArgMatches) {
    let userspec_str = options.value_of(options::USERSPEC);
    let user_str = options.value_of(options::USER).unwrap_or_default();
    let group_str = options.value_of(options::GROUP).unwrap_or_default();
    let groups_str = options.value_of(options::GROUPS).unwrap_or_default();
    let userspec = match userspec_str {
        Some(u) => {
            let s: Vec<&str> = u.split(':').collect();
            if s.len() != 2 || s.iter().any(|&spec| spec.is_empty()) {
                crash!(1, "invalid userspec: {}", u.quote())
            };
            s
        }
        None => Vec::new(),
    };

    let (user, group) = if userspec.is_empty() {
        (user_str, group_str)
    } else {
        (userspec[0], userspec[1])
    };

    enter_chroot(root);

    set_groups_from_str(groups_str);
    set_main_group(group);
    set_user(user);
}

fn enter_chroot(root: &Path) {
    std::env::set_current_dir(root).unwrap();
    let err = unsafe {
        chroot(CString::new(".").unwrap().as_bytes_with_nul().as_ptr() as *const libc::c_char)
    };
    if err != 0 {
        crash!(
            1,
            "cannot chroot to {}: {}",
            root.quote(),
            Error::last_os_error()
        )
    };
}

fn set_main_group(group: &str) {
    if !group.is_empty() {
        let group_id = match entries::grp2gid(group) {
            Ok(g) => g,
            _ => crash!(1, "no such group: {}", group.maybe_quote()),
        };
        let err = unsafe { setgid(group_id) };
        if err != 0 {
            crash!(
                1,
                "cannot set gid to {}: {}",
                group_id,
                Error::last_os_error()
            )
        }
    }
}

#[cfg(any(target_vendor = "apple", target_os = "freebsd"))]
fn set_groups(groups: Vec<libc::gid_t>) -> libc::c_int {
    unsafe { setgroups(groups.len() as libc::c_int, groups.as_ptr()) }
}

#[cfg(target_os = "linux")]
fn set_groups(groups: Vec<libc::gid_t>) -> libc::c_int {
    unsafe { setgroups(groups.len() as libc::size_t, groups.as_ptr()) }
}

fn set_groups_from_str(groups: &str) {
    if !groups.is_empty() {
        let groups_vec: Vec<libc::gid_t> = groups
            .split(',')
            .map(|x| match entries::grp2gid(x) {
                Ok(g) => g,
                _ => crash!(1, "no such group: {}", x),
            })
            .collect();
        let err = set_groups(groups_vec);
        if err != 0 {
            crash!(1, "cannot set groups: {}", Error::last_os_error())
        }
    }
}

fn set_user(user: &str) {
    if !user.is_empty() {
        let user_id = entries::usr2uid(user).unwrap();
        let err = unsafe { setuid(user_id as libc::uid_t) };
        if err != 0 {
            crash!(
                1,
                "cannot set user to {}: {}",
                user.maybe_quote(),
                Error::last_os_error()
            )
        }
    }
}
<commit_msg>`chroot`: quick fix for #2687<commit_after>// This file is part of the uutils coreutils package.
//
// (c) Vsevolod Velichko <torkvemada@sorokdva.net>
// (c) Jian Zeng <anonymousknight96 AT gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (ToDO) NEWROOT Userspec pstatus

#[macro_use]
extern crate uucore;
use clap::{crate_version, App, Arg};
use std::ffi::CString;
use std::io::Error;
use std::path::Path;
use std::process::Command;
use uucore::display::Quotable;
use uucore::libc::{self, chroot, setgid, setgroups, setuid};
use uucore::{entries, InvalidEncodingHandling};

static ABOUT: &str = "Run COMMAND with root directory set to NEWROOT.";
static SYNTAX: &str = "[OPTION]... NEWROOT [COMMAND [ARG]...]";

mod options {
    pub const NEWROOT: &str = "newroot";
    pub const USER: &str = "user";
    pub const GROUP: &str = "group";
    pub const GROUPS: &str = "groups";
    pub const USERSPEC: &str = "userspec";
    pub const COMMAND: &str = "command";
}

pub fn uumain(args: impl uucore::Args) -> i32 {
    let args = args
        .collect_str(InvalidEncodingHandling::ConvertLossy)
        .accept_any();

    let matches = uu_app().get_matches_from(args);

    let default_shell: &'static str = "/bin/sh";
    let default_option: &'static str = "-i";
    let user_shell = std::env::var("SHELL");

    let newroot: &Path = match matches.value_of(options::NEWROOT) {
        Some(v) => Path::new(v),
        None => crash!(
            1,
            "Missing operand: NEWROOT\nTry '{} --help' for more information.",
            uucore::execution_phrase()
        ),
    };

    if !newroot.is_dir() {
        crash!(
            1,
            "cannot change root directory to {}: no such directory",
            newroot.quote()
        );
    }

    let commands = match matches.values_of(options::COMMAND) {
        Some(v) => v.collect(),
        None => vec![],
    };

    // TODO: refactor the args and command matching
    // See: https://github.com/uutils/coreutils/pull/2365#discussion_r647849967
    let command: Vec<&str> = match commands.len() {
        0 => {
            let shell: &str = match user_shell {
                Err(_) => default_shell,
                Ok(ref s) => s.as_ref(),
            };
            vec![shell, default_option]
        }
        _ => commands,
    };

    set_context(newroot, &matches);

    assert!(!command.is_empty());
    let pstatus = Command::new(command[0])
        .args(&command[1..])
        .status()
        .unwrap_or_else(|e| {
            // TODO: Exit status:
            // 125 if chroot itself fails
            // 126 if command is found but cannot be invoked
            // 127 if command cannot be found
            crash!(
                1,
                "failed to run command {}: {}",
                command[0].to_string().quote(),
                e
            )
        });

    if pstatus.success() {
        0
    } else {
        pstatus.code().unwrap_or(-1)
    }
}

pub fn uu_app() -> App<'static, 'static> {
    App::new(uucore::util_name())
        .version(crate_version!())
        .about(ABOUT)
        .usage(SYNTAX)
        .arg(
            Arg::with_name(options::NEWROOT)
                .hidden(true)
                .required(true)
                .index(1),
        )
        .arg(
            Arg::with_name(options::USER)
                .short("u")
                .long(options::USER)
                .help("User (ID or name) to switch before running the program")
                .value_name("USER"),
        )
        .arg(
            Arg::with_name(options::GROUP)
                .short("g")
                .long(options::GROUP)
                .help("Group (ID or name) to switch to")
                .value_name("GROUP"),
        )
        .arg(
            Arg::with_name(options::GROUPS)
                .short("G")
                .long(options::GROUPS)
                .help("Comma-separated list of groups to switch to")
                .value_name("GROUP1,GROUP2..."),
        )
        .arg(
            Arg::with_name(options::USERSPEC)
                .long(options::USERSPEC)
                .help(
                    "Colon-separated user and group to switch to. \
                     Same as -u USER -g GROUP. \
                     Userspec has higher preference than -u and/or -g",
                )
                .value_name("USER:GROUP"),
        )
        .arg(
            Arg::with_name(options::COMMAND)
                .hidden(true)
                .multiple(true)
                .index(2),
        )
}

fn set_context(root: &Path, options: &clap::ArgMatches) {
    let userspec_str = options.value_of(options::USERSPEC);
    let user_str = options.value_of(options::USER).unwrap_or_default();
    let group_str = options.value_of(options::GROUP).unwrap_or_default();
    let groups_str = options.value_of(options::GROUPS).unwrap_or_default();
    let userspec = match userspec_str {
        Some(u) => {
            let s: Vec<&str> = u.split(':').collect();
            if s.len() != 2 || s.iter().any(|&spec| spec.is_empty()) {
                crash!(1, "invalid userspec: {}", u.quote())
            };
            s
        }
        None => Vec::new(),
    };

    let (user, group) = if userspec.is_empty() {
        (user_str, group_str)
    } else {
        (userspec[0], userspec[1])
    };

    enter_chroot(root);

    set_groups_from_str(groups_str);
    set_main_group(group);
    set_user(user);
}

fn enter_chroot(root: &Path) {
    std::env::set_current_dir(root).unwrap();
    let err = unsafe {
        chroot(CString::new(".").unwrap().as_bytes_with_nul().as_ptr() as *const libc::c_char)
    };
    if err != 0 {
        crash!(
            1,
            "cannot chroot to {}: {}",
            root.quote(),
            Error::last_os_error()
        )
    };
}

fn set_main_group(group: &str) {
    if !group.is_empty() {
        let group_id = match entries::grp2gid(group) {
            Ok(g) => g,
            _ => crash!(1, "no such group: {}", group.maybe_quote()),
        };
        let err = unsafe { setgid(group_id) };
        if err != 0 {
            crash!(
                1,
                "cannot set gid to {}: {}",
                group_id,
                Error::last_os_error()
            )
        }
    }
}

#[cfg(any(target_vendor = "apple", target_os = "freebsd"))]
fn set_groups(groups: Vec<libc::gid_t>) -> libc::c_int {
    unsafe { setgroups(groups.len() as libc::c_int, groups.as_ptr()) }
}

#[cfg(target_os = "linux")]
fn set_groups(groups: Vec<libc::gid_t>) -> libc::c_int {
    unsafe { setgroups(groups.len() as libc::size_t, groups.as_ptr()) }
}

fn set_groups_from_str(groups: &str) {
    if !groups.is_empty() {
        let groups_vec: Vec<libc::gid_t> = groups
            .split(',')
            .map(|x| match entries::grp2gid(x) {
                Ok(g) => g,
                _ => crash!(1, "no such group: {}", x),
            })
            .collect();
        let err = set_groups(groups_vec);
        if err != 0 {
            crash!(1, "cannot set groups: {}", Error::last_os_error())
        }
    }
}

fn set_user(user: &str) {
    if !user.is_empty() {
        let user_id = entries::usr2uid(user).unwrap();
        let err = unsafe { setuid(user_id as libc::uid_t) };
        if err != 0 {
            crash!(
                1,
                "cannot set user to {}: {}",
                user.maybe_quote(),
                Error::last_os_error()
            )
        }
    }
}
<|endoftext|>


<commit_before>use std::cell::{RefCell, UnsafeCell};
use std::fs;
use std::ops::{Deref, DerefMut};
use std::path::Path;
use std::rc::Rc;
use std::sync::Arc;

use std::io::{Error, Read, Seek, SeekFrom};
use std::fs::File;
use std::io::ErrorKind::{Interrupted, Other, UnexpectedEof};

use super::*;

/// Top-level configuration for the system.
///
/// # Examples
///
/// ```
/// let config = sled::Config::default()
///     .path("/path/to/data".to_owned())
///     .cache_capacity(10_000_000_000)
///     .use_compression(true)
///     .flush_every_ms(Some(1000))
///     .snapshot_after_ops(100_000);
/// ```
#[derive(Debug, Clone)]
pub struct Config {
    inner: Arc<UnsafeCell<ConfigInner>>,
}

unsafe impl Send for Config {}
unsafe impl Sync for Config {}

impl Default for Config {
    fn default() -> Config {
        let now = uptime();
        let nanos = (now.as_secs() * 1_000_000_000) + now.subsec_nanos() as u64;
        let tmp_path = format!("sled.tmp.{}", nanos);
        let inner = Arc::new(UnsafeCell::new(ConfigInner {
            io_bufs: 3,
            io_buf_size: 2 << 22, // 8mb
            blink_fanout: 32,
            page_consolidation_threshold: 10,
            path: tmp_path.to_owned(),
            cache_bits: 6, // 64 shards
            cache_capacity: 1024 * 1024 * 1024, // 1gb
            use_os_cache: true,
            use_compression: true,
            flush_every_ms: Some(500),
            snapshot_after_ops: 1_000_000,
            snapshot_path: None,
            cache_fixup_threshold: 1,
            segment_cleanup_threshold: 0.2,
            min_free_segments: 3,
            zero_copy_storage: false,
            tc: ThreadCache::default(),
            tmp_path: tmp_path.to_owned(),
        }));
        Config {
            inner: inner,
        }
    }
}

impl Deref for Config {
    type Target = ConfigInner;

    fn deref(&self) -> &Self::Target {
        unsafe { &*self.inner.get() }
    }
}

impl DerefMut for Config {
    fn deref_mut(&mut self) -> &mut ConfigInner {
        unsafe { &mut *self.inner.get() }
    }
}

impl Config {
    /// create a new `Tree` based on this configuration
    pub fn tree(&self) -> Tree {
        Tree::new(self.clone())
    }

    /// create a new `LockFreeLog` based on this
    /// configuration
    pub fn log(&self) -> LockFreeLog {
        LockFreeLog::start_system(self.clone())
    }
}

#[derive(Debug, Clone)]
pub struct ConfigInner {
    io_bufs: usize,
    io_buf_size: usize,
    blink_fanout: usize,
    page_consolidation_threshold: usize,
    path: String,
    cache_bits: usize,
    cache_capacity: usize,
    use_os_cache: bool,
    use_compression: bool,
    flush_every_ms: Option<u64>,
    snapshot_after_ops: usize,
    snapshot_path: Option<String>,
    cache_fixup_threshold: usize,
    segment_cleanup_threshold: f64,
    min_free_segments: usize,
    zero_copy_storage: bool,
    tc: ThreadCache<fs::File>,
    tmp_path: String,
}

macro_rules! builder {
    ($(($name:ident, $get:ident, $set:ident, $t:ty, $desc:expr)),*) => {
        $(
            #[doc="Get "]
            #[doc=$desc]
            pub fn $get(&self) -> $t {
                self.$name.clone()
            }

            #[doc="Set "]
            #[doc=$desc]
            pub fn $set(&mut self, to: $t) {
                self.$name = to;
            }

            #[doc="Builder, set "]
            #[doc=$desc]
            pub fn $name(&self, to: $t) -> Config {
                let mut ret = self.clone();
                ret.$name = to;
                Config { inner: Arc::new(UnsafeCell::new(ret))}
            }
        )*
    }
}

impl ConfigInner {
    builder!(
        (io_bufs, get_io_bufs, set_io_bufs, usize, "number of io buffers"),
        (io_buf_size, get_io_buf_size, set_io_buf_size, usize, "size of each io flush buffer. MUST be multiple of 512!"),
        (blink_fanout, get_blink_fanout, set_blink_fanout, usize, "b-link node fanout, minimum of 2"),
        (page_consolidation_threshold, get_page_consolidation_threshold, set_page_consolidation_threshold, usize, "page consolidation threshold"),
        (path, get_path, set_path, String, "path for the main storage file"),
        (cache_bits, get_cache_bits, set_cache_bits, usize, "log base 2 of the number of cache shards"),
        (cache_capacity, get_cache_capacity, set_cache_capacity, usize, "maximum size for the system page cache"),
        (use_os_cache, get_use_os_cache, set_use_os_cache, bool, "whether to use the OS page cache"),
        (use_compression, get_use_compression, set_use_compression, bool, "whether to use zstd compression"),
        (flush_every_ms, get_flush_every_ms, set_flush_every_ms, Option<u64>, "number of ms between IO buffer flushes"),
        (snapshot_after_ops, get_snapshot_after_ops, set_snapshot_after_ops, usize, "number of operations between page table snapshots"),
        (snapshot_path, get_snapshot_path, set_snapshot_path, Option<String>, "snapshot file location"),
        (cache_fixup_threshold, get_cache_fixup_threshold, set_cache_fixup_threshold, usize, "the maximum length of a cached page fragment chain"),
        (segment_cleanup_threshold, get_segment_cleanup_threshold, set_segment_cleanup_threshold, f64, "the proportion of remaining valid pages in the segment"),
        (min_free_segments, get_min_free_segments, set_min_free_segments, usize, "the minimum number of free segments to have on-deck before a compaction occurs"),
        (zero_copy_storage, get_zero_copy_storage, set_zero_copy_storage, bool, "disabling of the log segment copy cleaner")
    );

    /// Retrieve a thread-local file handle to the
    /// configured underlying storage,
    /// or create a new one if this is the first time the
    /// thread is accessing it.
    pub fn cached_file(&self) -> Rc<RefCell<fs::File>> {
        self.tc.get_or_else(|| {
            let path = self.get_path();
            let mut options = fs::OpenOptions::new();
            options.create(true);
            options.read(true);
            options.write(true);

            #[cfg(target_os = "linux")]
            {
                if !self.use_os_cache {
                    use std::os::unix::fs::OpenOptionsExt;
                    options.custom_flags(libc::O_DIRECT);
                    panic!("O_DIRECT support not sussed out yet.");
                }
            }

            options.open(path).unwrap()
        })
    }

    pub fn get_tmp_path(&self) -> String {
        self.tmp_path.clone()
    }

    /// returns the current snapshot file prefix
    pub fn snapshot_prefix(&self) -> String {
        let snapshot_path = self.get_snapshot_path();
        let path = self.get_path();
        snapshot_path.unwrap_or(path)
    }

    /// returns the snapshot file paths for this system
    pub fn get_snapshot_files(&self) -> Vec<String> {
        let mut prefix = self.snapshot_prefix();
        prefix.push_str(".");

        let abs_prefix: String = if Path::new(&prefix).is_absolute() {
            prefix
        } else {
            let mut abs_path =
                std::env::current_dir().expect("could not read current dir, maybe deleted?");
            abs_path.push(prefix.clone());
            abs_path.to_str().unwrap().to_owned()
        };

        let filter = |dir_entry: std::io::Result<std::fs::DirEntry>| if let Ok(de) = dir_entry {
            let path_buf = de.path();
            let path = path_buf.as_path();
            let path_str = path.to_str().unwrap();
            if path_str.starts_with(&abs_prefix) && !path_str.ends_with(".in___motion") {
                Some(path_str.to_owned())
            } else {
                None
            }
        } else {
            None
        };

        let snap_dir = Path::new(&abs_prefix).parent().expect(
            "could not read snapshot directory",
        );

        snap_dir
            .read_dir()
            .expect("could not read snapshot directory")
            .filter_map(filter)
            .collect()
    }

    /// read a segment of log messages. Only call after
    /// pausing segment rewriting on the segment accountant!
    pub fn read_segment(&self, offset: LogID) -> std::io::Result<log::Segment> {
        let segment_header_read = self.read(offset)?;
        if segment_header_read.is_corrupt() {
            return Err(Error::new(Other, "corrupt segment"));
        }
        if segment_header_read.is_zeroed() {
            return Err(Error::new(Other, "empty segment"));
        }
        let (lsn, _, _) = segment_header_read.flush().unwrap();
        assert_eq!(lsn % self.get_io_buf_size() as Lsn, 0);

        let mut buf = vec![];
        let cached_f = self.cached_file();
        let mut f = cached_f.borrow_mut();

        f.seek(SeekFrom::Start(offset))?;
        read_up_to(f, &mut buf, self.get_io_buf_size())?;

        Ok(log::Segment {
            buf: buf,
            lsn: lsn,
            read_offset: HEADER_LEN,
            position: offset,
        })
    }

    /// read a buffer from the disk
    pub fn read(&self, id: LogID) -> std::io::Result<LogRead> {
        let start = clock();
        let cached_f = self.cached_file();
        let mut f = cached_f.borrow_mut();
        f.seek(SeekFrom::Start(id))?;

        let mut valid_buf = [0u8; 1];
        f.read_exact(&mut valid_buf)?;
        let valid = valid_buf[0] == 1;

        let mut lsn_buf = [0u8; 8];
        f.read_exact(&mut lsn_buf)?;
        let lsn: Lsn = unsafe { std::mem::transmute(lsn_buf) };

        let mut len_buf = [0u8; 4];
        f.read_exact(&mut len_buf)?;

        let len32: u32 = unsafe { std::mem::transmute(len_buf) };
        let mut len = len32 as usize;
        let max = self.get_io_buf_size() - HEADER_LEN;
        if len > max {
            #[cfg(feature = "log")]
            error!("log read invalid message length, {} should be <= {}", len, max);
            M.read.measure(clock() - start);
            return Ok(LogRead::Corrupted(len));
        } else if len == 0 && !valid {
            // skip to next record, which starts with 1
            loop {
                let mut byte = [0u8; 1];
                if let Err(e) = f.read_exact(&mut byte) {
                    if e.kind() == UnexpectedEof {
                        // we've hit the end of the file
                        break;
                    }
                    panic!("{:?}", e);
                }
                if byte[0] != 1 {
                    debug_assert_eq!(byte[0], 0);
                    len += 1;
                } else {
                    break;
                }
            }
        }

        if !valid {
            M.read.measure(clock() - start);
            // we're 2 short when we started seeking for a non-zero byte (crc16)
            return Ok(LogRead::Zeroed(len + HEADER_LEN - 2));
        } else if (lsn % self.get_io_buf_size() as Lsn) != (id % self.get_io_buf_size() as LogID) {
            return Ok(LogRead::Corrupted(len + HEADER_LEN - 2));
        }

        let mut crc16_buf = [0u8; 2];
        f.read_exact(&mut crc16_buf)?;

        let mut buf = Vec::with_capacity(len);
        unsafe {
            buf.set_len(len);
        }
        f.read_exact(&mut buf)?;

        let checksum = crc16_arr(&buf);
        if checksum != crc16_buf {
            M.read.measure(clock() - start);
            return Ok(LogRead::Corrupted(len));
        }

        #[cfg(feature = "zstd")]
        let res = {
            if self.config().get_use_compression() {
                let start = clock();
                let res = Ok(LogRead::Flush(lsn, decompress(&*buf, max).unwrap(), len));
                M.decompress.measure(clock() - start);
                res
            } else {
                Ok(LogRead::Flush(lsn, buf, len))
            }
        };

        #[cfg(not(feature = "zstd"))]
        let res = Ok(LogRead::Flush(lsn, buf, len));

        M.read.measure(clock() - start);
        res
    }
}

impl Drop for ConfigInner {
    fn drop(&mut self) {
        let ephemeral = self.get_path() == self.get_tmp_path();
        if !ephemeral {
            return;
        }

        // Our files are temporary, so nuke them.

        let _res = fs::remove_file(self.tmp_path.clone());

        let candidates = self.get_snapshot_files();
        for path in candidates {
            if let Err(_e) = std::fs::remove_file(path) {
                    #[cfg(feature = "log")]
                warn!("failed to remove old snapshot file, maybe snapshot race? {}", _e);
            }
        }
    }
}

fn read_up_to(
    mut f: std::cell::RefMut<File>,
    buf: &mut Vec<u8>,
    max: usize,
) -> std::io::Result<usize> {
    buf.reserve(max);
    unsafe {
        buf.set_len(max);
    }
    let mut len = 0;
    loop {
        match f.read(&mut buf[len..max]) {
            Ok(0) => {
                unsafe {
                    buf.set_len(len);
                }
                return Ok(len);
            }
            Ok(n) => {
                len += n;
            }
            Err(ref e) if e.kind() == Interrupted => {}
            Err(e) => return Err(e),
        }
    }
}
<commit_msg>use /dev/shm for linux temp storage<commit_after>use std::cell::{RefCell, UnsafeCell};
use std::fs;
use std::ops::{Deref, DerefMut};
use std::path::Path;
use std::rc::Rc;
use std::sync::Arc;

use std::io::{Error, Read, Seek, SeekFrom};
use std::fs::File;
use std::io::ErrorKind::{Interrupted, Other, UnexpectedEof};

use super::*;

/// Top-level configuration for the system.
///
/// # Examples
///
/// ```
/// let config = sled::Config::default()
///     .path("/path/to/data".to_owned())
///     .cache_capacity(10_000_000_000)
///     .use_compression(true)
///     .flush_every_ms(Some(1000))
///     .snapshot_after_ops(100_000);
/// ```
#[derive(Debug, Clone)]
pub struct Config {
    inner: Arc<UnsafeCell<ConfigInner>>,
}

unsafe impl Send for Config {}
unsafe impl Sync for Config {}

impl Default for Config {
    fn default() -> Config {
        let now = uptime();
        let nanos = (now.as_secs() * 1_000_000_000) + now.subsec_nanos() as u64;

        // use shared memory for temporary linux files
        #[cfg(target_os = "linux")]
        let tmp_path = format!("/dev/shm/sled.tmp.{}", nanos);

        #[cfg(not(target_os = "linux"))]
        let tmp_path = format!("sled.tmp.{}", nanos);

        let inner = Arc::new(UnsafeCell::new(ConfigInner {
            io_bufs: 3,
            io_buf_size: 2 << 22, // 8mb
            blink_fanout: 32,
            page_consolidation_threshold: 10,
            path: tmp_path.to_owned(),
            cache_bits: 6, // 64 shards
            cache_capacity: 1024 * 1024 * 1024, // 1gb
            use_os_cache: true,
            use_compression: true,
            flush_every_ms: Some(500),
            snapshot_after_ops: 1_000_000,
            snapshot_path: None,
            cache_fixup_threshold: 1,
            segment_cleanup_threshold: 0.2,
            min_free_segments: 3,
            zero_copy_storage: false,
            tc: ThreadCache::default(),
            tmp_path: tmp_path.to_owned(),
        }));
        Config {
            inner: inner,
        }
    }
}

impl Deref for Config {
    type Target = ConfigInner;

    fn deref(&self) -> &Self::Target {
        unsafe { &*self.inner.get() }
    }
}

impl DerefMut for Config {
    fn deref_mut(&mut self) -> &mut ConfigInner {
        unsafe { &mut *self.inner.get() }
    }
}

impl Config {
    /// create a new `Tree` based on this configuration
    pub fn tree(&self) -> Tree {
        Tree::new(self.clone())
    }

    /// create a new `LockFreeLog` based on this
    /// configuration
    pub fn log(&self) -> LockFreeLog {
        LockFreeLog::start_system(self.clone())
    }
}

#[derive(Debug, Clone)]
pub struct ConfigInner {
    io_bufs: usize,
    io_buf_size: usize,
    blink_fanout: usize,
    page_consolidation_threshold: usize,
    path: String,
    cache_bits: usize,
    cache_capacity: usize,
    use_os_cache: bool,
    use_compression: bool,
    flush_every_ms: Option<u64>,
    snapshot_after_ops: usize,
    snapshot_path: Option<String>,
    cache_fixup_threshold: usize,
    segment_cleanup_threshold: f64,
    min_free_segments: usize,
    zero_copy_storage: bool,
    tc: ThreadCache<fs::File>,
    tmp_path: String,
}

macro_rules! builder {
    ($(($name:ident, $get:ident, $set:ident, $t:ty, $desc:expr)),*) => {
        $(
            #[doc="Get "]
            #[doc=$desc]
            pub fn $get(&self) -> $t {
                self.$name.clone()
            }

            #[doc="Set "]
            #[doc=$desc]
            pub fn $set(&mut self, to: $t) {
                self.$name = to;
            }

            #[doc="Builder, set "]
            #[doc=$desc]
            pub fn $name(&self, to: $t) -> Config {
                let mut ret = self.clone();
                ret.$name = to;
                Config { inner: Arc::new(UnsafeCell::new(ret))}
            }
        )*
    }
}

impl ConfigInner {
    builder!(
        (io_bufs, get_io_bufs, set_io_bufs, usize, "number of io buffers"),
        (io_buf_size, get_io_buf_size, set_io_buf_size, usize, "size of each io flush buffer. MUST be multiple of 512!"),
        (blink_fanout, get_blink_fanout, set_blink_fanout, usize, "b-link node fanout, minimum of 2"),
        (page_consolidation_threshold, get_page_consolidation_threshold, set_page_consolidation_threshold, usize, "page consolidation threshold"),
        (path, get_path, set_path, String, "path for the main storage file"),
        (cache_bits, get_cache_bits, set_cache_bits, usize, "log base 2 of the number of cache shards"),
        (cache_capacity, get_cache_capacity, set_cache_capacity, usize, "maximum size for the system page cache"),
        (use_os_cache, get_use_os_cache, set_use_os_cache, bool, "whether to use the OS page cache"),
        (use_compression, get_use_compression, set_use_compression, bool, "whether to use zstd compression"),
        (flush_every_ms, get_flush_every_ms, set_flush_every_ms, Option<u64>, "number of ms between IO buffer flushes"),
        (snapshot_after_ops, get_snapshot_after_ops, set_snapshot_after_ops, usize, "number of operations between page table snapshots"),
        (snapshot_path, get_snapshot_path, set_snapshot_path, Option<String>, "snapshot file location"),
        (cache_fixup_threshold, get_cache_fixup_threshold, set_cache_fixup_threshold, usize, "the maximum length of a cached page fragment chain"),
        (segment_cleanup_threshold, get_segment_cleanup_threshold, set_segment_cleanup_threshold, f64, "the proportion of remaining valid pages in the segment"),
        (min_free_segments, get_min_free_segments, set_min_free_segments, usize, "the minimum number of free segments to have on-deck before a compaction occurs"),
        (zero_copy_storage, get_zero_copy_storage, set_zero_copy_storage, bool, "disabling of the log segment copy cleaner")
    );

    /// Retrieve a thread-local file handle to the
    /// configured underlying storage,
    /// or create a new one if this is the first time the
    /// thread is accessing it.
    pub fn cached_file(&self) -> Rc<RefCell<fs::File>> {
        self.tc.get_or_else(|| {
            let path = self.get_path();
            let mut options = fs::OpenOptions::new();
            options.create(true);
            options.read(true);
            options.write(true);

            #[cfg(target_os = "linux")]
            {
                if !self.use_os_cache {
                    use std::os::unix::fs::OpenOptionsExt;
                    options.custom_flags(libc::O_DIRECT);
                    panic!("O_DIRECT support not sussed out yet.");
                }
            }

            options.open(path).unwrap()
        })
    }

    pub fn get_tmp_path(&self) -> String {
        self.tmp_path.clone()
    }

    /// returns the current snapshot file prefix
    pub fn snapshot_prefix(&self) -> String {
        let snapshot_path = self.get_snapshot_path();
        let path = self.get_path();
        snapshot_path.unwrap_or(path)
    }

    /// returns the snapshot file paths for this system
    pub fn get_snapshot_files(&self) -> Vec<String> {
        let mut prefix = self.snapshot_prefix();
        prefix.push_str(".");

        let abs_prefix: String = if Path::new(&prefix).is_absolute() {
            prefix
        } else {
            let mut abs_path =
                std::env::current_dir().expect("could not read current dir, maybe deleted?");
            abs_path.push(prefix.clone());
            abs_path.to_str().unwrap().to_owned()
        };

        let filter = |dir_entry: std::io::Result<std::fs::DirEntry>| if let Ok(de) = dir_entry {
            let path_buf = de.path();
            let path = path_buf.as_path();
            let path_str = path.to_str().unwrap();
            if path_str.starts_with(&abs_prefix) && !path_str.ends_with(".in___motion") {
                Some(path_str.to_owned())
            } else {
                None
            }
        } else {
            None
        };

        let snap_dir = Path::new(&abs_prefix).parent().expect(
            "could not read snapshot directory",
        );

        snap_dir
            .read_dir()
            .expect("could not read snapshot directory")
            .filter_map(filter)
            .collect()
    }

    /// read a segment of log messages. Only call after
    /// pausing segment rewriting on the segment accountant!
    pub fn read_segment(&self, offset: LogID) -> std::io::Result<log::Segment> {
        let segment_header_read = self.read(offset)?;
        if segment_header_read.is_corrupt() {
            return Err(Error::new(Other, "corrupt segment"));
        }
        if segment_header_read.is_zeroed() {
            return Err(Error::new(Other, "empty segment"));
        }
        let (lsn, _, _) = segment_header_read.flush().unwrap();
        assert_eq!(lsn % self.get_io_buf_size() as Lsn, 0);

        let mut buf = vec![];
        let cached_f = self.cached_file();
        let mut f = cached_f.borrow_mut();

        f.seek(SeekFrom::Start(offset))?;
        read_up_to(f, &mut buf, self.get_io_buf_size())?;

        Ok(log::Segment {
            buf: buf,
            lsn: lsn,
            read_offset: HEADER_LEN,
            position: offset,
        })
    }

    /// read a buffer from the disk
    pub fn read(&self, id: LogID) -> std::io::Result<LogRead> {
        let start = clock();
        let cached_f = self.cached_file();
        let mut f = cached_f.borrow_mut();
        f.seek(SeekFrom::Start(id))?;

        let mut valid_buf = [0u8; 1];
        f.read_exact(&mut valid_buf)?;
        let valid = valid_buf[0] == 1;

        let mut lsn_buf = [0u8; 8];
        f.read_exact(&mut lsn_buf)?;
        let lsn: Lsn = unsafe { std::mem::transmute(lsn_buf) };

        let mut len_buf = [0u8; 4];
        f.read_exact(&mut len_buf)?;

        let len32: u32 = unsafe { std::mem::transmute(len_buf) };
        let mut len = len32 as usize;
        let max = self.get_io_buf_size() - HEADER_LEN;
        if len > max {
            #[cfg(feature = "log")]
            error!("log read invalid message length, {} should be <= {}", len, max);
            M.read.measure(clock() - start);
            return Ok(LogRead::Corrupted(len));
        } else if len == 0 && !valid {
            // skip to next record, which starts with 1
            loop {
                let mut byte = [0u8; 1];
                if let Err(e) = f.read_exact(&mut byte) {
                    if e.kind() == UnexpectedEof {
                        // we've hit the end of the file
                        break;
                    }
                    panic!("{:?}", e);
                }
                if byte[0] != 1 {
                    debug_assert_eq!(byte[0], 0);
                    len += 1;
                } else {
                    break;
                }
            }
        }

        if !valid {
            M.read.measure(clock() - start);
            // we're 2 short when we started seeking for a non-zero byte (crc16)
            return Ok(LogRead::Zeroed(len + HEADER_LEN - 2));
        } else if (lsn % self.get_io_buf_size() as Lsn) != (id % self.get_io_buf_size() as LogID) {
            return Ok(LogRead::Corrupted(len + HEADER_LEN - 2));
        }

        let mut crc16_buf = [0u8; 2];
        f.read_exact(&mut crc16_buf)?;

        let mut buf = Vec::with_capacity(len);
        unsafe {
            buf.set_len(len);
        }
        f.read_exact(&mut buf)?;

        let checksum = crc16_arr(&buf);
        if checksum != crc16_buf {
            M.read.measure(clock() - start);
            return Ok(LogRead::Corrupted(len));
        }

        #[cfg(feature = "zstd")]
        let res = {
            if self.config().get_use_compression() {
                let start = clock();
                let res = Ok(LogRead::Flush(lsn, decompress(&*buf, max).unwrap(), len));
                M.decompress.measure(clock() - start);
                res
            } else {
                Ok(LogRead::Flush(lsn, buf, len))
            }
        };

        #[cfg(not(feature = "zstd"))]
        let res = Ok(LogRead::Flush(lsn, buf, len));

        M.read.measure(clock() - start);
        res
    }
}

impl Drop for ConfigInner {
    fn drop(&mut self) {
        let ephemeral = self.get_path() == self.get_tmp_path();
        if !ephemeral {
            return;
        }

        // Our files are temporary, so nuke them.

        let _res = fs::remove_file(self.tmp_path.clone());

        let candidates = self.get_snapshot_files();
        for path in candidates {
            if let Err(_e) = std::fs::remove_file(path) {
                    #[cfg(feature = "log")]
                warn!("failed to remove old snapshot file, maybe snapshot race? {}", _e);
            }
        }
    }
}

fn read_up_to(
    mut f: std::cell::RefMut<File>,
    buf: &mut Vec<u8>,
    max: usize,
) -> std::io::Result<usize> {
    buf.reserve(max);
    unsafe {
        buf.set_len(max);
    }
    let mut len = 0;
    loop {
        match f.read(&mut buf[len..max]) {
            Ok(0) => {
                unsafe {
                    buf.set_len(len);
                }
                return Ok(len);
            }
            Ok(n) => {
                len += n;
            }
            Err(ref e) if e.kind() == Interrupted => {}
            Err(e) => return Err(e),
        }
    }
}
<|endoftext|>