mcp-pacemaker 1.3.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,678 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.ComponentModel;
4
+ using System.Globalization;
5
+ using System.IO;
6
+ using System.Runtime.InteropServices;
7
+ using System.Security.AccessControl;
8
+ using System.Security.Cryptography;
9
+ using System.Security.Principal;
10
+ using System.Text;
11
+ using System.Web.Script.Serialization;
12
+ using Microsoft.Win32.SafeHandles;
13
+
14
+ internal sealed class PoolingNativeFiles
15
+ {
16
+ private const int MaximumFileSize = 1024 * 1024;
17
+ private const int MaximumRequestSize = 1400000;
18
+ private const uint ReadAccess = 0x20001;
19
+ private const uint WriteData = 2;
20
+ private const uint OpenReparsePoint = 0x00200000;
21
+ private const uint DaclProtected = 0x1000;
22
+ private const ControlFlags SupportedDescriptorControl =
23
+ ControlFlags.SelfRelative |
24
+ ControlFlags.DiscretionaryAclPresent |
25
+ ControlFlags.DiscretionaryAclProtected |
26
+ ControlFlags.DiscretionaryAclAutoInherited |
27
+ ControlFlags.SystemAclPresent |
28
+ ControlFlags.SystemAclProtected |
29
+ ControlFlags.SystemAclAutoInherited;
30
+ private readonly UTF8Encoding utf8 = new UTF8Encoding(false, true);
31
+ private readonly JavaScriptSerializer json = new JavaScriptSerializer
32
+ {
33
+ MaxJsonLength = MaximumRequestSize,
34
+ RecursionLimit = 8,
35
+ };
36
+
37
+ /// <summary>Runs one bounded native file operation.</summary>
38
+ /// <param name="action">inspect-access, stage, or move-no-replace.</param>
39
+ /// <param name="sourcePath">Source file path.</param>
40
+ /// <returns>A JSON identity, revision, security fingerprint, and size.</returns>
41
+ public string Run(string action, string sourcePath)
42
+ {
43
+ if (action == "inspect-access")
44
+ {
45
+ using (var handle = this.Open(sourcePath, ReadAccess, 1))
46
+ {
47
+ return this.json.Serialize(this.Snapshot(handle).Result);
48
+ }
49
+ }
50
+
51
+ var destination = Environment.GetEnvironmentVariable("MCP_POOL_TEMP");
52
+ if (string.IsNullOrEmpty(destination))
53
+ {
54
+ throw new ArgumentException();
55
+ }
56
+
57
+ var request = this.ReadRequest(action == "stage");
58
+ if (action == "stage")
59
+ {
60
+ return this.json.Serialize(this.Stage(sourcePath, destination, request));
61
+ }
62
+
63
+ if (action == "move-no-replace")
64
+ {
65
+ return this.json.Serialize(this.Move(sourcePath, destination, request));
66
+ }
67
+
68
+ throw new ArgumentException();
69
+ }
70
+
71
+ [DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode, SetLastError = true)]
72
+ private static extern SafeFileHandle CreateFile(string path, uint access, uint share,
73
+ IntPtr security, uint disposition, uint attributes, IntPtr template);
74
+
75
+ [DllImport("kernel32.dll", SetLastError = true)]
76
+ private static extern SafeFileHandle ReOpenFile(SafeFileHandle handle, uint access, uint share, uint flags);
77
+
78
+ [DllImport("kernel32.dll", SetLastError = true)]
79
+ private static extern bool GetFileInformationByHandle(SafeFileHandle handle, out FileInformation info);
80
+
81
+ [DllImport("kernel32.dll", SetLastError = true)]
82
+ private static extern uint GetFileType(SafeFileHandle handle);
83
+
84
+ [DllImport("kernel32.dll", SetLastError = true)]
85
+ private static extern bool SetFilePointerEx(SafeFileHandle handle, long distance, out long position, uint method);
86
+
87
+ [DllImport("kernel32.dll", SetLastError = true)]
88
+ private static extern bool ReadFile(SafeFileHandle handle, byte[] bytes, uint count, out uint read, IntPtr overlapped);
89
+
90
+ [DllImport("kernel32.dll", SetLastError = true)]
91
+ private static extern bool WriteFile(SafeFileHandle handle, byte[] bytes, uint count, out uint written, IntPtr overlapped);
92
+
93
+ [DllImport("kernel32.dll", SetLastError = true)]
94
+ private static extern bool FlushFileBuffers(SafeFileHandle handle);
95
+
96
+ [DllImport("kernel32.dll", EntryPoint = "MoveFileExW", CharSet = CharSet.Unicode, SetLastError = true)]
97
+ private static extern bool MoveFileEx(string source, string destination, uint flags);
98
+
99
+ [DllImport("advapi32.dll")]
100
+ private static extern uint GetSecurityInfo(SafeFileHandle handle, uint type, uint sections,
101
+ out IntPtr owner, out IntPtr group, out IntPtr dacl, out IntPtr sacl, out IntPtr descriptor);
102
+
103
+ [DllImport("advapi32.dll", SetLastError = true)]
104
+ private static extern bool GetSecurityDescriptorControl(IntPtr descriptor, out ushort control, out uint revision);
105
+
106
+ [DllImport("advapi32.dll")]
107
+ private static extern uint GetLengthSid(IntPtr sid);
108
+
109
+ [DllImport("kernel32.dll")]
110
+ private static extern IntPtr LocalFree(IntPtr memory);
111
+
112
+ private Request ReadRequest(bool stage)
113
+ {
114
+ using (var input = Console.OpenStandardInput())
115
+ using (var buffer = new MemoryStream())
116
+ {
117
+ var chunk = new byte[4096];
118
+ int count;
119
+ while ((count = input.Read(chunk, 0, chunk.Length)) != 0)
120
+ {
121
+ if (buffer.Length + count > MaximumRequestSize)
122
+ {
123
+ throw new ArgumentException();
124
+ }
125
+
126
+ buffer.Write(chunk, 0, count);
127
+ }
128
+
129
+ var root = this.json.DeserializeObject(this.utf8.GetString(buffer.ToArray())) as Dictionary<string, object>;
130
+ if (root == null ||
131
+ root.Count != (stage ? 2 : 1) ||
132
+ !root.ContainsKey("expected"))
133
+ {
134
+ throw new ArgumentException();
135
+ }
136
+
137
+ var expected = root["expected"] as Dictionary<string, object>;
138
+ if (expected == null ||
139
+ expected.Count != 3)
140
+ {
141
+ throw new ArgumentException();
142
+ }
143
+
144
+ var request = new Request
145
+ {
146
+ Identity = this.RequiredString(expected, "identity"),
147
+ Revision = this.RequiredString(expected, "revision"),
148
+ Security = this.RequiredString(expected, "security"),
149
+ };
150
+ if (!this.IsHexHash(request.Revision) ||
151
+ !this.IsHexHash(request.Security) ||
152
+ request.Identity.Length > 80)
153
+ {
154
+ throw new ArgumentException();
155
+ }
156
+
157
+ if (stage)
158
+ {
159
+ var encoded = this.RequiredString(root, "bytes", true);
160
+ request.Bytes = Convert.FromBase64String(encoded);
161
+ if (request.Bytes.Length > MaximumFileSize ||
162
+ Convert.ToBase64String(request.Bytes) != encoded)
163
+ {
164
+ throw new ArgumentException();
165
+ }
166
+
167
+ this.utf8.GetString(request.Bytes);
168
+ }
169
+
170
+ return request;
171
+ }
172
+ }
173
+
174
+ private string RequiredString(Dictionary<string, object> value, string name, bool allowEmpty = false)
175
+ {
176
+ object item;
177
+ if (!value.TryGetValue(name, out item))
178
+ {
179
+ throw new ArgumentException();
180
+ }
181
+
182
+ var text = item as string;
183
+ if (text == null ||
184
+ (!allowEmpty && text.Length == 0))
185
+ {
186
+ throw new ArgumentException();
187
+ }
188
+
189
+ return text;
190
+ }
191
+
192
+ private bool IsHexHash(string value)
193
+ {
194
+ if (value.Length != 64)
195
+ {
196
+ return false;
197
+ }
198
+
199
+ foreach (char character in value)
200
+ {
201
+ bool hexadecimal = (character >= '0' && character <= '9') ||
202
+ (character >= 'a' && character <= 'f');
203
+ if (!hexadecimal)
204
+ {
205
+ return false;
206
+ }
207
+ }
208
+
209
+ return true;
210
+ }
211
+
212
+ private Dictionary<string, object> Stage(string source, string destination, Request request)
213
+ {
214
+ // A parent DeleteChild grant must not bypass source data-write denial.
215
+ using (var sourceHandle = this.Open(source, ReadAccess | WriteData, 1))
216
+ {
217
+ var original = this.Snapshot(sourceHandle);
218
+ this.CheckExpected(original, request);
219
+ var accepted = original.Security;
220
+ using (var identity = WindowsIdentity.GetCurrent())
221
+ {
222
+ var creator = identity.User;
223
+ var sourceOwner = new SecurityIdentifier(accepted.Owner, 0);
224
+ bool retainsOwner = sourceOwner.Equals(creator) ||
225
+ sourceOwner.Equals(identity.Owner);
226
+ if (!retainsOwner)
227
+ {
228
+ this.CheckSimpleOwnerChange(accepted);
229
+ using (var authority = ReOpenFile(sourceHandle, 0x60000, 7, 0))
230
+ {
231
+ this.CheckHandle(authority);
232
+ }
233
+
234
+ accepted.Owner = new byte[creator.BinaryLength];
235
+ creator.GetBinaryForm(accepted.Owner, 0);
236
+ }
237
+ }
238
+
239
+ using (var candidate = this.Create(destination, accepted, original.Attributes))
240
+ {
241
+ var empty = this.Snapshot(candidate);
242
+ var expectedSecurity = this.SecurityHash(accepted, original.Attributes);
243
+ if (empty.Size != 0 ||
244
+ empty.Fingerprint != expectedSecurity)
245
+ {
246
+ throw new PoolingPolicyException();
247
+ }
248
+
249
+ // The owned handle denies other writers/deleters until verification completes.
250
+ this.CheckExpected(this.Snapshot(sourceHandle), request);
251
+ this.Write(candidate, request.Bytes);
252
+ var written = this.Snapshot(candidate);
253
+ if (written.Fingerprint != expectedSecurity ||
254
+ written.Revision != this.Hash(request.Bytes))
255
+ {
256
+ throw new PoolingConflictException();
257
+ }
258
+
259
+ this.CheckExpected(this.Snapshot(sourceHandle), request);
260
+ return written.Result;
261
+ }
262
+ }
263
+ }
264
+
265
+ private Dictionary<string, object> Move(string source, string destination, Request request)
266
+ {
267
+ using (var handle = this.Open(source, ReadAccess, 5))
268
+ {
269
+ var original = this.Snapshot(handle);
270
+ this.CheckExpected(original, request);
271
+ this.Require(MoveFileEx(source, destination, 0));
272
+ using (var moved = this.Open(destination, ReadAccess, 5))
273
+ {
274
+ var result = this.Snapshot(moved);
275
+ this.CheckExpected(result, request);
276
+ this.CheckExpected(this.Snapshot(handle), request);
277
+ return result.Result;
278
+ }
279
+ }
280
+ }
281
+
282
+ private void CheckExpected(SnapshotState state, Request expected)
283
+ {
284
+ if (state.Identity != expected.Identity ||
285
+ state.Revision != expected.Revision ||
286
+ state.Fingerprint != expected.Security)
287
+ {
288
+ throw new PoolingConflictException();
289
+ }
290
+ }
291
+
292
+ private SafeFileHandle Open(string path, uint access, uint share)
293
+ {
294
+ var handle = CreateFile(path, access, share, IntPtr.Zero, 3, OpenReparsePoint, IntPtr.Zero);
295
+ this.CheckHandle(handle);
296
+ return handle;
297
+ }
298
+
299
+ private void CheckHandle(SafeFileHandle handle)
300
+ {
301
+ if (handle.IsInvalid)
302
+ {
303
+ int error = Marshal.GetLastWin32Error();
304
+ handle.Dispose();
305
+ throw new Win32Exception(error);
306
+ }
307
+ }
308
+
309
+ private void Require(bool success)
310
+ {
311
+ if (!success)
312
+ {
313
+ throw new Win32Exception(Marshal.GetLastWin32Error());
314
+ }
315
+ }
316
+
317
+ private SnapshotState Snapshot(SafeFileHandle handle)
318
+ {
319
+ FileInformation info;
320
+ this.Require(GetFileInformationByHandle(handle, out info));
321
+ bool regular = GetFileType(handle) == 1 &&
322
+ info.Links == 1 &&
323
+ info.SizeHigh == 0 &&
324
+ info.SizeLow <= MaximumFileSize &&
325
+ (info.Attributes & ~0xa0U) == 0;
326
+ if (!regular)
327
+ {
328
+ throw new PoolingPolicyException();
329
+ }
330
+
331
+ var security = this.ReadSecurity(handle);
332
+ this.CheckPolicy(security);
333
+ var content = this.Read(handle);
334
+ FileInformation after;
335
+ this.Require(GetFileInformationByHandle(handle, out after));
336
+ var fingerprint = this.SecurityHash(security, info.Attributes);
337
+ bool changedWhileReading = after.SizeHigh != 0 ||
338
+ after.SizeLow != content.Length ||
339
+ after.SizeLow != info.SizeLow ||
340
+ after.Links != 1 ||
341
+ after.Attributes != info.Attributes ||
342
+ after.WriteTimeHigh != info.WriteTimeHigh ||
343
+ after.WriteTimeLow != info.WriteTimeLow ||
344
+ this.SecurityHash(this.ReadSecurity(handle), after.Attributes) != fingerprint;
345
+ if (changedWhileReading)
346
+ {
347
+ throw new PoolingConflictException();
348
+ }
349
+
350
+ return new SnapshotState
351
+ {
352
+ Identity = string.Format(CultureInfo.InvariantCulture, "{0:x8}:{1:x8}{2:x8}",
353
+ info.Volume, info.IndexHigh, info.IndexLow),
354
+ Revision = this.Hash(content),
355
+ Fingerprint = fingerprint,
356
+ Security = security,
357
+ Attributes = info.Attributes,
358
+ Size = content.Length,
359
+ };
360
+ }
361
+
362
+ private byte[] Read(SafeFileHandle handle)
363
+ {
364
+ long position;
365
+ this.Require(SetFilePointerEx(handle, 0, out position, 0));
366
+ using (var buffer = new MemoryStream())
367
+ {
368
+ var chunk = new byte[4096];
369
+ while (true)
370
+ {
371
+ uint read;
372
+ this.Require(ReadFile(handle, chunk, (uint)chunk.Length, out read, IntPtr.Zero));
373
+ if (read == 0)
374
+ {
375
+ return buffer.ToArray();
376
+ }
377
+
378
+ if (buffer.Length + read > MaximumFileSize)
379
+ {
380
+ throw new PoolingPolicyException();
381
+ }
382
+
383
+ buffer.Write(chunk, 0, (int)read);
384
+ }
385
+ }
386
+ }
387
+
388
+ private void Write(SafeFileHandle handle, byte[] bytes)
389
+ {
390
+ long position;
391
+ this.Require(SetFilePointerEx(handle, 0, out position, 0));
392
+ uint written;
393
+ this.Require(WriteFile(handle, bytes, (uint)bytes.Length, out written, IntPtr.Zero));
394
+ if (written != bytes.Length)
395
+ {
396
+ throw new IOException();
397
+ }
398
+
399
+ this.Require(FlushFileBuffers(handle));
400
+ }
401
+
402
+ private SecurityState ReadSecurity(SafeFileHandle handle)
403
+ {
404
+ IntPtr owner, group, dacl, sacl, descriptor;
405
+ uint status = GetSecurityInfo(handle, 1, 0x1f7, out owner, out group, out dacl, out sacl, out descriptor);
406
+ if (status != 0)
407
+ {
408
+ throw new Win32Exception((int)status);
409
+ }
410
+
411
+ try
412
+ {
413
+ ushort control;
414
+ uint revision;
415
+ this.Require(GetSecurityDescriptorControl(descriptor, out control, out revision));
416
+ return new SecurityState
417
+ {
418
+ Owner = this.CopySid(owner),
419
+ Group = this.CopySid(group),
420
+ Dacl = this.CopyAcl(dacl),
421
+ Labels = this.CopyAcl(sacl),
422
+ Control = control,
423
+ };
424
+ }
425
+ finally
426
+ {
427
+ LocalFree(descriptor);
428
+ }
429
+ }
430
+
431
+ private byte[] CopySid(IntPtr sid)
432
+ {
433
+ if (sid == IntPtr.Zero)
434
+ {
435
+ throw new PoolingPolicyException();
436
+ }
437
+
438
+ var bytes = new byte[GetLengthSid(sid)];
439
+ Marshal.Copy(sid, bytes, 0, bytes.Length);
440
+ return bytes;
441
+ }
442
+
443
+ private byte[] CopyAcl(IntPtr acl)
444
+ {
445
+ if (acl == IntPtr.Zero)
446
+ {
447
+ return null;
448
+ }
449
+
450
+ int length = (ushort)Marshal.ReadInt16(acl, 2);
451
+ var bytes = new byte[length];
452
+ Marshal.Copy(acl, bytes, 0, bytes.Length);
453
+ return bytes;
454
+ }
455
+
456
+ private void CheckPolicy(SecurityState state)
457
+ {
458
+ bool supportedDaclControl = (state.Control & 4) != 0 &&
459
+ (state.Control & ~(ushort)SupportedDescriptorControl) == 0;
460
+ if (state.Dacl == null ||
461
+ !supportedDaclControl)
462
+ {
463
+ throw new PoolingPolicyException();
464
+ }
465
+
466
+ var dacl = new RawAcl(state.Dacl, 0);
467
+ foreach (GenericAce ace in dacl)
468
+ {
469
+ var common = ace as CommonAce;
470
+ bool supported = common != null &&
471
+ !common.IsCallback &&
472
+ (common.AceQualifier == AceQualifier.AccessAllowed ||
473
+ common.AceQualifier == AceQualifier.AccessDenied);
474
+ if (!supported)
475
+ {
476
+ throw new PoolingPolicyException();
477
+ }
478
+ }
479
+
480
+ if (state.Labels == null)
481
+ {
482
+ return;
483
+ }
484
+
485
+ var labels = new RawAcl(state.Labels, 0);
486
+ if (labels.Count > 1)
487
+ {
488
+ throw new PoolingPolicyException();
489
+ }
490
+
491
+ foreach (GenericAce ace in labels)
492
+ {
493
+ var bytes = new byte[ace.BinaryLength];
494
+ ace.GetBinaryForm(bytes, 0);
495
+ if ((int)ace.AceType != 17 ||
496
+ bytes.Length != 20)
497
+ {
498
+ throw new PoolingPolicyException();
499
+ }
500
+
501
+ var sid = new SecurityIdentifier(bytes, 8).Value;
502
+ int mask = BitConverter.ToInt32(bytes, 4);
503
+ bool supported = (sid == "S-1-16-4096" || sid == "S-1-16-8192") &&
504
+ mask > 0 &&
505
+ (mask & ~7) == 0 &&
506
+ (ace.AceFlags & ~AceFlags.Inherited) == 0;
507
+ if (!supported)
508
+ {
509
+ throw new PoolingPolicyException();
510
+ }
511
+ }
512
+ }
513
+
514
+ private void CheckSimpleOwnerChange(SecurityState state)
515
+ {
516
+ foreach (GenericAce ace in new RawAcl(state.Dacl, 0))
517
+ {
518
+ var common = ace as CommonAce;
519
+ bool simple = common != null &&
520
+ !common.IsCallback &&
521
+ common.AceQualifier == AceQualifier.AccessAllowed &&
522
+ !common.SecurityIdentifier.Value.StartsWith("S-1-3-", StringComparison.Ordinal);
523
+ if (!simple)
524
+ {
525
+ throw new PoolingPolicyException();
526
+ }
527
+ }
528
+ }
529
+
530
+ private SafeFileHandle Create(string path, SecurityState security, uint attributes)
531
+ {
532
+ var owner = new SecurityIdentifier(security.Owner, 0);
533
+ var group = new SecurityIdentifier(security.Group, 0);
534
+ var flags = ControlFlags.DiscretionaryAclPresent |
535
+ ControlFlags.SelfRelative |
536
+ (ControlFlags)(security.Control & DaclProtected);
537
+ var labels = security.Labels == null ? null : new RawAcl(security.Labels, 0);
538
+ if (security.Labels != null)
539
+ {
540
+ // Default the audit portion so Windows inherits folder rules alongside the supplied label.
541
+ flags |= ControlFlags.SystemAclPresent | ControlFlags.SystemAclDefaulted;
542
+ }
543
+
544
+ // SACL protection would suppress the destination folder's inherited auditing.
545
+ var descriptor = new RawSecurityDescriptor(flags, owner, group, labels, new RawAcl(security.Dacl, 0));
546
+ var bytes = new byte[descriptor.BinaryLength];
547
+ descriptor.GetBinaryForm(bytes, 0);
548
+ var pinned = GCHandle.Alloc(bytes, GCHandleType.Pinned);
549
+ var nativeAttributes = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(SecurityAttributes)));
550
+ try
551
+ {
552
+ var value = new SecurityAttributes
553
+ {
554
+ Length = Marshal.SizeOf(typeof(SecurityAttributes)),
555
+ Descriptor = pinned.AddrOfPinnedObject(),
556
+ Inherit = 0,
557
+ };
558
+ Marshal.StructureToPtr(value, nativeAttributes, false);
559
+ var handle = CreateFile(path, ReadAccess | WriteData, 1, nativeAttributes, 1, attributes, IntPtr.Zero);
560
+ this.CheckHandle(handle);
561
+ return handle;
562
+ }
563
+ finally
564
+ {
565
+ Marshal.FreeHGlobal(nativeAttributes);
566
+ pinned.Free();
567
+ }
568
+ }
569
+
570
+ private string SecurityHash(SecurityState security, uint attributes)
571
+ {
572
+ using (var buffer = new MemoryStream())
573
+ using (var writer = new BinaryWriter(buffer))
574
+ {
575
+ writer.Write("mcp-access-v1");
576
+ this.WritePart(writer, security.Owner);
577
+ this.WritePart(writer, security.Group);
578
+ writer.Write((uint)(security.Control & DaclProtected));
579
+ this.WritePart(writer, security.Dacl);
580
+ this.WritePart(writer, security.Labels);
581
+ writer.Write(attributes);
582
+ writer.Flush();
583
+ return this.Hash(buffer.ToArray());
584
+ }
585
+ }
586
+
587
+ private void WritePart(BinaryWriter writer, byte[] bytes)
588
+ {
589
+ writer.Write(bytes == null ? -1 : bytes.Length);
590
+ if (bytes != null)
591
+ {
592
+ writer.Write(bytes);
593
+ }
594
+ }
595
+
596
+ private string Hash(byte[] bytes)
597
+ {
598
+ using (var hash = SHA256.Create())
599
+ {
600
+ return BitConverter.ToString(hash.ComputeHash(bytes)).Replace("-", string.Empty).ToLowerInvariant();
601
+ }
602
+ }
603
+
604
+ [StructLayout(LayoutKind.Sequential)]
605
+ private struct SecurityAttributes
606
+ {
607
+ public int Length;
608
+ public IntPtr Descriptor;
609
+ public int Inherit;
610
+ }
611
+
612
+ [StructLayout(LayoutKind.Sequential)]
613
+ private struct FileInformation
614
+ {
615
+ public uint Attributes;
616
+ public uint CreationTimeLow;
617
+ public uint CreationTimeHigh;
618
+ public uint AccessTimeLow;
619
+ public uint AccessTimeHigh;
620
+ public uint WriteTimeLow;
621
+ public uint WriteTimeHigh;
622
+ public uint Volume;
623
+ public uint SizeHigh;
624
+ public uint SizeLow;
625
+ public uint Links;
626
+ public uint IndexHigh;
627
+ public uint IndexLow;
628
+ }
629
+
630
+ private sealed class Request
631
+ {
632
+ public string Identity { get; set; }
633
+ public string Revision { get; set; }
634
+ public string Security { get; set; }
635
+ public byte[] Bytes { get; set; }
636
+ }
637
+
638
+ private sealed class SecurityState
639
+ {
640
+ public byte[] Owner { get; set; }
641
+ public byte[] Group { get; set; }
642
+ public byte[] Dacl { get; set; }
643
+ public byte[] Labels { get; set; }
644
+ public ushort Control { get; set; }
645
+ }
646
+
647
+ private sealed class SnapshotState
648
+ {
649
+ public string Identity { get; set; }
650
+ public string Revision { get; set; }
651
+ public string Fingerprint { get; set; }
652
+ public SecurityState Security { get; set; }
653
+ public uint Attributes { get; set; }
654
+ public int Size { get; set; }
655
+
656
+ public Dictionary<string, object> Result
657
+ {
658
+ get
659
+ {
660
+ return new Dictionary<string, object>
661
+ {
662
+ { "identity", this.Identity },
663
+ { "revision", this.Revision },
664
+ { "security", this.Fingerprint },
665
+ { "size", this.Size },
666
+ };
667
+ }
668
+ }
669
+ }
670
+ }
671
+
672
+ internal sealed class PoolingConflictException : Exception
673
+ {
674
+ }
675
+
676
+ internal sealed class PoolingPolicyException : Exception
677
+ {
678
+ }