Skip to main content

smb_server_proto/
types.rs

1//! Shared value types: NT status codes, FILETIME and attribute flags.
2
3/// NT status code carried in the SMB header status field.
4///
5/// Values follow the `NTSTATUS` numbering used throughout [MS-SMB]/[MS-CIFS];
6/// when the client did not negotiate `CAP_STATUS32` the dispatcher converts
7/// these to DOS class/code pairs.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9pub struct Status(pub u32);
10
11impl Status {
12    /// The operation completed successfully.
13    pub const SUCCESS: Status = Status(0x0000_0000);
14    /// Generic failure.
15    pub const UNSUCCESSFUL: Status = Status(0xC000_0001);
16    /// Function not implemented by this server.
17    pub const NOT_IMPLEMENTED: Status = Status(0xC000_0002);
18    /// A create traversed a symbolic link ([MS-SMB2] §2.2.2.2.1).
19    pub const STOPPED_ON_SYMLINK: Status = Status(0x8000_002D);
20    /// End of file reached during a read.
21    pub const END_OF_FILE: Status = Status(0xC000_0011);
22    /// The supplied handle (FID) is not valid on this session/tree.
23    pub const INVALID_HANDLE: Status = Status(0xC000_0008);
24    /// A parameter passed to the operation was invalid.
25    pub const INVALID_PARAMETER: Status = Status(0xC000_000D);
26    /// Requested file does not exist.
27    pub const NO_SUCH_FILE: Status = Status(0xC000_000F);
28    /// Supplied credentials are invalid.
29    pub const LOGON_FAILURE: Status = Status(0xC000_006D);
30    /// More processing is required (multi-leg authentication).
31    pub const MORE_PROCESSING_REQUIRED: Status = Status(0xC000_0016);
32    /// The object name was not found.
33    pub const OBJECT_NAME_NOT_FOUND: Status = Status(0xC000_0034);
34    /// An object with the given name already exists.
35    pub const OBJECT_NAME_COLLISION: Status = Status(0xC000_0035);
36    /// A component of the path was not found.
37    pub const OBJECT_PATH_NOT_FOUND: Status = Status(0xC000_003A);
38    /// The user session referenced has been deleted/invalidated.
39    pub const USER_SESSION_DELETED: Status = Status(0xC000_0203);
40    /// The file is temporarily unavailable (e.g. a live persistent handle).
41    pub const FILE_NOT_AVAILABLE: Status = Status(0xC000_0467);
42    /// The request is not accepted in the current lease-break state.
43    pub const REQUEST_NOT_ACCEPTED: Status = Status(0xC000_00D0);
44    /// Access to the object was denied.
45    pub const ACCESS_DENIED: Status = Status(0xC000_0022);
46    /// The output buffer was too small to hold the requested data.
47    pub const BUFFER_TOO_SMALL: Status = Status(0xC000_0023);
48    /// The requested item was not found (e.g. a non-DFS path for a referral).
49    pub const NOT_FOUND: Status = Status(0xC000_0225);
50    /// Another handle holds a conflicting open on the object.
51    pub const SHARING_VIOLATION: Status = Status(0xC000_0043);
52    /// Deletion has already been requested for this object.
53    pub const DELETE_PENDING: Status = Status(0xC000_0056);
54    /// Cannot remove a directory that still contains entries.
55    pub const DIRECTORY_NOT_EMPTY: Status = Status(0xC000_0101);
56    /// An operation expected a file but found a directory (or vice versa).
57    pub const NOT_A_DIRECTORY: Status = Status(0xC000_0103);
58    /// An operation expected a directory but found a file.
59    pub const FILE_IS_A_DIRECTORY: Status = Status(0xC000_00BA);
60    /// The share name is unknown to the server.
61    pub const BAD_NETWORK_NAME: Status = Status(0xC000_00CC);
62    /// The tree connect referenced by TreeId no longer exists on the server.
63    pub const NETWORK_NAME_DELETED: Status = Status(0xC000_00C9);
64    /// A prior open was force-closed by an application-instance failover.
65    pub const FILE_FORCED_CLOSED: Status = Status(0xC000_00B6);
66    /// The handle was closed out from under this open (app-instance failover).
67    pub const FILE_CLOSED: Status = Status(0xC000_0128);
68    /// The request was cancelled by the client.
69    pub const CANCELLED: Status = Status(0xC000_0120);
70    /// A pending CHANGE_NOTIFY was completed because its handle was closed
71    /// ([MS-SMB2] §3.3.5.10). Severity is success, so clients treat the
72    /// CHANGE_NOTIFY response as a (non-error) notification.
73    pub const NOTIFY_CLEANUP: Status = Status(0x0000_010B);
74    /// A byte-range lock request conflicts with an existing lock
75    /// ([MS-SMB2] §2.2.26, STATUS_LOCK_NOT_GRANTED).
76    pub const LOCK_NOT_GRANTED: Status = Status(0xC000_0055);
77    /// A read/write overlapped a conflicting byte-range lock ([MS-SMB2] §2.2.26).
78    pub const FILE_LOCK_CONFLICT: Status = Status(0xC000_0054);
79    /// SMB 3.1.1 negotiate carried no hash algorithm the server supports
80    /// ([MS-SMB2] §3.3.5.4).
81    pub const SMB_NO_PREAUTH_INTEGRITY_HASH_OVERLAP: Status = Status(0xC05D_0000);
82    /// The operation is being processed asynchronously; an interim response
83    /// ([MS-SMB2] §3.3.4.2) precedes the final one.
84    pub const PENDING: Status = Status(0x0000_0103);
85    /// No more matching directory entries (warning severity).
86    pub const NO_MORE_FILES: Status = Status(0x8000_0006);
87    /// The request specified an unknown device or request type.
88    pub const INVALID_DEVICE_REQUEST: Status = Status(0xC000_0010);
89
90    /// Raw NTSTATUS value.
91    pub fn raw(self) -> u32 {
92        self.0
93    }
94
95    /// True when the severity bits indicate an error (severity 3).
96    pub fn is_error(self) -> bool {
97        self.0 & 0xC000_0000 == 0xC000_0000
98    }
99
100    /// Map to a DOS error `(class, code)` for clients without
101    /// `CAP_STATUS32` ([MS-CIFS] §2.2.2.4 error classes and codes).
102    pub fn to_dos(self) -> (u8, u8) {
103        match self {
104            Self::SUCCESS => (0x00, 0x00),
105            Self::OBJECT_NAME_NOT_FOUND | Self::NO_SUCH_FILE | Self::OBJECT_PATH_NOT_FOUND => {
106                (0x01, 0x52) // ERRbadfile
107            }
108            Self::OBJECT_NAME_COLLISION => (0x01, 0x50), // ERRfilexists
109            Self::ACCESS_DENIED => (0x01, 0x05),         // ERRnoaccess
110            Self::SHARING_VIOLATION => (0x01, 0x20),     // ERRshare
111            Self::DIRECTORY_NOT_EMPTY => (0x01, 0x05),
112            Self::END_OF_FILE | Self::NO_MORE_FILES => (0x01, 0x12), // ERRnomorefiles
113            _ => (0x02, 0x01),                                       // ERRSRV/ERRgeneral
114        }
115    }
116}
117
118/// Windows FILETIME: 100-nanosecond intervals since 1601-01-01 UTC
119/// ([MS-DTYP] §2.3.3). Zero denotes "not set".
120#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
121pub struct FileTime(pub u64);
122
123/// Seconds between the Windows epoch (1601) and the Unix epoch (1970).
124pub const WIN_EPOCH_SECS: i64 = 11_644_473_600;
125
126impl FileTime {
127    /// Current system time as FILETIME.
128    pub fn now() -> Self {
129        use std::time::{SystemTime, UNIX_EPOCH};
130        let d = SystemTime::now()
131            .duration_since(UNIX_EPOCH)
132            .unwrap_or_default();
133        Self::from_unix(d.as_secs() as i64, d.subsec_nanos())
134    }
135
136    /// Build from Unix seconds + nanoseconds.
137    pub fn from_unix(secs: i64, nanos: u32) -> Self {
138        let t = (secs + WIN_EPOCH_SECS) as i128 * 10_000_000 + nanos as i128 / 100;
139        FileTime(if t <= 0 { 0 } else { t as u64 })
140    }
141
142    /// Convert to Unix `(seconds, nanoseconds)`; saturates at the epoch.
143    pub fn to_unix(self) -> (i64, u32) {
144        if self.0 == 0 {
145            return (0, 0);
146        }
147        let total = self.0 as i128 - WIN_EPOCH_SECS as i128 * 10_000_000;
148        if total < 0 {
149            return (0, 0);
150        }
151        ((total / 10_000_000) as i64, ((total % 10_000_000) * 100) as u32)
152    }
153}
154
155/// File attribute flags ([MS-SMB] §2.2.1.2.1 / [MS-FSCC] §2.6).
156#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
157pub struct AttrFlags(pub u32);
158
159impl AttrFlags {
160    /// Read-only file.
161    pub const READONLY: u32 = 0x0000_0001;
162    /// Hidden file.
163    pub const HIDDEN: u32 = 0x0000_0002;
164    /// System file.
165    pub const SYSTEM: u32 = 0x0000_0004;
166    /// Directory.
167    pub const DIRECTORY: u32 = 0x0000_0010;
168    /// Archive bit.
169    pub const ARCHIVE: u32 = 0x0000_0020;
170    /// Normal file (no other attributes).
171    pub const NORMAL: u32 = 0x0000_0080;
172
173    /// Wrap raw attribute bits.
174    pub fn new(bits: u32) -> Self {
175        AttrFlags(bits)
176    }
177
178    /// True when [`AttrFlags::DIRECTORY`] is set.
179    pub fn is_directory(self) -> bool {
180        self.0 & Self::DIRECTORY != 0
181    }
182}
183
184/// NT create disposition ([MS-SMB] §2.2.4.9.1 `CreateDisposition`,
185/// originally [MS-CIFS] §2.2.4.63.1).
186#[derive(Debug, Clone, Copy, PartialEq, Eq)]
187pub enum Disposition {
188    /// Overwrite existing or create new (attributes superseded).
189    Supersede,
190    /// Open only; fail when missing.
191    Open,
192    /// Create only; fail when present.
193    Create,
194    /// Open existing or create new.
195    OpenIf,
196    /// Open existing and truncate; fail when missing.
197    Overwrite,
198    /// Truncate-or-create.
199    OverwriteIf,
200}
201
202impl Disposition {
203    /// From the wire `CreateDisposition` field.
204    pub fn from_u32(v: u32) -> Option<Self> {
205        Some(match v {
206            0 => Self::Supersede,
207            1 => Self::Open,
208            2 => Self::Create,
209            3 => Self::OpenIf,
210            4 => Self::Overwrite,
211            5 => Self::OverwriteIf,
212            _ => return None,
213        })
214    }
215}
216
217/// Result of a create/open operation (`FILE_*` action codes).
218#[derive(Debug, Clone, Copy, PartialEq, Eq)]
219pub enum CreateAction {
220    /// Existing file was replaced (`FILE_SUPERSEDED`).
221    Superseded,
222    /// Existing file was opened (`FILE_OPENED`).
223    Opened,
224    /// New file was created (`FILE_CREATED`).
225    Created,
226    /// Existing file was overwritten (`FILE_OVERWRITTEN`).
227    Overwritten,
228}