| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684 |
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.Linq;
- using Renci.SshNet.Common;
- namespace Renci.SshNet.Sftp
- {
- /// <summary>
- /// Contains SFTP file attributes.
- /// </summary>
- public class SftpFileAttributes
- {
- #pragma warning disable IDE1006 // Naming Styles
- #pragma warning disable SA1310 // Field names should not contain underscore
- private const uint S_IFMT = 0xF000; // bitmask for the file type bitfields
- private const uint S_IFSOCK = 0xC000; // socket
- private const uint S_IFLNK = 0xA000; // symbolic link
- private const uint S_IFREG = 0x8000; // regular file
- private const uint S_IFBLK = 0x6000; // block device
- private const uint S_IFDIR = 0x4000; // directory
- private const uint S_IFCHR = 0x2000; // character device
- private const uint S_IFIFO = 0x1000; // FIFO
- private const uint S_ISUID = 0x0800; // set UID bit
- private const uint S_ISGID = 0x0400; // set-group-ID bit (see below)
- private const uint S_ISVTX = 0x0200; // sticky bit (see below)
- private const uint S_IRUSR = 0x0100; // owner has read permission
- private const uint S_IWUSR = 0x0080; // owner has write permission
- private const uint S_IXUSR = 0x0040; // owner has execute permission
- private const uint S_IRGRP = 0x0020; // group has read permission
- private const uint S_IWGRP = 0x0010; // group has write permission
- private const uint S_IXGRP = 0x0008; // group has execute permission
- private const uint S_IROTH = 0x0004; // others have read permission
- private const uint S_IWOTH = 0x0002; // others have write permission
- private const uint S_IXOTH = 0x0001; // others have execute permission
- #pragma warning restore SA1310 // Field names should not contain underscore
- #pragma warning restore IDE1006 // Naming Styles
- private readonly DateTime _originalLastAccessTimeUtc;
- private readonly DateTime _originalLastWriteTimeUtc;
- private readonly long _originalSize;
- private readonly int _originalUserId;
- private readonly int _originalGroupId;
- private readonly uint _originalPermissions;
- private readonly IDictionary<string, string> _originalExtensions;
- private bool _isBitFiledsBitSet;
- private bool _isUIDBitSet;
- private bool _isGroupIDBitSet;
- private bool _isStickyBitSet;
- internal bool IsLastAccessTimeChanged
- {
- get { return _originalLastAccessTimeUtc != LastAccessTimeUtc; }
- }
- internal bool IsLastWriteTimeChanged
- {
- get { return _originalLastWriteTimeUtc != LastWriteTimeUtc; }
- }
- internal bool IsSizeChanged
- {
- get { return _originalSize != Size; }
- }
- internal bool IsUserIdChanged
- {
- get { return _originalUserId != UserId; }
- }
- internal bool IsGroupIdChanged
- {
- get { return _originalGroupId != GroupId; }
- }
- internal bool IsPermissionsChanged
- {
- get { return _originalPermissions != Permissions; }
- }
- internal bool IsExtensionsChanged
- {
- get { return _originalExtensions != null && Extensions != null && !_originalExtensions.SequenceEqual(Extensions); }
- }
- /// <summary>
- /// Gets or sets the local time the current file or directory was last accessed.
- /// </summary>
- /// <value>
- /// The local time that the current file or directory was last accessed.
- /// </value>
- public DateTime LastAccessTime
- {
- get
- {
- return ToLocalTime(LastAccessTimeUtc);
- }
- set
- {
- LastAccessTimeUtc = ToUniversalTime(value);
- }
- }
- /// <summary>
- /// Gets or sets the local time when the current file or directory was last written to.
- /// </summary>
- /// <value>
- /// The local time the current file was last written.
- /// </value>
- public DateTime LastWriteTime
- {
- get
- {
- return ToLocalTime(LastWriteTimeUtc);
- }
- set
- {
- LastWriteTimeUtc = ToUniversalTime(value);
- }
- }
- /// <summary>
- /// Gets or sets the UTC time the current file or directory was last accessed.
- /// </summary>
- /// <value>
- /// The UTC time that the current file or directory was last accessed.
- /// </value>
- public DateTime LastAccessTimeUtc { get; set; }
- /// <summary>
- /// Gets or sets the UTC time when the current file or directory was last written to.
- /// </summary>
- /// <value>
- /// The UTC time the current file was last written.
- /// </value>
- public DateTime LastWriteTimeUtc { get; set; }
- /// <summary>
- /// Gets or sets the size, in bytes, of the current file.
- /// </summary>
- /// <value>
- /// The size of the current file in bytes.
- /// </value>
- public long Size { get; set; }
- /// <summary>
- /// Gets or sets file user id.
- /// </summary>
- /// <value>
- /// File user id.
- /// </value>
- public int UserId { get; set; }
- /// <summary>
- /// Gets or sets file group id.
- /// </summary>
- /// <value>
- /// File group id.
- /// </value>
- public int GroupId { get; set; }
- /// <summary>
- /// Gets a value indicating whether file represents a socket.
- /// </summary>
- /// <value>
- /// <see langword="true"/> if file represents a socket; otherwise, <see langword="false"/>.
- /// </value>
- public bool IsSocket { get; private set; }
- /// <summary>
- /// Gets a value indicating whether file represents a symbolic link.
- /// </summary>
- /// <value>
- /// <see langword="true"/> if file represents a symbolic link; otherwise, <see langword="false"/>.
- /// </value>
- public bool IsSymbolicLink { get; private set; }
- /// <summary>
- /// Gets a value indicating whether file represents a regular file.
- /// </summary>
- /// <value>
- /// <see langword="true"/> if file represents a regular file; otherwise, <see langword="false"/>.
- /// </value>
- public bool IsRegularFile { get; private set; }
- /// <summary>
- /// Gets a value indicating whether file represents a block device.
- /// </summary>
- /// <value>
- /// <see langword="true"/> if file represents a block device; otherwise, <see langword="false"/>.
- /// </value>
- public bool IsBlockDevice { get; private set; }
- /// <summary>
- /// Gets a value indicating whether file represents a directory.
- /// </summary>
- /// <value>
- /// <see langword="true"/> if file represents a directory; otherwise, <see langword="false"/>.
- /// </value>
- public bool IsDirectory { get; private set; }
- /// <summary>
- /// Gets a value indicating whether file represents a character device.
- /// </summary>
- /// <value>
- /// <see langword="true"/> if file represents a character device; otherwise, <see langword="false"/>.
- /// </value>
- public bool IsCharacterDevice { get; private set; }
- /// <summary>
- /// Gets a value indicating whether file represents a named pipe.
- /// </summary>
- /// <value>
- /// <see langword="true"/> if file represents a named pipe; otherwise, <see langword="false"/>.
- /// </value>
- public bool IsNamedPipe { get; private set; }
- /// <summary>
- /// Gets or sets a value indicating whether the owner can read from this file.
- /// </summary>
- /// <value>
- /// <see langword="true"/> if owner can read from this file; otherwise, <see langword="false"/>.
- /// </value>
- public bool OwnerCanRead { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether the owner can write into this file.
- /// </summary>
- /// <value>
- /// <see langword="true"/> if owner can write into this file; otherwise, <see langword="false"/>.
- /// </value>
- public bool OwnerCanWrite { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether the owner can execute this file.
- /// </summary>
- /// <value>
- /// <see langword="true"/> if owner can execute this file; otherwise, <see langword="false"/>.
- /// </value>
- public bool OwnerCanExecute { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether the group members can read from this file.
- /// </summary>
- /// <value>
- /// <see langword="true"/> if group members can read from this file; otherwise, <see langword="false"/>.
- /// </value>
- public bool GroupCanRead { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether the group members can write into this file.
- /// </summary>
- /// <value>
- /// <see langword="true"/> if group members can write into this file; otherwise, <see langword="false"/>.
- /// </value>
- public bool GroupCanWrite { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether the group members can execute this file.
- /// </summary>
- /// <value>
- /// <see langword="true"/> if group members can execute this file; otherwise, <see langword="false"/>.
- /// </value>
- public bool GroupCanExecute { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether the others can read from this file.
- /// </summary>
- /// <value>
- /// <see langword="true"/> if others can read from this file; otherwise, <see langword="false"/>.
- /// </value>
- public bool OthersCanRead { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether the others can write into this file.
- /// </summary>
- /// <value>
- /// <see langword="true"/> if others can write into this file; otherwise, <see langword="false"/>.
- /// </value>
- public bool OthersCanWrite { get; set; }
- /// <summary>
- /// Gets or sets a value indicating whether the others can execute this file.
- /// </summary>
- /// <value>
- /// <see langword="true"/> if others can execute this file; otherwise, <see langword="false"/>.
- /// </value>
- public bool OthersCanExecute { get; set; }
- /// <summary>
- /// Gets the extensions.
- /// </summary>
- /// <value>
- /// The extensions.
- /// </value>
- public IDictionary<string, string> Extensions { get; private set; }
- internal uint Permissions
- {
- get
- {
- uint permission = 0;
- if (_isBitFiledsBitSet)
- {
- permission |= S_IFMT;
- }
- if (IsSocket)
- {
- permission |= S_IFSOCK;
- }
- if (IsSymbolicLink)
- {
- permission |= S_IFLNK;
- }
- if (IsRegularFile)
- {
- permission |= S_IFREG;
- }
- if (IsBlockDevice)
- {
- permission |= S_IFBLK;
- }
- if (IsDirectory)
- {
- permission |= S_IFDIR;
- }
- if (IsCharacterDevice)
- {
- permission |= S_IFCHR;
- }
- if (IsNamedPipe)
- {
- permission |= S_IFIFO;
- }
- if (_isUIDBitSet)
- {
- permission |= S_ISUID;
- }
- if (_isGroupIDBitSet)
- {
- permission |= S_ISGID;
- }
- if (_isStickyBitSet)
- {
- permission |= S_ISVTX;
- }
- if (OwnerCanRead)
- {
- permission |= S_IRUSR;
- }
- if (OwnerCanWrite)
- {
- permission |= S_IWUSR;
- }
- if (OwnerCanExecute)
- {
- permission |= S_IXUSR;
- }
- if (GroupCanRead)
- {
- permission |= S_IRGRP;
- }
- if (GroupCanWrite)
- {
- permission |= S_IWGRP;
- }
- if (GroupCanExecute)
- {
- permission |= S_IXGRP;
- }
- if (OthersCanRead)
- {
- permission |= S_IROTH;
- }
- if (OthersCanWrite)
- {
- permission |= S_IWOTH;
- }
- if (OthersCanExecute)
- {
- permission |= S_IXOTH;
- }
- return permission;
- }
- private set
- {
- _isBitFiledsBitSet = (value & S_IFMT) == S_IFMT;
- IsSocket = (value & S_IFSOCK) == S_IFSOCK;
- IsSymbolicLink = (value & S_IFLNK) == S_IFLNK;
- IsRegularFile = (value & S_IFREG) == S_IFREG;
- IsBlockDevice = (value & S_IFBLK) == S_IFBLK;
- IsDirectory = (value & S_IFDIR) == S_IFDIR;
- IsCharacterDevice = (value & S_IFCHR) == S_IFCHR;
- IsNamedPipe = (value & S_IFIFO) == S_IFIFO;
- _isUIDBitSet = (value & S_ISUID) == S_ISUID;
- _isGroupIDBitSet = (value & S_ISGID) == S_ISGID;
- _isStickyBitSet = (value & S_ISVTX) == S_ISVTX;
- OwnerCanRead = (value & S_IRUSR) == S_IRUSR;
- OwnerCanWrite = (value & S_IWUSR) == S_IWUSR;
- OwnerCanExecute = (value & S_IXUSR) == S_IXUSR;
- GroupCanRead = (value & S_IRGRP) == S_IRGRP;
- GroupCanWrite = (value & S_IWGRP) == S_IWGRP;
- GroupCanExecute = (value & S_IXGRP) == S_IXGRP;
- OthersCanRead = (value & S_IROTH) == S_IROTH;
- OthersCanWrite = (value & S_IWOTH) == S_IWOTH;
- OthersCanExecute = (value & S_IXOTH) == S_IXOTH;
- }
- }
- private SftpFileAttributes()
- {
- }
- internal SftpFileAttributes(DateTime lastAccessTimeUtc, DateTime lastWriteTimeUtc, long size, int userId, int groupId, uint permissions, IDictionary<string, string> extensions)
- {
- LastAccessTimeUtc = _originalLastAccessTimeUtc = lastAccessTimeUtc;
- LastWriteTimeUtc = _originalLastWriteTimeUtc = lastWriteTimeUtc;
- Size = _originalSize = size;
- UserId = _originalUserId = userId;
- GroupId = _originalGroupId = groupId;
- Permissions = _originalPermissions = permissions;
- Extensions = _originalExtensions = extensions;
- }
- /// <summary>
- /// Sets the permissions.
- /// </summary>
- /// <param name="mode">The mode.</param>
- public void SetPermissions(short mode)
- {
- if (mode is < 0 or > 999)
- {
- throw new ArgumentOutOfRangeException(nameof(mode));
- }
- var modeBytes = mode.ToString(CultureInfo.InvariantCulture).PadLeft(3, '0').ToCharArray();
- var permission = ((modeBytes[0] & 0x0F) * 8 * 8) + ((modeBytes[1] & 0x0F) * 8) + (modeBytes[2] & 0x0F);
- OwnerCanRead = (permission & S_IRUSR) == S_IRUSR;
- OwnerCanWrite = (permission & S_IWUSR) == S_IWUSR;
- OwnerCanExecute = (permission & S_IXUSR) == S_IXUSR;
- GroupCanRead = (permission & S_IRGRP) == S_IRGRP;
- GroupCanWrite = (permission & S_IWGRP) == S_IWGRP;
- GroupCanExecute = (permission & S_IXGRP) == S_IXGRP;
- OthersCanRead = (permission & S_IROTH) == S_IROTH;
- OthersCanWrite = (permission & S_IWOTH) == S_IWOTH;
- OthersCanExecute = (permission & S_IXOTH) == S_IXOTH;
- }
- /// <summary>
- /// Returns a byte array representing the current <see cref="SftpFileAttributes"/>.
- /// </summary>
- /// <returns>
- /// A byte array representing the current <see cref="SftpFileAttributes"/>.
- /// </returns>
- public byte[] GetBytes()
- {
- using (var stream = new SshDataStream(4))
- {
- uint flag = 0;
- if (IsSizeChanged && IsRegularFile)
- {
- flag |= 0x00000001;
- }
- if (IsUserIdChanged || IsGroupIdChanged)
- {
- flag |= 0x00000002;
- }
- if (IsPermissionsChanged)
- {
- flag |= 0x00000004;
- }
- if (IsLastAccessTimeChanged || IsLastWriteTimeChanged)
- {
- flag |= 0x00000008;
- }
- if (IsExtensionsChanged)
- {
- flag |= 0x80000000;
- }
- stream.Write(flag);
- if (IsSizeChanged && IsRegularFile)
- {
- stream.Write((ulong)Size);
- }
- if (IsUserIdChanged || IsGroupIdChanged)
- {
- stream.Write((uint)UserId);
- stream.Write((uint)GroupId);
- }
- if (IsPermissionsChanged)
- {
- stream.Write(Permissions);
- }
- if (IsLastAccessTimeChanged || IsLastWriteTimeChanged)
- {
- var time = (uint)((LastAccessTimeUtc.ToFileTimeUtc() / 10000000) - 11644473600);
- stream.Write(time);
- time = (uint)((LastWriteTimeUtc.ToFileTimeUtc() / 10000000) - 11644473600);
- stream.Write(time);
- }
- if (IsExtensionsChanged)
- {
- foreach (var item in Extensions)
- {
- /*
- * TODO: we write as ASCII but read as UTF8 !!!
- */
- stream.Write(item.Key, SshData.Ascii);
- stream.Write(item.Value, SshData.Ascii);
- }
- }
- return stream.ToArray();
- }
- }
- internal static readonly SftpFileAttributes Empty = new SftpFileAttributes();
- internal static SftpFileAttributes FromBytes(SshDataStream stream)
- {
- const uint SSH_FILEXFER_ATTR_SIZE = 0x00000001;
- const uint SSH_FILEXFER_ATTR_UIDGID = 0x00000002;
- const uint SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004;
- const uint SSH_FILEXFER_ATTR_ACMODTIME = 0x00000008;
- const uint SSH_FILEXFER_ATTR_EXTENDED = 0x80000000;
- var flag = stream.ReadUInt32();
- long size = -1;
- var userId = -1;
- var groupId = -1;
- uint permissions = 0;
- DateTime accessTime;
- DateTime modifyTime;
- Dictionary<string, string> extensions = null;
- if ((flag & SSH_FILEXFER_ATTR_SIZE) == SSH_FILEXFER_ATTR_SIZE)
- {
- size = (long)stream.ReadUInt64();
- }
- if ((flag & SSH_FILEXFER_ATTR_UIDGID) == SSH_FILEXFER_ATTR_UIDGID)
- {
- userId = (int)stream.ReadUInt32();
- groupId = (int)stream.ReadUInt32();
- }
- if ((flag & SSH_FILEXFER_ATTR_PERMISSIONS) == SSH_FILEXFER_ATTR_PERMISSIONS)
- {
- permissions = stream.ReadUInt32();
- }
- if ((flag & SSH_FILEXFER_ATTR_ACMODTIME) == SSH_FILEXFER_ATTR_ACMODTIME)
- {
- // The incoming times are "Unix times", so they're already in UTC. We need to preserve that
- // to avoid losing information in a local time conversion during the "fall back" hour in DST.
- var time = stream.ReadUInt32();
- accessTime = DateTime.FromFileTimeUtc((time + 11644473600) * 10000000);
- time = stream.ReadUInt32();
- modifyTime = DateTime.FromFileTimeUtc((time + 11644473600) * 10000000);
- }
- else
- {
- accessTime = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);
- modifyTime = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);
- }
- if ((flag & SSH_FILEXFER_ATTR_EXTENDED) == SSH_FILEXFER_ATTR_EXTENDED)
- {
- var extendedCount = (int)stream.ReadUInt32();
- extensions = new Dictionary<string, string>(extendedCount);
- for (var i = 0; i < extendedCount; i++)
- {
- var extensionName = stream.ReadString(SshData.Utf8);
- var extensionData = stream.ReadString(SshData.Utf8);
- extensions.Add(extensionName, extensionData);
- }
- }
- return new SftpFileAttributes(accessTime, modifyTime, size, userId, groupId, permissions, extensions);
- }
- internal static SftpFileAttributes FromBytes(byte[] buffer)
- {
- using (var stream = new SshDataStream(buffer))
- {
- return FromBytes(stream);
- }
- }
- private static DateTime ToLocalTime(DateTime value)
- {
- DateTime result;
- if (value == DateTime.MinValue)
- {
- result = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Local);
- }
- else
- {
- result = value.ToLocalTime();
- }
- return result;
- }
- private static DateTime ToUniversalTime(DateTime value)
- {
- DateTime result;
- if (value == DateTime.MinValue)
- {
- result = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);
- }
- else
- {
- result = value.ToUniversalTime();
- }
- return result;
- }
- }
- }
|