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}