Skip to main content

rustfs_filemeta/filemeta/
validation.rs

1// Copyright 2024 RustFS Team
2//
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
15use super::*;
16
17impl FileMeta {
18    /// Check if the metadata format is compatible
19    pub fn is_compatible_with_meta(&self) -> bool {
20        // Check version compatibility
21        if self.meta_ver != XL_META_VERSION {
22            return false;
23        }
24
25        // For compatibility, we allow versions with different types
26        // Just check basic structure validity
27        true
28    }
29
30    /// Validate metadata integrity
31    pub fn validate_integrity(&self) -> Result<()> {
32        // Check if versions are sorted by modification time
33        if !self.is_sorted_by_mod_time() {
34            return Err(Error::other("versions not sorted by modification time"));
35        }
36
37        // Validate inline data if present
38        self.data.validate()?;
39
40        Ok(())
41    }
42
43    /// Check if versions are sorted by modification time (newest first)
44    pub(crate) fn is_sorted_by_mod_time(&self) -> bool {
45        if self.versions.len() <= 1 {
46            return true;
47        }
48
49        for i in 1..self.versions.len() {
50            let prev_time = self.versions[i - 1].header.mod_time;
51            let curr_time = self.versions[i].header.mod_time;
52
53            match (prev_time, curr_time) {
54                (Some(prev), Some(curr)) => {
55                    if prev < curr {
56                        return false;
57                    }
58                }
59                (None, Some(_)) => return false,
60                _ => continue,
61            }
62        }
63
64        true
65    }
66
67    /// Get statistics about versions
68    pub fn get_version_stats(&self) -> VersionStats {
69        let mut stats = VersionStats {
70            total_versions: self.versions.len(),
71            ..Default::default()
72        };
73
74        for version in &self.versions {
75            match version.header.version_type {
76                VersionType::Object => stats.object_versions += 1,
77                VersionType::Delete => stats.delete_markers += 1,
78                VersionType::Invalid | VersionType::Legacy => stats.invalid_versions += 1,
79            }
80
81            if version.header.free_version() {
82                stats.free_versions += 1;
83            }
84        }
85
86        stats
87    }
88}
89
90#[derive(Debug, Default, Clone)]
91pub struct VersionStats {
92    pub total_versions: usize,
93    pub object_versions: usize,
94    pub delete_markers: usize,
95    pub invalid_versions: usize,
96    pub free_versions: usize,
97}
98
99impl FileMetaVersionHeader {
100    pub fn is_valid(&self) -> bool {
101        // Check if version type is valid
102        if !self.version_type.valid() {
103            return false;
104        }
105
106        // Check if modification time is reasonable (not too far in the future)
107        if let Some(mod_time) = self.mod_time {
108            let now = OffsetDateTime::now_utc();
109            let future_limit = now + time::Duration::hours(24); // Allow 24 hours in future
110            if mod_time > future_limit {
111                return false;
112            }
113        }
114
115        // Check erasure coding parameters
116        if self.has_ec() && (self.ec_n == 0 || self.ec_m == 0 || self.ec_m < self.ec_n) {
117            return false;
118        }
119
120        true
121    }
122}
123
124/// Enhanced version statistics with more detailed information
125#[derive(Debug, Default, Clone)]
126pub struct DetailedVersionStats {
127    pub total_versions: usize,
128    pub object_versions: usize,
129    pub delete_markers: usize,
130    pub invalid_versions: usize,
131    pub legacy_versions: usize,
132    pub free_versions: usize,
133    pub versions_with_data_dir: usize,
134    pub versions_with_inline_data: usize,
135    pub total_size: i64,
136    pub latest_mod_time: Option<OffsetDateTime>,
137}
138
139impl FileMeta {
140    /// Get detailed statistics about versions
141    pub fn get_detailed_version_stats(&self) -> DetailedVersionStats {
142        let mut stats = DetailedVersionStats {
143            total_versions: self.versions.len(),
144            ..Default::default()
145        };
146
147        for version in &self.versions {
148            match version.header.version_type {
149                VersionType::Object => {
150                    stats.object_versions += 1;
151                    if let Ok(ver) = FileMetaVersion::try_from(version.meta.as_slice())
152                        && let Some(obj) = &ver.object
153                    {
154                        stats.total_size += obj.size;
155                        if obj.uses_data_dir() {
156                            stats.versions_with_data_dir += 1;
157                        }
158                        if obj.inlinedata() {
159                            stats.versions_with_inline_data += 1;
160                        }
161                    }
162                }
163                VersionType::Delete => stats.delete_markers += 1,
164                VersionType::Legacy => stats.legacy_versions += 1,
165                VersionType::Invalid => stats.invalid_versions += 1,
166            }
167
168            if version.header.free_version() {
169                stats.free_versions += 1;
170            }
171
172            if stats.latest_mod_time.is_none()
173                || (version.header.mod_time.is_some() && version.header.mod_time > stats.latest_mod_time)
174            {
175                stats.latest_mod_time = version.header.mod_time;
176            }
177        }
178
179        stats
180    }
181}