diff mbox series

[2/2] smbinfo: rewrite in python

Message ID 20191014170738.21724-1-aaptel@suse.com (mailing list archive)
State New, archived
Headers show
Series [1/2] smbinfo.rst: document new `keys` command | expand

Commit Message

Aurélien Aptel Oct. 14, 2019, 5:07 p.m. UTC
Signed-off-by: Aurelien Aptel <aaptel@suse.com>
---
 Makefile.am |    3 +-
 smbinfo     |  779 +++++++++++++++++++++++++++++++++++
 smbinfo.c   | 1296 -----------------------------------------------------------
 3 files changed, 780 insertions(+), 1298 deletions(-)
 create mode 100755 smbinfo
 delete mode 100644 smbinfo.c

Comments

Aurélien Aptel Oct. 14, 2019, 5:13 p.m. UTC | #1
Note that I haven't tested the `quota` and `list-snapshots` sub-commands
as I don't know how to set that up easily on my Windows Server vm (let
me know how if you know, thx) so please give it a try, these are
untested.

Cheers,
ronnie sahlberg Oct. 15, 2019, 5:29 a.m. UTC | #2
Very nice, thanks for doing this!

Does this make the other python tools we have in cifs-utils obsolete?
I don't think so.

I think we should keep both smbinfo as well as the dedicated tools.
Leave smbinfo as a generic read and dump the info to the screen
utility for any/all types of info,
but additionally have specialized tools that are much more complex for
some specific tasks.

What comes to my mind here would be specialized tools for quota,
security descriptors, snapshots to start with.
These tools would be much more specialized than smbinfo and also
provide command line arguments (and optional GUI)
to set and mutate the object they operate on.
I.e. do a lot of things that would possibly be outside the scope of smbinfo.

For quota for example I could imagine it having an optional GUI as
well as command line arguments to also SET the quota.
Maybe having integration with winbind to allow mapping SIDs to/from
Account Names,
possibly even having a configuration file so that a hypothetical
smb2-quota --check   would check the quota limits and provide
alerts/emails based on the configuration file.

I.e. full blown utility to manage the quota as well as a "--check"
command that can be run as a cron job.


For snapshots it would be nice with a dedicated tool, with optional
GUI, that would list all mounted cifs filesystems,
and possibly also read additional server/shares from a configuration
file that would allow you to fully manage snapshots, including
mounting and unmounting them.
I.e. everything you can do from windows explorer you should be able to
do from this tool.
Of course we are not nearly there yet but we have to start from
somewhere and it is just a matter of continue to incrementally improve
these tools.


We could of course do this to smbinfo too but I think it would be
better to keep these specialized tools as separate programs
even if there is some functionality overlap between them and smbinfo.


regards
ronnie sahlberg

On Tue, Oct 15, 2019 at 3:19 AM Aurélien Aptel <aaptel@suse.com> wrote:
>
>
> Note that I haven't tested the `quota` and `list-snapshots` sub-commands
> as I don't know how to set that up easily on my Windows Server vm (let
> me know how if you know, thx) so please give it a try, these are
> untested.
>
> Cheers,
> --
> Aurélien Aptel / SUSE Labs Samba Team
> GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
> SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
> GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)
Pavel Shilovsky Dec. 13, 2019, 12:33 a.m. UTC | #3
пн, 14 окт. 2019 г. в 10:07, Aurelien Aptel <aaptel@suse.com>:
>
> Signed-off-by: Aurelien Aptel <aaptel@suse.com>
> ---
>  Makefile.am |    3 +-
>  smbinfo     |  779 +++++++++++++++++++++++++++++++++++
>  smbinfo.c   | 1296 -----------------------------------------------------------
>  3 files changed, 780 insertions(+), 1298 deletions(-)
>  create mode 100755 smbinfo
>  delete mode 100644 smbinfo.c
>
> diff --git a/Makefile.am b/Makefile.am
> index 8291b99..33a420c 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -80,8 +80,7 @@ setcifsacl.rst: setcifsacl.rst.in
>  endif
>
>  if CONFIG_SMBINFO
> -bin_PROGRAMS += smbinfo
> -smbinfo_SOURCES = smbinfo.c
> +bin_SCRIPTS = smbinfo
>  rst_man_pages += smbinfo.1
>  endif
>
> diff --git a/smbinfo b/smbinfo
> new file mode 100755
> index 0000000..1be82c7
> --- /dev/null
> +++ b/smbinfo
> @@ -0,0 +1,779 @@
> +#!/usr/bin/env python3
> +# -*- coding: utf-8 -*-
> +#
> +# smbinfo is a cmdline tool to query SMB-specific file and fs
> +# information on a Linux SMB mount (cifs.ko).
> +#
> +# Copyright (C) 2019 Aurelien Aptel <aaptel@suse.com>
> +# Copyright (C) 2019 Ronnie Sahlberg <lsahlberg@redhat.com>
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 2 of the License, or
> +# (at your option) any later version.
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +# You should have received a copy of the GNU General Public License
> +# along with this program; if not, write to the Free Software
> +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> +
> +import os
> +import re
> +import argparse
> +import fcntl
> +import struct
> +import stat
> +import datetime
> +
> +VERBOSE = False
> +
> +# ioctl ctl codes
> +CIFS_QUERY_INFO          = 0xc018cf07
> +CIFS_ENUMERATE_SNAPSHOTS = 0x800ccf06
> +CIFS_DUMP_KEY            = 0xc03acf08
> +
> +# large enough input buffer length
> +INPUT_BUFFER_LENGTH = 16384
> +
> +# cifs query flags
> +PASSTHRU_QUERY_INFO = 0x00000000
> +PASSTHRU_FSCTL      = 0x00000001
> +
> +DIR_ACCESS_FLAGS = [
> +    (0x00000001, "LIST_DIRECTORY"),
> +    (0x00000002, "ADD_FILE"),
> +    (0x00000004, "ADD_SUBDIRECTORY"),
> +    (0x00000008, "READ_EA"),
> +    (0x00000010, "WRITE_EA"),
> +    (0x00000020, "TRAVERSE"),
> +    (0x00000040, "DELETE_CHILD"),
> +    (0x00000080, "READ_ATTRIBUTES"),
> +    (0x00000100, "WRITE_ATTRIBUTES"),
> +    (0x00010000, "DELETE"),
> +    (0x00020000, "READ_CONTROL"),
> +    (0x00040000, "WRITE_DAC"),
> +    (0x00080000, "WRITE_OWNER"),
> +    (0x00100000, "SYNCHRONIZER"),
> +    (0x01000000, "ACCESS_SYSTEM_SECURITY"),
> +    (0x02000000, "MAXIMUM_ALLOWED"),
> +    (0x10000000, "GENERIC_ALL"),
> +    (0x20000000, "GENERIC_EXECUTE"),
> +    (0x40000000, "GENERIC_WRITE"),
> +    (0x80000000, "GENERIC_READ"),
> +]
> +
> +FILE_ACCESS_FLAGS = [
> +    (0x00000001, "READ_DATA"),
> +    (0x00000002, "WRITE_DATA"),
> +    (0x00000004, "APPEND_DATA"),
> +    (0x00000008, "READ_EA"),
> +    (0x00000010, "WRITE_EA"),
> +    (0x00000020, "EXECUTE"),
> +    (0x00000040, "DELETE_CHILD"),
> +    (0x00000080, "READ_ATTRIBUTES"),
> +    (0x00000100, "WRITE_ATTRIBUTES"),
> +    (0x00010000, "DELETE"),
> +    (0x00020000, "READ_CONTROL"),
> +    (0x00040000, "WRITE_DAC"),
> +    (0x00080000, "WRITE_OWNER"),
> +    (0x00100000, "SYNCHRONIZER"),
> +    (0x01000000, "ACCESS_SYSTEM_SECURITY"),
> +    (0x02000000, "MAXIMUM_ALLOWED"),
> +    (0x10000000, "GENERIC_ALL"),
> +    (0x20000000, "GENERIC_EXECUTE"),
> +    (0x40000000, "GENERIC_WRITE"),
> +    (0x80000000, "GENERIC_READ"),
> +]
> +
> +FILE_ATTR_FLAGS = [
> +    (0x00000001, "READ_ONLY"),
> +    (0x00000002, "HIDDEN"),
> +    (0x00000004, "SYSTEM"),
> +    (0x00000010, "DIRECTORY"),
> +    (0x00000020, "ARCHIVE"),
> +    (0x00000080, "NORMAL"),
> +    (0x00000100, "TEMPORARY"),
> +    (0x00000200, "SPARSE_FILE"),
> +    (0x00000400, "REPARSE_POINT"),
> +    (0x00000800, "COMPRESSED"),
> +    (0x00001000, "OFFLINE"),
> +    (0x00002000, "NOT_CONTENT_INDEXED"),
> +    (0x00004000, "ENCRYPTED"),
> +    (0x00008000, "INTEGRITY_STREAM"),
> +    (0x00020000, "NO_SCRUB_DATA"),
> +]
> +
> +FILE_MODE_FLAGS = [
> +    (0x00000002, "WRITE_THROUGH"),
> +    (0x00000004, "SEQUENTIAL_ONLY"),
> +    (0x00000008, "NO_INTERMEDIATE_BUFFERING"),
> +    (0x00000010, "SYNCHRONOUS_IO_ALERT"),
> +    (0x00000020, "SYNCHRONOUS_IO_NONALERT"),
> +    (0x00001000, "DELETE_ON_CLOSE"),
> +]
> +
> +ALIGN_TYPES = [
> +    (0, "BYTE_ALIGNMENT"),
> +    (1, "WORD_ALIGNMENT"),
> +    (3, "LONG_ALIGNMENT"),
> +    (7, "QUAD_ALIGNMENT"),
> +    (15, "OCTA_ALIGNMENT"),
> +    (31, "32_bit_ALIGNMENT"),
> +    (63, "64_bit_ALIGNMENT"),
> +    (127, "128_bit_ALIGNMENT"),
> +    (255, "254_bit_ALIGNMENT"),
> +    (511, "512_bit_ALIGNMENT"),
> +]
> +
> +COMPRESSION_TYPES = [
> +    (0x0000, "NONE"),
> +    (0x0002, "LZNT1"),
> +]
> +
> +CONTROL_FLAGS = [
> +    (0x8000, "SR"),
> +    (0x4000, "RM"),
> +    (0x2000, "PS"),
> +    (0x1000, "PD"),
> +    (0x0800, "SI"),
> +    (0x0400, "DI"),
> +    (0x0200, "SC"),
> +    (0x0100, "DC"),
> +    (0x0080, "DT"),
> +    (0x0040, "SS"),
> +    (0x0020, "SD"),
> +    (0x0010, "SP"),
> +    (0x0008, "DD"),
> +    (0x0004, "DP"),
> +    (0x0002, "GD"),
> +    (0x0001, "OD"),
> +]
> +
> +ACE_TYPES = [
> +    (0x00, "ALLOWED"),
> +    (0x01, "DENIED"),
> +    (0x02, "AUDIT"),
> +    (0x03, "ALARM"),
> +    (0x04, "ALLOWED_COMPOUND"),
> +    (0x05, "ALLOWED_OBJECT"),
> +    (0x06, "DENIED_OBJECT"),
> +    (0x07, "AUDIT_OBJECT"),
> +    (0x08, "ALARM_OBJECT"),
> +    (0x09, "ALLOWED_CALLBACK"),
> +    (0x0a, "DENIED_CALLBACK"),
> +    (0x0b, "ALLOWED_CALLBACK_OBJECT"),
> +    (0x0c, "DENIED_CALLBACK_OBJECT"),
> +    (0x0d, "AUDIT_CALLBACK"),
> +    (0x0e, "ALARM_CALLBACK"),
> +    (0x0f, "AUDIT_CALLBACK_OBJECT"),
> +    (0x10, "ALARM_CALLBACK_OBJECT"),
> +    (0x11, "MANDATORY_LABEL"),
> +    (0x12, "RESOURCE_ATTRIBUTE"),
> +    (0x13, "SCOPED_POLICY_ID"),
> +]
> +
> +ACE_FLAGS = [
> +    (0x80, "FAILED_ACCESS"),
> +    (0x40, "SUCCESSFUL_ACCESS"),
> +    (0x10, "INHERITED"),
> +    (0x08, "INHERIT_ONLY"),
> +    (0x04, "NO_PROPAGATE_INHERIT"),
> +    (0x02, "CONTAINER_INHERIT"),
> +    (0x01, "OBJECT_INHERIT"),
> +]
> +
> +CIPHER_TYPES = [
> +    (0x00, "SMB3.0 CCM encryption"),
> +    (0x01, "CCM encryption"),
> +    (0x02, "GCM encryption"),
> +]
> +
> +def main():
> +    #
> +    # Global options and arguments
> +    #
> +
> +    ap = argparse.ArgumentParser(description="Display SMB-specific file information using cifs IOCTL")
> +    ap.add_argument("-V", "--verbose", action="store_true", help="verbose output")
> +    subp = ap.add_subparsers(help="sub-commands help")
> +    subp.required = True
> +    subp.dest = 'subcommand'
> +
> +    #
> +    # To add a new sub-command xxx, add a subparser xxx complete with
> +    # help, options and/or arguments and implement cmd_xxx()
> +    #
> +
> +    sap = subp.add_parser("fileaccessinfo", help="Prints FileAccessInfo for a cifs file")
> +    sap.add_argument("file")
> +    sap.set_defaults(func=cmd_fileaccessinfo)
> +
> +    sap = subp.add_parser("filealigninfo", help="Prints FileAlignInfo for a cifs file")
> +    sap.add_argument("file")
> +    sap.set_defaults(func=cmd_filealigninfo)
> +
> +    sap = subp.add_parser("fileallinfo", help="Prints FileAllInfo for a cifs file")
> +    sap.add_argument("file")
> +    sap.set_defaults(func=cmd_fileallinfo)
> +
> +    sap = subp.add_parser("filebasicinfo", help="Prints FileBasicInfo for a cifs file")
> +    sap.add_argument("file")
> +    sap.set_defaults(func=cmd_filebasicinfo)
> +
> +    sap = subp.add_parser("fileeainfo", help="Prints FileEAInfo for a cifs file")
> +    sap.add_argument("file")
> +    sap.set_defaults(func=cmd_fileeainfo)
> +
> +    sap = subp.add_parser("filefsfullsizeinfo", help="Prints FileFsFullSizeInfo for a cifs file")
> +    sap.add_argument("file")
> +    sap.set_defaults(func=cmd_filefsfullsizeinfo)
> +
> +    sap = subp.add_parser("fileinternalinfo", help="Prints FileInternalInfo for a cifs file")
> +    sap.add_argument("file")
> +    sap.set_defaults(func=cmd_fileinternalinfo)
> +
> +    sap = subp.add_parser("filemodeinfo", help="Prints FileModeInfo for a cifs file")
> +    sap.add_argument("file")
> +    sap.set_defaults(func=cmd_filemodeinfo)
> +
> +    sap = subp.add_parser("filepositioninfo", help="Prints FilePositionInfo for a cifs file")
> +    sap.add_argument("file")
> +    sap.set_defaults(func=cmd_filepositioninfo)
> +
> +    sap = subp.add_parser("filestandardinfo", help="Prints FileStandardInfo for a cifs file")
> +    sap.add_argument("file")
> +    sap.set_defaults(func=cmd_filestandardinfo)
> +
> +    sap = subp.add_parser("fsctl-getobjid", help="Prints the objectid of the file and GUID of the underlying volume.")
> +    sap.add_argument("file")
> +    sap.set_defaults(func=cmd_fsctl_getobjid)
> +
> +    sap = subp.add_parser("getcompression", help="Prints the compression setting for the file")
> +    sap.add_argument("file")
> +    sap.set_defaults(func=cmd_getcompression)
> +
> +    sap = subp.add_parser("setcompression", help="Sets the compression level for the file")
> +    sap.add_argument("type", choices=['no','default','lznt1'])
> +    sap.add_argument("file")
> +    sap.set_defaults(func=cmd_setcompression)
> +
> +    sap = subp.add_parser("list-snapshots", help="List the previous versions of the volume that backs this file")
> +    sap.add_argument("file")
> +    sap.set_defaults(func=cmd_list_snapshots)
> +
> +    sap = subp.add_parser("quota", help="Prints the quota for a cifs file")
> +    sap.add_argument("file")
> +    sap.set_defaults(func=cmd_quota)
> +
> +    sap = subp.add_parser("secdesc", help="Prints the security descriptor for a cifs file")
> +    sap.add_argument("file")
> +    sap.set_defaults(func=cmd_secdesc)
> +
> +    sap = subp.add_parser("keys", help="Prints the decryption information needed to view encrypted network traces")
> +    sap.add_argument("file")
> +    sap.set_defaults(func=cmd_keys)
> +
> +    # parse arguments
> +    args = ap.parse_args()
> +
> +    # act on any global options
> +    if args.verbose:
> +        global VERBOSE
> +        VERBOSE = True
> +
> +    # call subcommand function
> +    args.func(args)
> +
> +class QueryInfoStruct:
> +    def __init__(self,
> +                 info_type=0, file_info_class=0, additional_information=0,
> +                 flags=0, input_buffer_length=0, output_buffer_length=0):
> +        self.info_type = info_type
> +        self.file_info_class = file_info_class
> +        self.additional_information = additional_information
> +        self.flags = flags
> +        self.input_buffer_length = input_buffer_length
> +        self.output_buffer_length = output_buffer_length
> +        buf_size = max(self.input_buffer_length, self.output_buffer_length)
> +        self.input_buffer = bytearray(buf_size)
> +
> +    def pack_input(self, fmt, offset, *vals):
> +        struct.pack_into(fmt, self.input_buffer, offset, *vals)
> +
> +    def ioctl(self, fd, out_fmt=None):
> +        buf = bytearray()
> +        buf.extend(struct.pack("IIIIII",
> +                               self.info_type,
> +                               self.file_info_class,
> +                               self.additional_information,
> +                               self.flags,
> +                               self.input_buffer_length,
> +                               self.output_buffer_length))
> +        in_len = len(buf)
> +        buf.extend(self.input_buffer)
> +        fcntl.ioctl(fd, CIFS_QUERY_INFO, buf, True)
> +        if out_fmt:
> +            return struct.unpack_from(out_fmt, buf, in_len)
> +        else:
> +            return buf[in_len:]
> +
> +def flags_to_str(flags, bitlist, verbose=None):
> +    if verbose is None:
> +        verbose = VERBOSE
> +
> +    if not verbose:
> +        return "0x%08x"%flags
> +
> +    out = []
> +    for bit, name in bitlist:
> +        if flags & bit:
> +            out.append(name)
> +
> +    return "0x%08x (%s)"%(flags, ",".join(out))
> +
> +def type_to_str(typ, typelist, verbose=None):
> +    if verbose is None:
> +        verbose = VERBOSE
> +
> +    if not verbose:
> +        return "0x%08x"%typ
> +
> +    s = "Unknown"
> +    for val, name in typelist:
> +        if typ == val:
> +            s = name
> +
> +    return "0x%08x (%s)"%(typ, s)
> +
> +def cmd_fileaccessinfo(args):
> +    qi = QueryInfoStruct(info_type=0x1, file_info_class=8, input_buffer_length=4)
> +    try:
> +        fd = os.open(args.file, os.O_RDONLY)
> +        info = os.fstat(fd)
> +        buf = qi.ioctl(fd)
> +    except Exception as e:
> +        print("syscall failed: %s"%e)
> +        return False
> +
> +    print_fileaccessinfo(buf, info)
> +
> +def print_fileaccessinfo(buf, info):
> +    flags = struct.unpack_from('<I', buf, 0)[0]
> +    if stat.S_ISDIR(info.st_mode):
> +        print("Directory access flags:", flags_to_str(flags, DIR_ACCESS_FLAGS))
> +    else:
> +        print("File/Printer access flags:", flags_to_str(flags, FILE_ACCESS_FLAGS))
> +
> +def cmd_filealigninfo(args):
> +    qi = QueryInfoStruct(info_type=0x1, file_info_class=17, input_buffer_length=4)
> +    try:
> +        fd = os.open(args.file, os.O_RDONLY)
> +        buf = qi.ioctl(fd)
> +    except Exception as e:
> +        print("syscall failed: %s"%e)
> +        return False
> +
> +    print_filealigninfo(buf)
> +
> +def print_filealigninfo(buf):
> +    mask = struct.unpack_from('<I', buf, 0)[0]
> +    print("File alignment: %s"%type_to_str(mask, ALIGN_TYPES))
> +
> +def cmd_fileallinfo(args):
> +    qi = QueryInfoStruct(info_type=0x1, file_info_class=18, input_buffer_length=INPUT_BUFFER_LENGTH)
> +    try:
> +        fd = os.open(args.file, os.O_RDONLY)
> +        info = os.fstat(fd)
> +        buf = qi.ioctl(fd)
> +    except Exception as e:
> +        print("syscall failed: %s"%e)
> +        return False
> +
> +    print_filebasicinfo(buf)
> +    print_filestandardinfo(buf[40:])
> +    print_fileinternalinfo(buf[64:])
> +    print_fileeainfo(buf[72:])
> +    print_fileaccessinfo(buf[76:], info)
> +    print_filepositioninfo(buf[80:])
> +    print_filemodeinfo(buf[88:])
> +    print_filealigninfo(buf[92:])
> +
> +def win_to_datetime(smb2_time):
> +    usec = (smb2_time / 10) % 1000000
> +    sec  = (smb2_time - 116444736000000000) // 10000000
> +    return datetime.datetime.fromtimestamp(sec + usec/10000000)
> +
> +def cmd_filebasicinfo(args):
> +    qi = QueryInfoStruct(info_type=0x1, file_info_class=4, input_buffer_length=40)
> +    try:
> +        fd = os.open(args.file, os.O_RDONLY)
> +        buf = qi.ioctl(fd)
> +    except Exception as e:
> +        print("syscall failed: %s"%e)
> +        return False
> +
> +    print_filebasicinfo(buf)
> +
> +def print_filebasicinfo(buf):
> +    ctime, atime, wtime, mtime, attrs = struct.unpack_from('<QQQQI', buf, 0)
> +    print("Creation Time: %s"%win_to_datetime(ctime))
> +    print("Last Access Time: %s"%win_to_datetime(atime))
> +    print("Last Write Time: %s"%win_to_datetime(wtime))
> +    print("Last Change Time: %s"%win_to_datetime(mtime))
> +    print("File Attributes: %s"%flags_to_str(attrs, FILE_ATTR_FLAGS))
> +
> +def cmd_fileeainfo(args):
> +    qi = QueryInfoStruct(info_type=0x1, file_info_class=7, input_buffer_length=4)
> +    try:
> +        fd = os.open(args.file, os.O_RDONLY)
> +        buf = qi.ioctl(fd)
> +    except Exception as e:
> +        print("syscall failed: %s"%e)
> +        return False
> +
> +    print_fileeainfo(buf)
> +
> +def print_fileeainfo(buf):
> +    size = struct.unpack_from('<I', buf, 0)[0]
> +    print("EA Size: %d"%size)
> +
> +def cmd_filefsfullsizeinfo(args):
> +    qi = QueryInfoStruct(info_type=0x2, file_info_class=7, input_buffer_length=32)
> +    try:
> +        fd = os.open(args.file, os.O_RDONLY)
> +        total, caller_avail, actual_avail, sec_per_unit, byte_per_sec = qi.ioctl(fd, CIFS_QUERY_INFO, '<QQQII')
> +    except Exception as e:
> +        print("syscall failed: %s"%e)
> +        return False
> +
> +    print("Total Allocation Units: %d"%total)
> +    print("Caller Available Allocation Units: %d"%caller_avail)
> +    print("Actual Available Allocation Units: %d"%actual_avail)
> +    print("Sectors Per Allocation Unit: %d"%sec_per_unit)
> +    print("Bytes Per Sector: %d"%byte_per_sec)
> +
> +def cmd_fileinternalinfo(args):
> +    qi = QueryInfoStruct(info_type=0x1, file_info_class=6, input_buffer_length=8)
> +    try:
> +        fd = os.open(args.file, os.O_RDONLY)
> +        buf = qi.ioctl(fd)
> +    except Exception as e:
> +        print("syscall failed: %s"%e)
> +        return False
> +
> +    print_fileinternalinfo(buf)
> +
> +def print_fileinternalinfo(buf):
> +    index = struct.unpack_from('<Q', buf, 0)[0]
> +    print("Index Number: %d"%index)
> +
> +
> +def cmd_filemodeinfo(args):
> +    qi = QueryInfoStruct(info_type=0x1, file_info_class=16, input_buffer_length=4)
> +    try:
> +        fd = os.open(args.file, os.O_RDONLY)
> +        buf = qi.ioctl(fd)
> +    except Exception as e:
> +        print("syscall failed: %s"%e)
> +        return False
> +
> +    print_filemodeinfo(buf)
> +
> +def print_filemodeinfo(buf):
> +        mode = struct.unpack_from('<I', buf, 0)[0]
> +        print("Mode: %s"%flags_to_str(mode, FILE_MODE_FLAGS))
> +
> +def cmd_filepositioninfo(args):
> +    qi = QueryInfoStruct(info_type=0x1, file_info_class=14, input_buffer_length=8)
> +    try:
> +        fd = os.open(args.file, os.O_RDONLY)
> +        buf = qi.ioctl(fd)
> +    except Exception as e:
> +        print("syscall failed: %s"%e)
> +        return False
> +
> +    print_filepositioninfo(buf)
> +
> +def print_filepositioninfo(buf):
> +    offset = struct.unpack_from('<Q', buf, 0)[0]
> +    print("Current Byte Offset: %d"%offset)
> +
> +def cmd_filestandardinfo(args):
> +    qi = QueryInfoStruct(info_type=0x1, file_info_class=5, input_buffer_length=24)
> +    try:
> +        fd = os.open(args.file, os.O_RDONLY)
> +        buf = qi.ioctl(fd)
> +    except Exception as e:
> +        print("syscall failed: %s"%e)
> +        return False
> +
> +    print_filestandardinfo(buf)
> +
> +def print_filestandardinfo(buf):
> +    nalloc, eof, nlink, del_pending, del_dir = struct.unpack_from('<QQIBB', buf, 0)
> +    print("Allocation Size: %d"%nalloc)
> +    print("End Of File: %d"%eof)
> +    print("Number of Links: %d"%nlink)
> +    print("Delete Pending: %d"%del_pending)
> +    print("Delete Directory: %d"%del_dir)
> +
> +def guid_to_str(buf):
> +    return "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"%struct.unpack_from('<ISSBBBBBBBB', buf, 0)
> +
> +def cmd_fsctl_getobjid(args):
> +    qi = QueryInfoStruct(info_type=0x9009c, file_info_class=5, flags=PASSTHRU_FSCTL, input_buffer_length=64)
> +    try:
> +        fd = os.open(args.file, os.O_RDONLY)
> +        buf = qi.ioctl(fd)
> +    except Exception as e:
> +        print("syscall failed: %s"%e)
> +        return False
> +
> +    print("Object-ID: %s"%guid_to_str(buf))
> +    print("Birth-Volume-ID: %s"%guid_to_str(buf[16:]))
> +    print("Birth-Object-ID: %s"%guid_to_str(buf[32:]))
> +    print("Domain-ID: %s"%guid_to_str(buf[48:]))
> +
> +def cmd_getcompression(args):
> +    qi = QueryInfoStruct(info_type=0x9003c, flags=PASSTHRU_FSCTL, input_buffer_length=2)
> +    try:
> +        fd = os.open(args.file, os.O_RDONLY)
> +        ctype = qi.ioctl(fd, CIFS_QUERY_INFO, '<H')[0]
> +    except Exception as e:
> +        print("syscall failed: %s"%e)
> +        return False
> +
> +    ctype_name = "UNKNOWN"
> +    for val, name in COMPRESSION_TYPES:
> +        if ctype == val:
> +            ctype_name = name
> +            break
> +    print("Compression: %d (%s)"%(ctype, ctype_name))
> +
> +def cmd_setcompression(args):
> +    qi = QueryInfoStruct(info_type=0x9c040, flags=PASSTHRU_FSCTL, output_buffer_length=2)
> +    type_map = {'no': 0, 'default': 1, 'lznt1': 2}
> +    qi.pack_input('<H', 0, type_map[args.type])
> +    try:
> +        fd = os.open(args.file, os.O_RDONLY)
> +        qi.ioctl(fd)
> +    except Exception as e:
> +        print("syscall failed: %s"%e)
> +        return False
> +
> +class SnapshotArrayStruct:
> +    def __init__(self,
> +                 nb_snapshots=0,
> +                 nb_snapshots_returned=0,
> +                 snapshot_array_size=12):
> +        self.nb_snapshots = nb_snapshots
> +        self.nb_snapshots_returned = nb_snapshots_returned
> +        self.snapshot_array_size = snapshot_array_size
> +        self.snapshot_array = []
> +
> +    def ioctl(self, fd, op):
> +        buf = bytearray()
> +        buf.extend(struct.pack("III",
> +                               self.nb_snapshots,
> +                               self.nb_snapshots_returned,
> +                               self.snapshot_array_size))
> +
> +        buf.extend(bytearray(16 + self.snapshot_array_size))
> +        fcntl.ioctl(fd, op, buf, True)
> +
> +        out = SnapshotArrayStruct()
> +        out.nb_snapshots, out.nb_snapshots_returned, out.snapshot_array_size = struct.unpack_from('III', buf, 0)
> +        data = buf[12:]
> +
> +        for gmt in re.findall(rb'''@([^\x00]+)''', data):
> +            d = datetime.strptime("@GMT-%Y.%m.%d-%H.%M.%S")
> +            out.snapshot_array.append(d)
> +
> +        return out
> +
> +def datetime_to_smb(dt):
> +    ntfs_time_offset = (369*365 + 89) * 24 * 3600 * 10000000
> +    return datetime.datetime.timestamp(dt) * 10000000 + ntfs_time_offset
> +
> +def cmd_list_snapshots(args):
> +    sa1req = SnapshotArrayStruct()
> +    sa1res = None
> +    sa2req = None
> +    sa2res = None
> +
> +    try:
> +        fd = os.open(args.file, os.O_RDONLY)
> +        sa1res = sa1req.ioctl(fd, CIFS_ENUMERATE_SNAPSHOTS)
> +    except Exception as e:
> +        print("syscall failed: %s"%e)
> +        return False
> +
> +    if sa1res.nb_snapshots == 0:
> +        return
> +
> +    sa2req = SnapshotArrayStruct(nb_snapshots=sa1res.nb_snapshots, snapshot_array_size=sa1res.snapshot_array_size)
> +    try:
> +        fd = os.open(args.file, os.O_RDONLY)
> +        sa2res = sa2req.ioctl(fd, CIFS_ENUMERATE_SNAPSHOTS)
> +    except Exception as e:
> +        print("syscall failed: %s"%e)
> +        return False
> +
> +
> +    print("Number of snapshots: %d Number of snapshots returned: %d"%(sa2res.nb_snapshots, sa2res.nb_snapshots_returned))
> +    print("Snapshot list in GMT (Coordinated UTC Time) and SMB format (100 nanosecond units needed for snapshot mounts):")
> +    for i, d in enumerate(sa2res.snapshot_array):
> +        print("%d) GMT: %s\n   SMB3: %d"%(i, d, datetime_to_smb(d)))
> +
> +class SID:
> +    def __init__(self, buf, off=0):
> +        rev, sublen = struct.unpack_from('BB', buf, off+0)
> +        off += 2
> +        auth = 0
> +        subauth = []
> +        for i in range(6):
> +            auth = (auth << 8)|buf[off]
> +            off += 1
> +        for i in range(sublen):
> +            subauth.append(struct.unpack_from('<I', buf, off))
> +            off += 4
> +
> +        self.rev = rev
> +        self.auth = auth
> +        self.subauth = subauth
> +
> +    def __str__(self):
> +        auth = ("0x%x" if self.auth >= 2**32 else "%d")%self.auth
> +        return  "S-%d-%s-%s"%(self.rev, auth, '-'.join(["%d"%x for x in self.subauth]))
> +
> +class ACE:
> +    def __init__(self, buf, off=0, is_dir=False):
> +        self.typ, self.flags, self.size = struct.unpack_from('<BBH', buf, off)
> +        self.is_dir = is_dir
> +        if self.typ not in [0,1,2]:
> +            self.buf = buf[4:]
> +        else:
> +            self.mask = struct.unpack_from('<I', buf, off+4)[0]
> +            self.sid = SID(buf, off+8)
> +
> +    def __str__(self):
> +        s = []
> +        s.append("Type: %s" % type_to_str(self.typ, ACE_TYPES))
> +        s.append("Flags: %s" % flags_to_str(self.flags, ACE_FLAGS))
> +        if self.typ not in [0,1,2]:
> +            s.append("<%s>"%(" ".join(["%02x"%x for x in self.buf])))
> +        else:
> +            s.append("Mask: %s"%flags_to_str(self.mask, (DIR_ACCESS_FLAGS if self.is_dir else FILE_ACCESS_FLAGS)))
> +            s.append("SID: %s"%self.sid)
> +        return ", ".join(s)
> +
> +def cmd_quota(args):
> +    qi = QueryInfoStruct(info_type=0x04, input_buffer_length=INPUT_BUFFER_LENGTH)
> +    qi.pack_input('BBI', 0,
> +                  0, # return single
> +                  1, # restart scan
> +                  0, # sid list length
> +                  )
> +    qi.output_buffer_length = 16
> +    buf = None
> +
> +    try:
> +        fd = os.open(args.file, os.O_RDONLY)
> +        buf = qi.ioctl(fd)
> +    except Exception as e:
> +        print("syscall failed: %s"%e)
> +        return False
> +
> +    off = 0
> +    while off < len(buf):
> +        next_off = struct.unpack_from('<I', buf, off+ 0)[0]
> +        sid_len  = struct.unpack_from('<I', buf, off+ 4)[0]
> +        atime    = struct.unpack_from('<Q', buf, off+ 8)[0]
> +        qused    = struct.unpack_from('<Q', buf, off+16)[0]
> +        qthresh  = struct.unpack_from('<Q', buf, off+24)[0]
> +        qlimit   = struct.unpack_from('<Q', buf, off+32)[0]
> +        sid = SID(buf, off+40)
> +
> +        print("SID Length: %d"%sid_len)
> +        print("Change Time: %s"%win_to_datetime(atime))
> +        print("Quota Used: %d"%qused)
> +        print("Quota Threshold:", ("NO THRESHOLD" if qthresh == 0xffffffffffffffff else "%d"%qthresh))
> +        print("Quota Limit:", ("NO LIMIT" if qlimit == 0xffffffffffffffff else "%d"%qlimit))
> +        print("SID: %s"%sid)
> +
> +        if next_off == 0:
> +            break
> +        off += next_off
> +
> +def cmd_secdesc(args):
> +    qi = QueryInfoStruct(info_type=0x03,
> +                         additional_information=0x7, # owner, group, dacl
> +                         input_buffer_length=INPUT_BUFFER_LENGTH)
> +    buf = None
> +    info = None
> +
> +    try:
> +        fd = os.open(args.file, os.O_RDONLY)
> +        info = os.fstat(fd)
> +        buf = qi.ioctl(fd)
> +    except Exception as e:
> +        print("syscall failed: %s"%e)
> +        return False
> +
> +    is_dir = stat.S_ISDIR(info.st_mode)
> +    rev, ctrl, off_owner, off_group, off_dacl = struct.unpack_from('<BxHIIxxxxI', buf, 0)
> +
> +    print("Revision: %d"%rev)
> +    print("Control: %s"%flags_to_str(ctrl, CONTROL_FLAGS))
> +    if off_owner:
> +        print("Owner: %s"%SID(buf, off_owner))
> +    if off_group:
> +        print("Group: %s"%SID(buf, off_group))
> +    if off_dacl:
> +        print("DACL:")
> +        rev, count = struct.unpack_from('<BxxxH', buf, off_dacl)
> +        off_dacl += 8
> +        for i in range(count):
> +              ace = ACE(buf, off_dacl, is_dir=is_dir)
> +              print(ace)
> +              off_dacl += ace.size
> +
> +
> +class KeyDebugInfoStruct:
> +    def __init__(self):
> +        self.suid = bytearray()
> +        self.cipher = 0
> +        self.auth_key = bytearray()
> +        self.enc_key = bytearray()
> +        self.dec_key = bytearray()
> +
> +    def ioctl(self, fd):
> +        buf = bytearray()
> +        buf.extend(struct.pack("= 8s H 16s 16s 16s", self.suid, self.cipher,
> +                               self.auth_key, self.enc_key, self.dec_key))
> +        fcntl.ioctl(fd, CIFS_DUMP_KEY, buf, True)
> +        (self.suid, self.cipher, self.auth_key,
> +         self.enc_key, self.dec_key) = struct.unpack_from('= 8s H 16s 16s 16s', buf, 0)
> +
> +def bytes_to_hex(buf):
> +    return " ".join(["%02x"%x for x in buf])
> +
> +def cmd_keys(args):
> +    kd = KeyDebugInfoStruct()
> +    try:
> +        fd = os.open(args.file, os.O_RDONLY)
> +        kd.ioctl(fd)
> +    except Exception as e:
> +        print("syscall failed: %s"%e)
> +        return False
> +
> +    print("Session Id: %s"%bytes_to_hex(kd.suid))
> +    print("Cipher: %s"%type_to_str(kd.cipher, CIPHER_TYPES, verbose=True))
> +    print("Session Key: %s"%bytes_to_hex(kd.auth_key))
> +    print("Encryption key: %s"%bytes_to_hex(kd.enc_key))
> +    print("Decryption key: %s"%bytes_to_hex(kd.dec_key))
> +
> +if __name__ == '__main__':
> +    main()
> diff --git a/smbinfo.c b/smbinfo.c
> deleted file mode 100644
> index 636f1bd..0000000
> --- a/smbinfo.c
> +++ /dev/null
> @@ -1,1296 +0,0 @@
> -/*
> - * smbinfo
> - *
> - * Copyright (C) Ronnie Sahlberg (lsahlberg@redhat.com) 2018
> - * Copyright (C) Aurelien Aptel (aaptel@suse.com) 2018
> - *
> - * Display SMB-specific file information using cifs IOCTL
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; either version 2 of the License, or
> - * (at your option) any later version.
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> - * GNU General Public License for more details.
> - * You should have received a copy of the GNU General Public License
> - * along with this program; if not, write to the Free Software
> - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> - */
> -
> -#ifdef HAVE_CONFIG_H
> -#include "config.h"
> -#endif /* HAVE_CONFIG_H */
> -
> -#include <endian.h>
> -#include <errno.h>
> -#include <getopt.h>
> -#include <sys/ioctl.h>
> -#include <sys/types.h>
> -#include <sys/stat.h>
> -#include <fcntl.h>
> -#include <string.h>
> -#include <stdint.h>
> -#include <stdio.h>
> -#include <stdlib.h>
> -#include <time.h>
> -#include <unistd.h>
> -#include <inttypes.h>
> -
> -#define CIFS_IOCTL_MAGIC 0xCF
> -
> -/* query_info flags */
> -#define PASSTHRU_QUERY_INFO     0x00000000
> -#define PASSTHRU_FSCTL          0x00000001
> -
> -struct smb_query_info {
> -       uint32_t   info_type;
> -       uint32_t   file_info_class;
> -       uint32_t   additional_information;
> -       uint32_t   flags;
> -       uint32_t   input_buffer_length;
> -       uint32_t   output_buffer_length;
> -       /* char buffer[]; */
> -} __packed;
> -
> -#define SMB3_SIGN_KEY_SIZE 16
> -struct smb3_key_debug_info {
> -       uint64_t Suid;
> -       uint16_t cipher_type;
> -       uint8_t auth_key[16]; /* SMB2_NTLMV2_SESSKEY_SIZE */
> -       uint8_t smb3encryptionkey[SMB3_SIGN_KEY_SIZE];
> -       uint8_t smb3decryptionkey[SMB3_SIGN_KEY_SIZE];
> -} __attribute__((packed));
> -
> -#define CIFS_QUERY_INFO _IOWR(CIFS_IOCTL_MAGIC, 7, struct smb_query_info)
> -#define CIFS_DUMP_KEY _IOWR(CIFS_IOCTL_MAGIC, 8, struct smb3_key_debug_info)
> -#define INPUT_BUFFER_LENGTH 16384
> -
> -int verbose;
> -
> -static void
> -usage(char *name)
> -{
> -       fprintf(stderr, "Usage: %s [-V] <command> <file>\n"
> -               "-V for verbose output\n"
> -               "-h display this help text\n"
> -               "-v print smbinfo version\n"
> -               "Commands are\n"
> -               "  fileaccessinfo:\n"
> -               "      Prints FileAccessInfo for a cifs file.\n"
> -               "  filealigninfo:\n"
> -               "      Prints FileAlignInfo for a cifs file.\n"
> -               "  fileallinfo:\n"
> -               "      Prints FileAllInfo for a cifs file.\n"
> -               "  filebasicinfo:\n"
> -               "      Prints FileBasicInfo for a cifs file.\n"
> -               "  fileeainfo:\n"
> -               "      Prints FileEAInfo for a cifs file.\n"
> -               "  filefsfullsizeinfo:\n"
> -               "      Prints FileFsFullSizeInfo for a cifs share.\n"
> -               "  fileinternalinfo:\n"
> -               "      Prints FileInternalInfo for a cifs file.\n"
> -               "  filemodeinfo:\n"
> -               "      Prints FileModeInfo for a cifs file.\n"
> -               "  filepositioninfo:\n"
> -               "      Prints FilePositionInfo for a cifs file.\n"
> -               "  filestandardinfo:\n"
> -               "      Prints FileStandardInfo for a cifs file.\n"
> -               "  fsctl-getobjid:\n"
> -               "      Prints the objectid of the file and GUID of the underlying volume.\n"
> -               "  getcompression:\n"
> -               "      Prints the compression setting for the file.\n"
> -               "  setcompression <no|default|lznt1>:\n"
> -               "      Sets the compression level for the file.\n"
> -               "  list-snapshots:\n"
> -               "      List the previous versions of the volume that backs this file.\n"
> -               "  quota:\n"
> -               "      Prints the quota for a cifs file.\n"
> -               "  secdesc:\n"
> -               "      Prints the security descriptor for a cifs file.\n"
> -               "  keys:\n"
> -               "      Prints the decryption information needed to view encrypted network traces.\n",
> -               name);
> -       exit(1);
> -}
> -
> -static void
> -short_usage(char *name)
> -{
> -       fprintf(stderr, "Usage: %s [-v] [-V] <command> <file>\n"
> -               "Try 'smbinfo -h' for more information.\n", name);
> -       exit(1);
> -}
> -
> -static void
> -win_to_timeval(uint64_t smb2_time, struct timeval *tv)
> -{
> -       tv->tv_usec = (smb2_time / 10) % 1000000;
> -       tv->tv_sec  = (smb2_time - 116444736000000000) / 10000000;
> -}
> -
> -struct bit_string {
> -       unsigned int bit;
> -       char *string;
> -};
> -
> -struct bit_string directory_access_mask[] = {
> -       { 0x00000001, "LIST_DIRECTORY" },
> -       { 0x00000002, "ADD_FILE" },
> -       { 0x00000004, "ADD_SUBDIRECTORY" },
> -       { 0x00000008, "READ_EA" },
> -       { 0x00000010, "WRITE_EA" },
> -       { 0x00000020, "TRAVERSE" },
> -       { 0x00000040, "DELETE_CHILD" },
> -       { 0x00000080, "READ_ATTRIBUTES" },
> -       { 0x00000100, "WRITE_ATTRIBUTES" },
> -       { 0x00010000, "DELETE" },
> -       { 0x00020000, "READ_CONTROL" },
> -       { 0x00040000, "WRITE_DAC" },
> -       { 0x00080000, "WRITE_OWNER" },
> -       { 0x00100000, "SYNCHRONIZER" },
> -       { 0x01000000, "ACCESS_SYSTEM_SECURITY" },
> -       { 0x02000000, "MAXIMUM_ALLOWED" },
> -       { 0x10000000, "GENERIC_ALL" },
> -       { 0x20000000, "GENERIC_EXECUTE" },
> -       { 0x40000000, "GENERIC_WRITE" },
> -       { 0x80000000, "GENERIC_READ" },
> -       { 0, NULL }
> -};
> -
> -struct bit_string file_access_mask[] = {
> -       { 0x00000001, "READ_DATA" },
> -       { 0x00000002, "WRITE_DATA" },
> -       { 0x00000004, "APPEND_DATA" },
> -       { 0x00000008, "READ_EA" },
> -       { 0x00000010, "WRITE_EA" },
> -       { 0x00000020, "EXECUTE" },
> -       { 0x00000040, "DELETE_CHILD" },
> -       { 0x00000080, "READ_ATTRIBUTES" },
> -       { 0x00000100, "WRITE_ATTRIBUTES" },
> -       { 0x00010000, "DELETE" },
> -       { 0x00020000, "READ_CONTROL" },
> -       { 0x00040000, "WRITE_DAC" },
> -       { 0x00080000, "WRITE_OWNER" },
> -       { 0x00100000, "SYNCHRONIZER" },
> -       { 0x01000000, "ACCESS_SYSTEM_SECURITY" },
> -       { 0x02000000, "MAXIMUM_ALLOWED" },
> -       { 0x10000000, "GENERIC_ALL" },
> -       { 0x20000000, "GENERIC_EXECUTE" },
> -       { 0x40000000, "GENERIC_WRITE" },
> -       { 0x80000000, "GENERIC_READ" },
> -       { 0, NULL }
> -};
> -
> -static void
> -print_bits(uint32_t mask, struct bit_string *bs)
> -{
> -       int first = 1;
> -
> -       if (!verbose)
> -               return;
> -
> -       while (bs->string) {
> -               if (mask & bs->bit) {
> -                       printf("%s%s", first?"":",", bs->string);
> -                       first = 0;
> -               }
> -               bs++;
> -       }
> -       if (!first)
> -               printf(" ");
> -}
> -
> -static void
> -print_guid(uint8_t *sd)
> -{
> -       uint32_t u32;
> -       uint16_t u16;
> -       int i;
> -
> -       memcpy(&u32, &sd[0], 4);
> -       printf("%08x-", le32toh(u32));
> -
> -       memcpy(&u16, &sd[4], 2);
> -       printf("%04x-", le16toh(u16));
> -
> -       memcpy(&u16, &sd[6], 2);
> -       printf("%04x-", le16toh(u16));
> -
> -       printf("%02x%02x-", sd[8], sd[9]);
> -       for (i = 0; i < 6; i++)
> -               printf("%02x", sd[10 + i]);
> -}
> -
> -static void
> -print_objidbuf(uint8_t *sd)
> -{
> -       printf("Object-ID: ");
> -       print_guid(&sd[0]);
> -       printf("\n");
> -
> -       printf("Birth-Volume-ID: ");
> -       print_guid(&sd[16]);
> -       printf("\n");
> -
> -       printf("Birth-Object-ID: ");
> -       print_guid(&sd[32]);
> -       printf("\n");
> -
> -       printf("Domain-ID: ");
> -       print_guid(&sd[48]);
> -       printf("\n");
> -}
> -
> -static void
> -fsctlgetobjid(int f)
> -{
> -       struct smb_query_info *qi;
> -       struct stat st;
> -
> -       fstat(f, &st);
> -
> -       qi = malloc(sizeof(struct smb_query_info) + 64);
> -       memset(qi, 0, sizeof(qi) + 64);
> -       qi->info_type = 0x9009c;
> -       qi->file_info_class = 0;
> -       qi->additional_information = 0;
> -       qi->input_buffer_length = 64;
> -       qi->flags = PASSTHRU_FSCTL;
> -
> -       if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
> -               fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
> -               exit(1);
> -       }
> -       print_objidbuf((uint8_t *)(&qi[1]));
> -
> -       free(qi);
> -}
> -
> -static void
> -print_getcompression(uint8_t *sd)
> -{
> -       uint16_t u16;
> -
> -       memcpy(&u16, &sd[0], 2);
> -       u16 = le16toh(u16);
> -
> -       printf("Compression: ");
> -       switch (u16) {
> -       case 0:
> -               printf("(0) NONE\n");
> -               break;
> -       case 2:
> -               printf("(2) LZNT1\n");
> -               break;
> -       default:
> -               printf("(%d) UNKNOWN\n", u16);
> -               break;
> -       }
> -}
> -
> -static void
> -getcompression(int f)
> -{
> -       struct smb_query_info *qi;
> -
> -       qi = malloc(sizeof(struct smb_query_info) + 2);
> -       memset(qi, 0, sizeof(qi) + 2);
> -       qi->info_type = 0x9003c;
> -       qi->file_info_class = 0;
> -       qi->additional_information = 0;
> -       qi->input_buffer_length = 2;
> -       qi->flags = PASSTHRU_FSCTL;
> -
> -       if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
> -               fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
> -               exit(1);
> -       }
> -       print_getcompression((uint8_t *)(&qi[1]));
> -
> -       free(qi);
> -}
> -
> -static void
> -setcompression(int f, uint16_t level)
> -{
> -       struct smb_query_info *qi;
> -
> -       qi = malloc(sizeof(struct smb_query_info) + 2);
> -       memset(qi, 0, sizeof(qi) + 2);
> -       qi->info_type = 0x9c040;
> -       qi->file_info_class = 0;
> -       qi->additional_information = 0;
> -       qi->output_buffer_length = 2;
> -       qi->flags = PASSTHRU_FSCTL;
> -
> -       level = htole16(level);
> -       memcpy(&qi[1], &level, 2);
> -
> -       if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
> -               fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
> -               exit(1);
> -       }
> -
> -       free(qi);
> -}
> -
> -static void
> -print_fileaccessinfo(uint8_t *sd, int type)
> -{
> -       uint32_t access_flags;
> -
> -       memcpy(&access_flags, &sd[0], 4);
> -       access_flags = le32toh(access_flags);
> -
> -       if (type == S_IFDIR) {
> -               printf("Directory access flags 0x%08x: ", access_flags);
> -               print_bits(access_flags, directory_access_mask);
> -       } else {
> -               printf("File/Printer access flags 0x%08x: ", access_flags);
> -               print_bits(access_flags, file_access_mask);
> -       }
> -       printf("\n");
> -}
> -
> -static void
> -fileaccessinfo(int f)
> -{
> -       struct smb_query_info *qi;
> -       struct stat st;
> -
> -       fstat(f, &st);
> -
> -       qi = malloc(sizeof(struct smb_query_info) + 4);
> -       memset(qi, 0, sizeof(qi) + 4);
> -       qi->info_type = 0x01;
> -       qi->file_info_class = 8;
> -       qi->additional_information = 0;
> -       qi->input_buffer_length = 4;
> -
> -       if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
> -               fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
> -               exit(1);
> -       }
> -
> -       print_fileaccessinfo((uint8_t *)(&qi[1]), st.st_mode & S_IFMT);
> -       free(qi);
> -}
> -
> -static void
> -print_filealigninfo(uint8_t *sd)
> -{
> -       uint32_t mask;
> -
> -       memcpy(&mask, &sd[0], 4);
> -       mask = le32toh(mask);
> -
> -       printf("File alignment: ");
> -       if (mask == 0)
> -               printf("BYTE_ALIGNMENT");
> -       else if (mask == 1)
> -               printf("WORD_ALIGNMENT");
> -       else if (mask == 3)
> -               printf("LONG_ALIGNMENT");
> -       else if (mask == 7)
> -               printf("QUAD_ALIGNMENT");
> -       else if (mask == 15)
> -               printf("OCTA_ALIGNMENT");
> -       else if (mask == 31)
> -               printf("32_bit_ALIGNMENT");
> -       else if (mask == 63)
> -               printf("64_bit_ALIGNMENT");
> -       else if (mask == 127)
> -               printf("128_bit_ALIGNMENT");
> -       else if (mask == 255)
> -               printf("254_bit_ALIGNMENT");
> -       else if (mask == 511)
> -               printf("512_bit_ALIGNMENT");
> -
> -       printf("\n");
> -}
> -
> -static void
> -filealigninfo(int f)
> -{
> -       struct smb_query_info *qi;
> -
> -       qi = malloc(sizeof(struct smb_query_info) + 4);
> -       memset(qi, 0, sizeof(qi) + 4);
> -       qi->info_type = 0x01;
> -       qi->file_info_class = 17;
> -       qi->additional_information = 0;
> -       qi->input_buffer_length = 4;
> -
> -       if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
> -               fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
> -               exit(1);
> -       }
> -
> -       print_filealigninfo((uint8_t *)(&qi[1]));
> -       free(qi);
> -}
> -
> -struct bit_string file_attributes_mask[] = {
> -       { 0x00000001, "READ_ONLY" },
> -       { 0x00000002, "HIDDEN" },
> -       { 0x00000004, "SYSTEM" },
> -       { 0x00000010, "DIRECTORY" },
> -       { 0x00000020, "ARCHIVE" },
> -       { 0x00000080, "NORMAL" },
> -       { 0x00000100, "TEMPORARY" },
> -       { 0x00000200, "SPARSE_FILE" },
> -       { 0x00000400, "REPARSE_POINT" },
> -       { 0x00000800, "COMPRESSED" },
> -       { 0x00001000, "OFFLINE" },
> -       { 0x00002000, "NOT_CONTENT_INDEXED" },
> -       { 0x00004000, "ENCRYPTED" },
> -       { 0x00008000, "INTEGRITY_STREAM" },
> -       { 0x00020000, "NO_SCRUB_DATA" },
> -       { 0, NULL }
> -};
> -
> -static void
> -print_filebasicinfo(uint8_t *sd)
> -{
> -       struct timeval tv;
> -       uint64_t u64;
> -       uint32_t u32;
> -
> -       memcpy(&u64, &sd[0], 8);
> -       win_to_timeval(le64toh(u64), &tv);
> -       printf("Creation Time %s", ctime(&tv.tv_sec));
> -
> -       memcpy(&u64, &sd[8], 8);
> -       win_to_timeval(le64toh(u64), &tv);
> -       printf("Last Access Time %s", ctime(&tv.tv_sec));
> -
> -       memcpy(&u64, &sd[16], 8);
> -       win_to_timeval(le64toh(u64), &tv);
> -       printf("Last Write Time %s", ctime(&tv.tv_sec));
> -
> -       memcpy(&u64, &sd[24], 8);
> -       win_to_timeval(le64toh(u64), &tv);
> -       printf("Last Change Time %s", ctime(&tv.tv_sec));
> -
> -       memcpy(&u32, &sd[32], 4);
> -       u32 = le32toh(u32);
> -       printf("File Attributes 0x%08x: ", u32);
> -       print_bits(u32, file_attributes_mask);
> -       printf("\n");
> -}
> -
> -static void
> -filebasicinfo(int f)
> -{
> -       struct smb_query_info *qi;
> -
> -       qi = malloc(sizeof(struct smb_query_info) + 40);
> -       memset(qi, 0, sizeof(qi) + 40);
> -       qi->info_type = 0x01;
> -       qi->file_info_class = 4;
> -       qi->additional_information = 0;
> -       qi->input_buffer_length = 40;
> -
> -       if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
> -               fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
> -               exit(1);
> -       }
> -
> -       print_filebasicinfo((uint8_t *)(&qi[1]));
> -       free(qi);
> -}
> -
> -static void
> -print_filestandardinfo(uint8_t *sd)
> -{
> -       uint64_t u64;
> -       uint32_t u32;
> -
> -       memcpy(&u64, &sd[0], 8);
> -       printf("Allocation Size %" PRIu64 "\n", le64toh(u64));
> -
> -       memcpy(&u64, &sd[8], 8);
> -       printf("End Of File %" PRIu64 "\n", le64toh(u64));
> -
> -       memcpy(&u32, &sd[16], 4);
> -       printf("Number Of Links %" PRIu32 "\n", le32toh(u32));
> -
> -       printf("Delete Pending %d\n", sd[20]);
> -       printf("Delete Directory %d\n", sd[21]);
> -}
> -
> -static void
> -filestandardinfo(int f)
> -{
> -       struct smb_query_info *qi;
> -
> -       qi = malloc(sizeof(struct smb_query_info) + 24);
> -       memset(qi, 0, sizeof(qi) + 24);
> -       qi->info_type = 0x01;
> -       qi->file_info_class = 5;
> -       qi->additional_information = 0;
> -       qi->input_buffer_length = 24;
> -
> -       if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
> -               fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
> -               exit(1);
> -       }
> -
> -       print_filestandardinfo((uint8_t *)(&qi[1]));
> -       free(qi);
> -}
> -
> -static void
> -print_fileinternalinfo(uint8_t *sd)
> -{
> -       uint64_t u64;
> -
> -       memcpy(&u64, &sd[0], 8);
> -       printf("Index Number %" PRIu64 "\n", le64toh(u64));
> -}
> -
> -static void
> -fileinternalinfo(int f)
> -{
> -       struct smb_query_info *qi;
> -
> -       qi = malloc(sizeof(struct smb_query_info) + 8);
> -       memset(qi, 0, sizeof(qi) + 8);
> -       qi->info_type = 0x01;
> -       qi->file_info_class = 6;
> -       qi->additional_information = 0;
> -       qi->input_buffer_length = 8;
> -
> -       if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
> -               fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
> -               exit(1);
> -       }
> -
> -       print_fileinternalinfo((uint8_t *)(&qi[1]));
> -       free(qi);
> -}
> -
> -struct bit_string file_mode_mask[] = {
> -       { 0x00000002, "WRITE_THROUGH" },
> -       { 0x00000004, "SEQUENTIAL_ONLY" },
> -       { 0x00000008, "NO_INTERMEDIATE_BUFFERING" },
> -       { 0x00000010, "SYNCHRONOUS_IO_ALERT" },
> -       { 0x00000020, "SYNCHRONOUS_IO_NONALERT" },
> -       { 0x00001000, "DELETE_ON_CLOSE" },
> -       { 0, NULL }
> -};
> -
> -static void
> -print_filemodeinfo(uint8_t *sd)
> -{
> -       uint32_t u32;
> -
> -       memcpy(&u32, &sd[32], 4);
> -       u32 = le32toh(u32);
> -       printf("Mode 0x%08x: ", u32);
> -       print_bits(u32, file_mode_mask);
> -       printf("\n");
> -}
> -
> -static void
> -filemodeinfo(int f)
> -{
> -       struct smb_query_info *qi;
> -
> -       qi = malloc(sizeof(struct smb_query_info) + 4);
> -       memset(qi, 0, sizeof(qi) + 4);
> -       qi->info_type = 0x01;
> -       qi->file_info_class = 16;
> -       qi->additional_information = 0;
> -       qi->input_buffer_length = 4;
> -
> -       if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
> -               fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
> -               exit(1);
> -       }
> -
> -       print_filemodeinfo((uint8_t *)(&qi[1]));
> -       free(qi);
> -}
> -
> -static void
> -print_filepositioninfo(uint8_t *sd)
> -{
> -       uint64_t u64;
> -
> -       memcpy(&u64, &sd[0], 8);
> -       printf("Current Byte Offset %" PRIu64 "\n", le64toh(u64));
> -}
> -
> -static void
> -filepositioninfo(int f)
> -{
> -       struct smb_query_info *qi;
> -
> -       qi = malloc(sizeof(struct smb_query_info) + 8);
> -       memset(qi, 0, sizeof(qi) + 8);
> -       qi->info_type = 0x01;
> -       qi->file_info_class = 14;
> -       qi->additional_information = 0;
> -       qi->input_buffer_length = 8;
> -
> -       if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
> -               fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
> -               exit(1);
> -       }
> -
> -       print_filepositioninfo((uint8_t *)(&qi[1]));
> -       free(qi);
> -}
> -
> -static void
> -print_fileeainfo(uint8_t *sd)
> -{
> -       uint32_t u32;
> -
> -       memcpy(&u32, &sd[0], 4);
> -       printf("Ea Size %" PRIu32 "\n", le32toh(u32));
> -}
> -
> -static void
> -fileeainfo(int f)
> -{
> -       struct smb_query_info *qi;
> -
> -       qi = malloc(sizeof(struct smb_query_info) + 4);
> -       memset(qi, 0, sizeof(qi) + 4);
> -       qi->info_type = 0x01;
> -       qi->file_info_class = 7;
> -       qi->additional_information = 0;
> -       qi->input_buffer_length = 4;
> -
> -       if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
> -               fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
> -               exit(1);
> -       }
> -
> -       print_fileeainfo((uint8_t *)(&qi[1]));
> -       free(qi);
> -}
> -
> -static void
> -print_filefullsizeinfo(uint8_t *sd)
> -{
> -       uint32_t u32;
> -       uint64_t u64;
> -
> -       memcpy(&u64, &sd[0], 8);
> -       printf("Total Allocation Units: %" PRIu64 "\n", le64toh(u64));
> -
> -       memcpy(&u64, &sd[8], 8);
> -       printf("Caller Available Allocation Units: %" PRIu64 "\n",
> -              le64toh(u64));
> -
> -       memcpy(&u64, &sd[16], 8);
> -       printf("Actual Available Allocation Units: %" PRIu64 "\n",
> -              le64toh(u64));
> -
> -       memcpy(&u32, &sd[24], 4);
> -       printf("Sectors Per Allocation Unit: %" PRIu32 "\n", le32toh(u32));
> -
> -       memcpy(&u32, &sd[28], 4);
> -       printf("Bytes Per Sector: %" PRIu32 "\n", le32toh(u32));
> -}
> -
> -static void
> -filefsfullsizeinfo(int f)
> -{
> -       struct smb_query_info *qi;
> -
> -       qi = malloc(sizeof(struct smb_query_info) + 32);
> -       memset(qi, 0, sizeof(qi) + 32);
> -       qi->info_type = 0x02;
> -       qi->file_info_class = 7;
> -       qi->additional_information = 0;
> -       qi->input_buffer_length = 32;
> -
> -       if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
> -               fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
> -               exit(1);
> -       }
> -
> -       print_filefullsizeinfo((uint8_t *)(&qi[1]));
> -       free(qi);
> -}
> -
> -static void
> -fileallinfo(int f)
> -{
> -       struct smb_query_info *qi;
> -       struct stat st;
> -
> -       fstat(f, &st);
> -
> -       qi = malloc(sizeof(struct smb_query_info) + INPUT_BUFFER_LENGTH);
> -       memset(qi, 0, sizeof(qi) + INPUT_BUFFER_LENGTH);
> -       qi->info_type = 0x01;
> -       qi->file_info_class = 18;
> -       qi->additional_information = 0;
> -       qi->input_buffer_length = INPUT_BUFFER_LENGTH;
> -
> -       if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
> -               fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
> -               exit(1);
> -       }
> -
> -       print_filebasicinfo((uint8_t *)(&qi[1]));
> -       print_filestandardinfo((uint8_t *)(&qi[1]) + 40);
> -       print_fileinternalinfo((uint8_t *)(&qi[1]) + 64);
> -       print_fileeainfo((uint8_t *)(&qi[1]) + 72);
> -       print_fileaccessinfo((uint8_t *)(&qi[1]) + 76, st.st_mode & S_IFMT);
> -       print_filepositioninfo((uint8_t *)(&qi[1]) + 80);
> -       print_filemodeinfo((uint8_t *)(&qi[1]) + 88);
> -       print_filealigninfo((uint8_t *)(&qi[1]) + 92);
> -       // SMB2 servers like Win16 does not seem to return name info
> -       free(qi);
> -}
> -
> -static void
> -print_sid(unsigned char *sd)
> -{
> -       int i;
> -       uint32_t subauth;
> -       uint64_t idauth;
> -
> -       if (sd[0] != 1) {
> -               fprintf(stderr, "Unknown SID revision\n");
> -               return;
> -       }
> -
> -       idauth = 0;
> -       for (i = 0; i < 6; i++)
> -               idauth = (idauth << 8) | sd[2 + i];
> -
> -       printf("S-1-%" PRIu64, idauth);
> -       for (i = 0; i < sd[1]; i++) {
> -               memcpy(&subauth, &sd[8 + 4 * i], 4);
> -               subauth = le32toh(subauth);
> -               printf("-%u", subauth);
> -       }
> -}
> -
> -static void
> -print_ace_type(uint8_t t)
> -{
> -       switch(t) {
> -       case 0x00: printf("ALLOWED"); break;
> -       case 0x01: printf("DENIED"); break;
> -       case 0x02: printf("AUDIT"); break;
> -       case 0x03: printf("ALARM"); break;
> -       case 0x04: printf("ALLOWED_COMPOUND"); break;
> -       case 0x05: printf("ALLOWED_OBJECT"); break;
> -       case 0x06: printf("DENIED_OBJECT"); break;
> -       case 0x07: printf("AUDIT_OBJECT"); break;
> -       case 0x08: printf("ALARM_OBJECT"); break;
> -       case 0x09: printf("ALLOWED_CALLBACK"); break;
> -       case 0x0a: printf("DENIED_CALLBACK"); break;
> -       case 0x0b: printf("ALLOWED_CALLBACK_OBJECT"); break;
> -       case 0x0c: printf("DENIED_CALLBACK_OBJECT"); break;
> -       case 0x0d: printf("AUDIT_CALLBACK"); break;
> -       case 0x0e: printf("ALARM_CALLBACK"); break;
> -       case 0x0f: printf("AUDIT_CALLBACK_OBJECT"); break;
> -       case 0x10: printf("ALARM_CALLBACK_OBJECT"); break;
> -       case 0x11: printf("MANDATORY_LABEL"); break;
> -       case 0x12: printf("RESOURCE_ATTRIBUTE"); break;
> -       case 0x13: printf("SCOPED_POLICY_ID"); break;
> -       default: printf("<UNKNOWN>");
> -       }
> -       printf(" ");
> -}
> -
> -struct bit_string ace_flags_mask[] = {
> -       { 0x80, "FAILED_ACCESS" },
> -       { 0x40, "SUCCESSFUL_ACCESS" },
> -       { 0x10, "INHERITED" },
> -       { 0x08, "INHERIT_ONLY" },
> -       { 0x04, "NO_PROPAGATE_INHERIT" },
> -       { 0x02, "CONTAINER_INHERIT" },
> -       { 0x01, "OBJECT_INHERIT" },
> -       { 0, NULL }
> -};
> -
> -static void
> -print_mask_sid_ace(unsigned char *sd, int type)
> -{
> -       uint32_t u32;
> -
> -       memcpy(&u32, &sd[0], 4);
> -       printf("Mask:0x%08x ", le32toh(u32));
> -       if (type == S_IFDIR)
> -               print_bits(le32toh(u32), directory_access_mask);
> -       else
> -               print_bits(le32toh(u32), file_access_mask);
> -       printf("SID:");
> -       print_sid(&sd[4]);
> -       printf("\n");
> -}
> -
> -static int
> -print_ace(unsigned char *sd, int type)
> -{
> -       uint16_t size;
> -       int i;
> -
> -       printf("Type:0x%02x ", sd[0]);
> -       if (verbose) {
> -               print_ace_type(sd[0]);
> -       }
> -
> -       printf("Flags:0x%02x ", sd[1]);
> -       print_bits(sd[1], ace_flags_mask);
> -
> -       memcpy(&size, &sd[2], 2);
> -       size = le16toh(size);
> -
> -       switch (sd[0]) {
> -       case 0x00:
> -       case 0x01:
> -       case 0x02:
> -               print_mask_sid_ace(&sd[4], type);
> -               break;
> -       default:
> -               for (i = 0; i < size; i++)
> -                       printf("%02x", sd[4 + i]);
> -       }
> -
> -       printf("\n");
> -       return size;
> -}
> -
> -static void
> -print_acl(unsigned char *sd, int type)
> -{
> -       int i, off;
> -       uint16_t count;
> -
> -       if ((sd[0] != 2) && (sd[0] != 4)) {
> -               fprintf(stderr, "Unknown ACL revision\n");
> -               return;
> -       }
> -
> -       memcpy(&count, &sd[4], 2);
> -       count = le16toh(count);
> -       off = 8;
> -       for (i = 0; i < count; i++)
> -               off += print_ace(&sd[off], type);
> -}
> -
> -struct bit_string control_bits_mask[] = {
> -       { 0x8000, "SR" },
> -       { 0x4000, "RM" },
> -       { 0x2000, "PS" },
> -       { 0x1000, "PD" },
> -       { 0x0800, "SI" },
> -       { 0x0400, "DI" },
> -       { 0x0200, "SC" },
> -       { 0x0100, "DC" },
> -       { 0x0080, "DT" },
> -       { 0x0040, "SS" },
> -       { 0x0020, "SD" },
> -       { 0x0010, "SP" },
> -       { 0x0008, "DD" },
> -       { 0x0004, "DP" },
> -       { 0x0002, "GD" },
> -       { 0x0001, "OD" },
> -       { 0, NULL }
> -};
> -
> -static void
> -print_control(uint16_t c)
> -{
> -       printf("Control: 0x%04x ", c);
> -       print_bits(c, control_bits_mask);
> -       printf("\n");
> -}
> -
> -static void
> -print_sd(uint8_t *sd, int type)
> -{
> -       int offset_owner, offset_group, offset_dacl;
> -       uint16_t u16;
> -
> -       printf("Revision:%d\n", sd[0]);
> -       if (sd[0] != 1) {
> -               fprintf(stderr, "Unknown SD revision\n");
> -               exit(1);
> -       }
> -
> -       memcpy(&u16, &sd[2], 2);
> -       print_control(le16toh(u16));
> -
> -       memcpy(&offset_owner, &sd[4], 4);
> -       offset_owner = le32toh(offset_owner);
> -       memcpy(&offset_group, &sd[8], 4);
> -       offset_group = le32toh(offset_group);
> -       memcpy(&offset_dacl, &sd[16], 4);
> -       offset_dacl = le32toh(offset_dacl);
> -
> -       if (offset_owner) {
> -               printf("Owner: ");
> -               print_sid(&sd[offset_owner]);
> -               printf("\n");
> -       }
> -       if (offset_group) {
> -               printf("Group: ");
> -               print_sid(&sd[offset_group]);
> -               printf("\n");
> -       }
> -       if (offset_dacl) {
> -               printf("DACL:\n");
> -               print_acl(&sd[offset_dacl], type);
> -       }
> -}
> -
> -static void
> -secdesc(int f)
> -{
> -       struct smb_query_info *qi;
> -       struct stat st;
> -
> -       fstat(f, &st);
> -
> -       qi = malloc(sizeof(struct smb_query_info) + INPUT_BUFFER_LENGTH);
> -       memset(qi, 0, sizeof(qi) + INPUT_BUFFER_LENGTH);
> -       qi->info_type = 0x03;
> -       qi->file_info_class = 0;
> -       qi->additional_information = 0x00000007; /* Owner, Group, Dacl */
> -       qi->input_buffer_length = INPUT_BUFFER_LENGTH;
> -
> -       if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
> -               fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
> -               exit(1);
> -       }
> -
> -       print_sd((uint8_t *)(&qi[1]), st.st_mode & S_IFMT);
> -       free(qi);
> -}
> -
> -static void
> -print_quota(unsigned char *sd)
> -{
> -       uint32_t u32, neo;
> -       uint64_t u64;
> -       struct timeval tv;
> -       int i, off = 0;
> -
> -one_more:
> -       memcpy(&u32, &sd[off], 4);
> -       neo = le32toh(u32);
> -
> -       memcpy(&u32, &sd[off + 4], 4);
> -       u32 = le32toh(u32);
> -       printf("SID Length %d\n", u32);
> -
> -       memcpy(&u64, &sd[off + 8], 8);
> -       win_to_timeval(le64toh(u64), &tv);
> -       printf("Change Time %s", ctime(&tv.tv_sec));
> -
> -       memcpy(&u64, &sd[off + 16], 8);
> -       u64 = le32toh(u64);
> -       printf("Quota Used %" PRIu64 "\n", u64);
> -
> -       memcpy(&u64, &sd[off + 24], 8);
> -       u64 = le64toh(u64);
> -       if (u64 == 0xffffffffffffffff)
> -               printf("Quota Threshold NO THRESHOLD\n");
> -       else
> -               printf("Quota Threshold %" PRIu64 "\n", u64);
> -
> -       memcpy(&u64, &sd[off + 32], 8);
> -       u64 = le64toh(u64);
> -       if (u64 == 0xffffffffffffffff)
> -               printf("Quota Limit NO LIMIT\n");
> -       else
> -               printf("Quota Limit %" PRIu64 "\n", u64);
> -
> -       printf("SID: S-1");
> -       u64 = 0;
> -       for (i = 0; i < 6; i++)
> -               u64 = (u64 << 8) | sd[off + 42 + i];
> -       printf("-%" PRIu64, u64);
> -
> -       for (i = 0; i < sd[off + 41]; i++) {
> -               memcpy(&u32, &sd[off + 48 + 4 * i], 4);
> -               u32 = le32toh(u32);
> -               printf("-%u", u32);
> -       }
> -       printf("\n\n");
> -       off += neo;
> -
> -       if (neo != 0)
> -               goto one_more;
> -}
> -
> -static void
> -quota(int f)
> -{
> -       struct smb_query_info *qi;
> -       char *buf;
> -       int i;
> -
> -       qi = malloc(sizeof(struct smb_query_info) + INPUT_BUFFER_LENGTH);
> -       memset(qi, 0, sizeof(struct smb_query_info) + INPUT_BUFFER_LENGTH);
> -       qi->info_type = 0x04;
> -       qi->file_info_class = 0;
> -       qi->additional_information = 0; /* Owner, Group, Dacl */
> -       qi->input_buffer_length = INPUT_BUFFER_LENGTH;
> -
> -       buf = (char *)&qi[1];
> -       buf[0] = 0; /* return single */
> -       buf[1] = 1; /* restart scan */
> -
> -       /* sid list length */
> -       i = 0;
> -       memcpy(&buf[4], &i, 4);
> -
> -       qi->output_buffer_length = 16;
> -
> -       if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
> -               fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
> -               exit(1);
> -       }
> -
> -       print_quota((unsigned char *)(&qi[1]));
> -       free(qi);
> -}
> -
> -
> -struct smb_snapshot_array {
> -       int32_t number_of_snapshots;
> -       int32_t number_of_snapshots_returned;
> -       int32_t snapshot_array_size;
> -       char snapshot_data[0];
> -};
> -
> -
> -#define GMT_NAME_LEN 24 /* length of a @GMT- name */
> -#define GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
> -
> -#define NTFS_TIME_OFFSET ((unsigned long long)(369*365 + 89) * 24 * 3600 * 10000000)
> -
> -static void print_snapshots(struct smb_snapshot_array *psnap)
> -{
> -       int current_snapshot_entry = 0;
> -       char gmt_token[GMT_NAME_LEN + 1] = {0};
> -       int i;
> -       int j = 0;
> -       struct tm tm;
> -       unsigned long long dce_time;
> -
> -       printf("Number of snapshots: %d Number of snapshots returned: %d\n",
> -               psnap->number_of_snapshots,
> -               psnap->number_of_snapshots_returned);
> -       printf("Snapshot list in GMT (Coordinated UTC Time) and SMB format (100 nanosecond units needed for snapshot mounts):");
> -       for (i = 0; i < psnap->snapshot_array_size; i++) {
> -               if (psnap->snapshot_data[i] == '@') {
> -                       j = 0;
> -                       current_snapshot_entry++;
> -                       printf("\n%d) GMT:", current_snapshot_entry);
> -               }
> -               if (psnap->snapshot_data[i] != 0) {
> -                       gmt_token[j] = psnap->snapshot_data[i];
> -                       j++;
> -               }
> -               if (j == GMT_NAME_LEN) {
> -                       printf("%s", gmt_token);
> -                       j = 0;
> -                       strptime(gmt_token, GMT_FORMAT, &tm);
> -                       dce_time = timegm(&tm) * 10000000 + NTFS_TIME_OFFSET;
> -                       printf("\n   SMB3:%llu", dce_time);
> -               }
> -       }
> -       printf("\n");
> -}
> -
> -static void
> -dump_keys(int f)
> -{
> -       struct smb3_key_debug_info keys_info;
> -       uint8_t *psess_id;
> -
> -       if (ioctl(f, CIFS_DUMP_KEY, &keys_info) < 0) {
> -               fprintf(stderr, "Querying keys information failed with %s\n", strerror(errno));
> -               exit(1);
> -       }
> -
> -       if (keys_info.cipher_type == 1)
> -               printf("CCM encryption");
> -       else if (keys_info.cipher_type == 2)
> -               printf("GCM encryption");
> -       else if (keys_info.cipher_type == 0)
> -               printf("SMB3.0 CCM encryption");
> -       else
> -               printf("unknown encryption type");
> -
> -       printf("\nSession Id:  ");
> -       psess_id = (uint8_t *)&keys_info.Suid;
> -       for (int i = 0; i < 8; i++)
> -               printf(" %02x", psess_id[i]);
> -
> -       printf("\nSession Key: ");
> -       for (int i = 0; i < 16; i++)
> -               printf(" %02x", keys_info.auth_key[i]);
> -       printf("\nServer Encryption Key: ");
> -       for (int i = 0; i < SMB3_SIGN_KEY_SIZE; i++)
> -               printf(" %02x", keys_info.smb3encryptionkey[i]);
> -       printf("\nServer Decryption Key: ");
> -       for (int i = 0; i < SMB3_SIGN_KEY_SIZE; i++)
> -               printf(" %02x", keys_info.smb3decryptionkey[i]);
> -       printf("\n");
> -}
> -
> -#define CIFS_ENUMERATE_SNAPSHOTS _IOR(CIFS_IOCTL_MAGIC, 6, struct smb_snapshot_array)
> -
> -#define MIN_SNAPSHOT_ARRAY_SIZE 16 /* See MS-SMB2 section 3.3.5.15.1 */
> -
> -static void
> -list_snapshots(int f)
> -{
> -
> -       struct smb_snapshot_array snap_inf;
> -       struct smb_snapshot_array *buf;
> -
> -       /*
> -        * When first field in structure we pass in here is zero, cifs.ko can
> -        * recognize that this is the first query and that it must set the SMB3
> -        * FSCTL response buffer size (in the request) to exactly 16 bytes
> -        * (which is required by some servers to process the initial query)
> -        */
> -       snap_inf.number_of_snapshots = 0;
> -       snap_inf.number_of_snapshots_returned = 0;
> -       snap_inf.snapshot_array_size = sizeof(struct smb_snapshot_array);
> -
> -       /* Query the number of snapshots so we know how much to allocate */
> -       if (ioctl(f, CIFS_ENUMERATE_SNAPSHOTS, &snap_inf) < 0) {
> -               fprintf(stderr, "Querying snapshots failed with %s\n", strerror(errno));
> -               exit(1);
> -       }
> -
> -       if (snap_inf.number_of_snapshots == 0)
> -               return;
> -
> -       /* Now that we know the size, query the list from the server */
> -
> -       buf = malloc(snap_inf.snapshot_array_size + MIN_SNAPSHOT_ARRAY_SIZE);
> -
> -       if (buf == NULL) {
> -               printf("Failed, out of memory.\n");
> -               exit(1);
> -       }
> -       /*
> -        * first parm is non-zero which allows cifs.ko to recognize that this is
> -        * the second query (it has to set response buf size larger)
> -        */
> -       buf->number_of_snapshots = snap_inf.number_of_snapshots;
> -
> -       buf->snapshot_array_size = snap_inf.snapshot_array_size;
> -
> -       if (ioctl(f, CIFS_ENUMERATE_SNAPSHOTS, buf) < 0) {
> -               fprintf(stderr, "Querying snapshots failed with %s\n", strerror(errno));
> -               exit(1);
> -       }
> -
> -       print_snapshots(buf);
> -       free(buf);
> -}
> -
> -static int
> -parse_compression(const char *arg)
> -{
> -       if (!strcmp(arg, "no"))
> -               return 0;
> -       else if (!strcmp(arg, "default"))
> -               return 1;
> -       else if (!strcmp(arg, "lznt1"))
> -               return 2;
> -
> -       fprintf(stderr, "compression must be no|default|lznt1\n");
> -       exit(10);
> -}
> -
> -int main(int argc, char *argv[])
> -{
> -       int c;
> -       int f;
> -       int compression = 1;
> -
> -       if (argc < 2) {
> -               short_usage(argv[0]);
> -       }
> -
> -       while ((c = getopt_long(argc, argv, "c:vVh", NULL, NULL)) != -1) {
> -               switch (c) {
> -               case 'c':
> -                       compression = parse_compression(optarg);
> -                       break;
> -               case 'v':
> -                       printf("smbinfo version %s\n", VERSION);
> -                       return 0;
> -               case 'V':
> -                       verbose = 1;
> -                       break;
> -               case 'h':
> -                       usage(argv[0]);
> -                       break;
> -               default:
> -                       short_usage(argv[0]);
> -               }
> -       }
> -
> -       if (optind >= argc -1)
> -               short_usage(argv[0]);
> -
> -       if ((f = open(argv[optind + 1 ], O_RDONLY)) < 0) {
> -               fprintf(stderr, "Failed to open %s\n", argv[optind + 1]);
> -               exit(1);
> -       }
> -
> -       if (!strcmp(argv[optind], "fileaccessinfo"))
> -               fileaccessinfo(f);
> -       else if (!strcmp(argv[optind], "filealigninfo"))
> -               filealigninfo(f);
> -       else if (!strcmp(argv[optind], "fileallinfo"))
> -               fileallinfo(f);
> -       else if (!strcmp(argv[optind], "filebasicinfo"))
> -               filebasicinfo(f);
> -       else if (!strcmp(argv[optind], "fileeainfo"))
> -               fileeainfo(f);
> -       else if (!strcmp(argv[optind], "filefsfullsizeinfo"))
> -               filefsfullsizeinfo(f);
> -       else if (!strcmp(argv[optind], "fileinternalinfo"))
> -               fileinternalinfo(f);
> -       else if (!strcmp(argv[optind], "filemodeinfo"))
> -               filemodeinfo(f);
> -       else if (!strcmp(argv[optind], "filepositioninfo"))
> -               filepositioninfo(f);
> -       else if (!strcmp(argv[optind], "filestandardinfo"))
> -               filestandardinfo(f);
> -       else if (!strcmp(argv[optind], "fsctl-getobjid"))
> -               fsctlgetobjid(f);
> -       else if (!strcmp(argv[optind], "getcompression"))
> -               getcompression(f);
> -       else if (!strcmp(argv[optind], "setcompression"))
> -               setcompression(f, compression);
> -       else if (!strcmp(argv[optind], "list-snapshots"))
> -               list_snapshots(f);
> -       else if (!strcmp(argv[optind], "quota"))
> -               quota(f);
> -       else if (!strcmp(argv[optind], "secdesc"))
> -               secdesc(f);
> -       else if (!strcmp(argv[optind], "keys"))
> -               dump_keys(f);
> -       else {
> -               fprintf(stderr, "Unknown command %s\n", argv[optind]);
> -               exit(1);
> -       }
> -
> -       close(f);
> -       return 0;
> -}
> --
> 2.16.4
>

Great work, thanks!

I am planning to merge this for the next after next release since it
doesn't add new functionality but also bring some risk. So, we will
have time to catch possible regressions here.

--
Best regards,
Pavel Shilovsky
Pavel Shilovsky Dec. 17, 2019, 10:36 p.m. UTC | #4
чт, 12 дек. 2019 г. в 16:33, Pavel Shilovsky <piastryyy@gmail.com>:
>
...
>
> I am planning to merge this for the next after next release since it
> doesn't add new functionality but also bring some risk. So, we will
> have time to catch possible regressions here.

Tentatively merged:

https://github.com/piastry/cifs-utils/commit/b7bd342c6d4dc794dd29760d1417e8a87849b531

Btw, I noticed differences between the current smbinfo help output:

$ smbinfo
Usage: smbinfo [-v] [-V] <command> <file>
Try 'smbinfo -h' for more information.

and the new one:

$ ./smbinfo
usage: smbinfo [-h] [-V]
               {fileaccessinfo,filealigninfo,fileallinfo,filebasicinfo,fileeainfo,filefsfullsizeinfo,fileinternalinfo,filemodeinfo,filepositioninfo,filestandardinfo,fsctl-getobjid,getcompression,setcompression,list-snapshots,quota,secdesc,keys}
               ...
smbinfo: error: the following arguments are required: subcommand


We should probably keep the output the same if possible. I personally
like the original version better because it is shorter - for more
details the user may type smbinfo -h to get the list of supported
commands. But no strong opinion here.

--
Best regards,
Pavel Shilovsky
diff mbox series

Patch

diff --git a/Makefile.am b/Makefile.am
index 8291b99..33a420c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -80,8 +80,7 @@  setcifsacl.rst: setcifsacl.rst.in
 endif
 
 if CONFIG_SMBINFO
-bin_PROGRAMS += smbinfo
-smbinfo_SOURCES = smbinfo.c
+bin_SCRIPTS = smbinfo
 rst_man_pages += smbinfo.1
 endif
 
diff --git a/smbinfo b/smbinfo
new file mode 100755
index 0000000..1be82c7
--- /dev/null
+++ b/smbinfo
@@ -0,0 +1,779 @@ 
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# smbinfo is a cmdline tool to query SMB-specific file and fs
+# information on a Linux SMB mount (cifs.ko).
+#
+# Copyright (C) 2019 Aurelien Aptel <aaptel@suse.com>
+# Copyright (C) 2019 Ronnie Sahlberg <lsahlberg@redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import os
+import re
+import argparse
+import fcntl
+import struct
+import stat
+import datetime
+
+VERBOSE = False
+
+# ioctl ctl codes
+CIFS_QUERY_INFO          = 0xc018cf07
+CIFS_ENUMERATE_SNAPSHOTS = 0x800ccf06
+CIFS_DUMP_KEY            = 0xc03acf08
+
+# large enough input buffer length
+INPUT_BUFFER_LENGTH = 16384
+
+# cifs query flags
+PASSTHRU_QUERY_INFO = 0x00000000
+PASSTHRU_FSCTL      = 0x00000001
+
+DIR_ACCESS_FLAGS = [
+    (0x00000001, "LIST_DIRECTORY"),
+    (0x00000002, "ADD_FILE"),
+    (0x00000004, "ADD_SUBDIRECTORY"),
+    (0x00000008, "READ_EA"),
+    (0x00000010, "WRITE_EA"),
+    (0x00000020, "TRAVERSE"),
+    (0x00000040, "DELETE_CHILD"),
+    (0x00000080, "READ_ATTRIBUTES"),
+    (0x00000100, "WRITE_ATTRIBUTES"),
+    (0x00010000, "DELETE"),
+    (0x00020000, "READ_CONTROL"),
+    (0x00040000, "WRITE_DAC"),
+    (0x00080000, "WRITE_OWNER"),
+    (0x00100000, "SYNCHRONIZER"),
+    (0x01000000, "ACCESS_SYSTEM_SECURITY"),
+    (0x02000000, "MAXIMUM_ALLOWED"),
+    (0x10000000, "GENERIC_ALL"),
+    (0x20000000, "GENERIC_EXECUTE"),
+    (0x40000000, "GENERIC_WRITE"),
+    (0x80000000, "GENERIC_READ"),
+]
+
+FILE_ACCESS_FLAGS = [
+    (0x00000001, "READ_DATA"),
+    (0x00000002, "WRITE_DATA"),
+    (0x00000004, "APPEND_DATA"),
+    (0x00000008, "READ_EA"),
+    (0x00000010, "WRITE_EA"),
+    (0x00000020, "EXECUTE"),
+    (0x00000040, "DELETE_CHILD"),
+    (0x00000080, "READ_ATTRIBUTES"),
+    (0x00000100, "WRITE_ATTRIBUTES"),
+    (0x00010000, "DELETE"),
+    (0x00020000, "READ_CONTROL"),
+    (0x00040000, "WRITE_DAC"),
+    (0x00080000, "WRITE_OWNER"),
+    (0x00100000, "SYNCHRONIZER"),
+    (0x01000000, "ACCESS_SYSTEM_SECURITY"),
+    (0x02000000, "MAXIMUM_ALLOWED"),
+    (0x10000000, "GENERIC_ALL"),
+    (0x20000000, "GENERIC_EXECUTE"),
+    (0x40000000, "GENERIC_WRITE"),
+    (0x80000000, "GENERIC_READ"),
+]
+
+FILE_ATTR_FLAGS = [
+    (0x00000001, "READ_ONLY"),
+    (0x00000002, "HIDDEN"),
+    (0x00000004, "SYSTEM"),
+    (0x00000010, "DIRECTORY"),
+    (0x00000020, "ARCHIVE"),
+    (0x00000080, "NORMAL"),
+    (0x00000100, "TEMPORARY"),
+    (0x00000200, "SPARSE_FILE"),
+    (0x00000400, "REPARSE_POINT"),
+    (0x00000800, "COMPRESSED"),
+    (0x00001000, "OFFLINE"),
+    (0x00002000, "NOT_CONTENT_INDEXED"),
+    (0x00004000, "ENCRYPTED"),
+    (0x00008000, "INTEGRITY_STREAM"),
+    (0x00020000, "NO_SCRUB_DATA"),
+]
+
+FILE_MODE_FLAGS = [
+    (0x00000002, "WRITE_THROUGH"),
+    (0x00000004, "SEQUENTIAL_ONLY"),
+    (0x00000008, "NO_INTERMEDIATE_BUFFERING"),
+    (0x00000010, "SYNCHRONOUS_IO_ALERT"),
+    (0x00000020, "SYNCHRONOUS_IO_NONALERT"),
+    (0x00001000, "DELETE_ON_CLOSE"),
+]
+
+ALIGN_TYPES = [
+    (0, "BYTE_ALIGNMENT"),
+    (1, "WORD_ALIGNMENT"),
+    (3, "LONG_ALIGNMENT"),
+    (7, "QUAD_ALIGNMENT"),
+    (15, "OCTA_ALIGNMENT"),
+    (31, "32_bit_ALIGNMENT"),
+    (63, "64_bit_ALIGNMENT"),
+    (127, "128_bit_ALIGNMENT"),
+    (255, "254_bit_ALIGNMENT"),
+    (511, "512_bit_ALIGNMENT"),
+]
+
+COMPRESSION_TYPES = [
+    (0x0000, "NONE"),
+    (0x0002, "LZNT1"),
+]
+
+CONTROL_FLAGS = [
+    (0x8000, "SR"),
+    (0x4000, "RM"),
+    (0x2000, "PS"),
+    (0x1000, "PD"),
+    (0x0800, "SI"),
+    (0x0400, "DI"),
+    (0x0200, "SC"),
+    (0x0100, "DC"),
+    (0x0080, "DT"),
+    (0x0040, "SS"),
+    (0x0020, "SD"),
+    (0x0010, "SP"),
+    (0x0008, "DD"),
+    (0x0004, "DP"),
+    (0x0002, "GD"),
+    (0x0001, "OD"),
+]
+
+ACE_TYPES = [
+    (0x00, "ALLOWED"),
+    (0x01, "DENIED"),
+    (0x02, "AUDIT"),
+    (0x03, "ALARM"),
+    (0x04, "ALLOWED_COMPOUND"),
+    (0x05, "ALLOWED_OBJECT"),
+    (0x06, "DENIED_OBJECT"),
+    (0x07, "AUDIT_OBJECT"),
+    (0x08, "ALARM_OBJECT"),
+    (0x09, "ALLOWED_CALLBACK"),
+    (0x0a, "DENIED_CALLBACK"),
+    (0x0b, "ALLOWED_CALLBACK_OBJECT"),
+    (0x0c, "DENIED_CALLBACK_OBJECT"),
+    (0x0d, "AUDIT_CALLBACK"),
+    (0x0e, "ALARM_CALLBACK"),
+    (0x0f, "AUDIT_CALLBACK_OBJECT"),
+    (0x10, "ALARM_CALLBACK_OBJECT"),
+    (0x11, "MANDATORY_LABEL"),
+    (0x12, "RESOURCE_ATTRIBUTE"),
+    (0x13, "SCOPED_POLICY_ID"),
+]
+
+ACE_FLAGS = [
+    (0x80, "FAILED_ACCESS"),
+    (0x40, "SUCCESSFUL_ACCESS"),
+    (0x10, "INHERITED"),
+    (0x08, "INHERIT_ONLY"),
+    (0x04, "NO_PROPAGATE_INHERIT"),
+    (0x02, "CONTAINER_INHERIT"),
+    (0x01, "OBJECT_INHERIT"),
+]
+
+CIPHER_TYPES = [
+    (0x00, "SMB3.0 CCM encryption"),
+    (0x01, "CCM encryption"),
+    (0x02, "GCM encryption"),
+]
+
+def main():
+    #
+    # Global options and arguments
+    #
+
+    ap = argparse.ArgumentParser(description="Display SMB-specific file information using cifs IOCTL")
+    ap.add_argument("-V", "--verbose", action="store_true", help="verbose output")
+    subp = ap.add_subparsers(help="sub-commands help")
+    subp.required = True
+    subp.dest = 'subcommand'
+
+    #
+    # To add a new sub-command xxx, add a subparser xxx complete with
+    # help, options and/or arguments and implement cmd_xxx()
+    #
+
+    sap = subp.add_parser("fileaccessinfo", help="Prints FileAccessInfo for a cifs file")
+    sap.add_argument("file")
+    sap.set_defaults(func=cmd_fileaccessinfo)
+
+    sap = subp.add_parser("filealigninfo", help="Prints FileAlignInfo for a cifs file")
+    sap.add_argument("file")
+    sap.set_defaults(func=cmd_filealigninfo)
+
+    sap = subp.add_parser("fileallinfo", help="Prints FileAllInfo for a cifs file")
+    sap.add_argument("file")
+    sap.set_defaults(func=cmd_fileallinfo)
+
+    sap = subp.add_parser("filebasicinfo", help="Prints FileBasicInfo for a cifs file")
+    sap.add_argument("file")
+    sap.set_defaults(func=cmd_filebasicinfo)
+
+    sap = subp.add_parser("fileeainfo", help="Prints FileEAInfo for a cifs file")
+    sap.add_argument("file")
+    sap.set_defaults(func=cmd_fileeainfo)
+
+    sap = subp.add_parser("filefsfullsizeinfo", help="Prints FileFsFullSizeInfo for a cifs file")
+    sap.add_argument("file")
+    sap.set_defaults(func=cmd_filefsfullsizeinfo)
+
+    sap = subp.add_parser("fileinternalinfo", help="Prints FileInternalInfo for a cifs file")
+    sap.add_argument("file")
+    sap.set_defaults(func=cmd_fileinternalinfo)
+
+    sap = subp.add_parser("filemodeinfo", help="Prints FileModeInfo for a cifs file")
+    sap.add_argument("file")
+    sap.set_defaults(func=cmd_filemodeinfo)
+
+    sap = subp.add_parser("filepositioninfo", help="Prints FilePositionInfo for a cifs file")
+    sap.add_argument("file")
+    sap.set_defaults(func=cmd_filepositioninfo)
+
+    sap = subp.add_parser("filestandardinfo", help="Prints FileStandardInfo for a cifs file")
+    sap.add_argument("file")
+    sap.set_defaults(func=cmd_filestandardinfo)
+
+    sap = subp.add_parser("fsctl-getobjid", help="Prints the objectid of the file and GUID of the underlying volume.")
+    sap.add_argument("file")
+    sap.set_defaults(func=cmd_fsctl_getobjid)
+
+    sap = subp.add_parser("getcompression", help="Prints the compression setting for the file")
+    sap.add_argument("file")
+    sap.set_defaults(func=cmd_getcompression)
+
+    sap = subp.add_parser("setcompression", help="Sets the compression level for the file")
+    sap.add_argument("type", choices=['no','default','lznt1'])
+    sap.add_argument("file")
+    sap.set_defaults(func=cmd_setcompression)
+
+    sap = subp.add_parser("list-snapshots", help="List the previous versions of the volume that backs this file")
+    sap.add_argument("file")
+    sap.set_defaults(func=cmd_list_snapshots)
+
+    sap = subp.add_parser("quota", help="Prints the quota for a cifs file")
+    sap.add_argument("file")
+    sap.set_defaults(func=cmd_quota)
+
+    sap = subp.add_parser("secdesc", help="Prints the security descriptor for a cifs file")
+    sap.add_argument("file")
+    sap.set_defaults(func=cmd_secdesc)
+
+    sap = subp.add_parser("keys", help="Prints the decryption information needed to view encrypted network traces")
+    sap.add_argument("file")
+    sap.set_defaults(func=cmd_keys)
+
+    # parse arguments
+    args = ap.parse_args()
+
+    # act on any global options
+    if args.verbose:
+        global VERBOSE
+        VERBOSE = True
+
+    # call subcommand function
+    args.func(args)
+
+class QueryInfoStruct:
+    def __init__(self,
+                 info_type=0, file_info_class=0, additional_information=0,
+                 flags=0, input_buffer_length=0, output_buffer_length=0):
+        self.info_type = info_type
+        self.file_info_class = file_info_class
+        self.additional_information = additional_information
+        self.flags = flags
+        self.input_buffer_length = input_buffer_length
+        self.output_buffer_length = output_buffer_length
+        buf_size = max(self.input_buffer_length, self.output_buffer_length)
+        self.input_buffer = bytearray(buf_size)
+
+    def pack_input(self, fmt, offset, *vals):
+        struct.pack_into(fmt, self.input_buffer, offset, *vals)
+
+    def ioctl(self, fd, out_fmt=None):
+        buf = bytearray()
+        buf.extend(struct.pack("IIIIII",
+                               self.info_type,
+                               self.file_info_class,
+                               self.additional_information,
+                               self.flags,
+                               self.input_buffer_length,
+                               self.output_buffer_length))
+        in_len = len(buf)
+        buf.extend(self.input_buffer)
+        fcntl.ioctl(fd, CIFS_QUERY_INFO, buf, True)
+        if out_fmt:
+            return struct.unpack_from(out_fmt, buf, in_len)
+        else:
+            return buf[in_len:]
+
+def flags_to_str(flags, bitlist, verbose=None):
+    if verbose is None:
+        verbose = VERBOSE
+
+    if not verbose:
+        return "0x%08x"%flags
+
+    out = []
+    for bit, name in bitlist:
+        if flags & bit:
+            out.append(name)
+
+    return "0x%08x (%s)"%(flags, ",".join(out))
+
+def type_to_str(typ, typelist, verbose=None):
+    if verbose is None:
+        verbose = VERBOSE
+
+    if not verbose:
+        return "0x%08x"%typ
+
+    s = "Unknown"
+    for val, name in typelist:
+        if typ == val:
+            s = name
+
+    return "0x%08x (%s)"%(typ, s)
+
+def cmd_fileaccessinfo(args):
+    qi = QueryInfoStruct(info_type=0x1, file_info_class=8, input_buffer_length=4)
+    try:
+        fd = os.open(args.file, os.O_RDONLY)
+        info = os.fstat(fd)
+        buf = qi.ioctl(fd)
+    except Exception as e:
+        print("syscall failed: %s"%e)
+        return False
+
+    print_fileaccessinfo(buf, info)
+
+def print_fileaccessinfo(buf, info):
+    flags = struct.unpack_from('<I', buf, 0)[0]
+    if stat.S_ISDIR(info.st_mode):
+        print("Directory access flags:", flags_to_str(flags, DIR_ACCESS_FLAGS))
+    else:
+        print("File/Printer access flags:", flags_to_str(flags, FILE_ACCESS_FLAGS))
+
+def cmd_filealigninfo(args):
+    qi = QueryInfoStruct(info_type=0x1, file_info_class=17, input_buffer_length=4)
+    try:
+        fd = os.open(args.file, os.O_RDONLY)
+        buf = qi.ioctl(fd)
+    except Exception as e:
+        print("syscall failed: %s"%e)
+        return False
+
+    print_filealigninfo(buf)
+
+def print_filealigninfo(buf):
+    mask = struct.unpack_from('<I', buf, 0)[0]
+    print("File alignment: %s"%type_to_str(mask, ALIGN_TYPES))
+
+def cmd_fileallinfo(args):
+    qi = QueryInfoStruct(info_type=0x1, file_info_class=18, input_buffer_length=INPUT_BUFFER_LENGTH)
+    try:
+        fd = os.open(args.file, os.O_RDONLY)
+        info = os.fstat(fd)
+        buf = qi.ioctl(fd)
+    except Exception as e:
+        print("syscall failed: %s"%e)
+        return False
+
+    print_filebasicinfo(buf)
+    print_filestandardinfo(buf[40:])
+    print_fileinternalinfo(buf[64:])
+    print_fileeainfo(buf[72:])
+    print_fileaccessinfo(buf[76:], info)
+    print_filepositioninfo(buf[80:])
+    print_filemodeinfo(buf[88:])
+    print_filealigninfo(buf[92:])
+
+def win_to_datetime(smb2_time):
+    usec = (smb2_time / 10) % 1000000
+    sec  = (smb2_time - 116444736000000000) // 10000000
+    return datetime.datetime.fromtimestamp(sec + usec/10000000)
+
+def cmd_filebasicinfo(args):
+    qi = QueryInfoStruct(info_type=0x1, file_info_class=4, input_buffer_length=40)
+    try:
+        fd = os.open(args.file, os.O_RDONLY)
+        buf = qi.ioctl(fd)
+    except Exception as e:
+        print("syscall failed: %s"%e)
+        return False
+
+    print_filebasicinfo(buf)
+
+def print_filebasicinfo(buf):
+    ctime, atime, wtime, mtime, attrs = struct.unpack_from('<QQQQI', buf, 0)
+    print("Creation Time: %s"%win_to_datetime(ctime))
+    print("Last Access Time: %s"%win_to_datetime(atime))
+    print("Last Write Time: %s"%win_to_datetime(wtime))
+    print("Last Change Time: %s"%win_to_datetime(mtime))
+    print("File Attributes: %s"%flags_to_str(attrs, FILE_ATTR_FLAGS))
+
+def cmd_fileeainfo(args):
+    qi = QueryInfoStruct(info_type=0x1, file_info_class=7, input_buffer_length=4)
+    try:
+        fd = os.open(args.file, os.O_RDONLY)
+        buf = qi.ioctl(fd)
+    except Exception as e:
+        print("syscall failed: %s"%e)
+        return False
+
+    print_fileeainfo(buf)
+
+def print_fileeainfo(buf):
+    size = struct.unpack_from('<I', buf, 0)[0]
+    print("EA Size: %d"%size)
+
+def cmd_filefsfullsizeinfo(args):
+    qi = QueryInfoStruct(info_type=0x2, file_info_class=7, input_buffer_length=32)
+    try:
+        fd = os.open(args.file, os.O_RDONLY)
+        total, caller_avail, actual_avail, sec_per_unit, byte_per_sec = qi.ioctl(fd, CIFS_QUERY_INFO, '<QQQII')
+    except Exception as e:
+        print("syscall failed: %s"%e)
+        return False
+
+    print("Total Allocation Units: %d"%total)
+    print("Caller Available Allocation Units: %d"%caller_avail)
+    print("Actual Available Allocation Units: %d"%actual_avail)
+    print("Sectors Per Allocation Unit: %d"%sec_per_unit)
+    print("Bytes Per Sector: %d"%byte_per_sec)
+
+def cmd_fileinternalinfo(args):
+    qi = QueryInfoStruct(info_type=0x1, file_info_class=6, input_buffer_length=8)
+    try:
+        fd = os.open(args.file, os.O_RDONLY)
+        buf = qi.ioctl(fd)
+    except Exception as e:
+        print("syscall failed: %s"%e)
+        return False
+
+    print_fileinternalinfo(buf)
+
+def print_fileinternalinfo(buf):
+    index = struct.unpack_from('<Q', buf, 0)[0]
+    print("Index Number: %d"%index)
+
+
+def cmd_filemodeinfo(args):
+    qi = QueryInfoStruct(info_type=0x1, file_info_class=16, input_buffer_length=4)
+    try:
+        fd = os.open(args.file, os.O_RDONLY)
+        buf = qi.ioctl(fd)
+    except Exception as e:
+        print("syscall failed: %s"%e)
+        return False
+
+    print_filemodeinfo(buf)
+
+def print_filemodeinfo(buf):
+        mode = struct.unpack_from('<I', buf, 0)[0]
+        print("Mode: %s"%flags_to_str(mode, FILE_MODE_FLAGS))
+
+def cmd_filepositioninfo(args):
+    qi = QueryInfoStruct(info_type=0x1, file_info_class=14, input_buffer_length=8)
+    try:
+        fd = os.open(args.file, os.O_RDONLY)
+        buf = qi.ioctl(fd)
+    except Exception as e:
+        print("syscall failed: %s"%e)
+        return False
+
+    print_filepositioninfo(buf)
+
+def print_filepositioninfo(buf):
+    offset = struct.unpack_from('<Q', buf, 0)[0]
+    print("Current Byte Offset: %d"%offset)
+
+def cmd_filestandardinfo(args):
+    qi = QueryInfoStruct(info_type=0x1, file_info_class=5, input_buffer_length=24)
+    try:
+        fd = os.open(args.file, os.O_RDONLY)
+        buf = qi.ioctl(fd)
+    except Exception as e:
+        print("syscall failed: %s"%e)
+        return False
+
+    print_filestandardinfo(buf)
+
+def print_filestandardinfo(buf):
+    nalloc, eof, nlink, del_pending, del_dir = struct.unpack_from('<QQIBB', buf, 0)
+    print("Allocation Size: %d"%nalloc)
+    print("End Of File: %d"%eof)
+    print("Number of Links: %d"%nlink)
+    print("Delete Pending: %d"%del_pending)
+    print("Delete Directory: %d"%del_dir)
+
+def guid_to_str(buf):
+    return "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"%struct.unpack_from('<ISSBBBBBBBB', buf, 0)
+
+def cmd_fsctl_getobjid(args):
+    qi = QueryInfoStruct(info_type=0x9009c, file_info_class=5, flags=PASSTHRU_FSCTL, input_buffer_length=64)
+    try:
+        fd = os.open(args.file, os.O_RDONLY)
+        buf = qi.ioctl(fd)
+    except Exception as e:
+        print("syscall failed: %s"%e)
+        return False
+
+    print("Object-ID: %s"%guid_to_str(buf))
+    print("Birth-Volume-ID: %s"%guid_to_str(buf[16:]))
+    print("Birth-Object-ID: %s"%guid_to_str(buf[32:]))
+    print("Domain-ID: %s"%guid_to_str(buf[48:]))
+
+def cmd_getcompression(args):
+    qi = QueryInfoStruct(info_type=0x9003c, flags=PASSTHRU_FSCTL, input_buffer_length=2)
+    try:
+        fd = os.open(args.file, os.O_RDONLY)
+        ctype = qi.ioctl(fd, CIFS_QUERY_INFO, '<H')[0]
+    except Exception as e:
+        print("syscall failed: %s"%e)
+        return False
+
+    ctype_name = "UNKNOWN"
+    for val, name in COMPRESSION_TYPES:
+        if ctype == val:
+            ctype_name = name
+            break
+    print("Compression: %d (%s)"%(ctype, ctype_name))
+
+def cmd_setcompression(args):
+    qi = QueryInfoStruct(info_type=0x9c040, flags=PASSTHRU_FSCTL, output_buffer_length=2)
+    type_map = {'no': 0, 'default': 1, 'lznt1': 2}
+    qi.pack_input('<H', 0, type_map[args.type])
+    try:
+        fd = os.open(args.file, os.O_RDONLY)
+        qi.ioctl(fd)
+    except Exception as e:
+        print("syscall failed: %s"%e)
+        return False
+
+class SnapshotArrayStruct:
+    def __init__(self,
+                 nb_snapshots=0,
+                 nb_snapshots_returned=0,
+                 snapshot_array_size=12):
+        self.nb_snapshots = nb_snapshots
+        self.nb_snapshots_returned = nb_snapshots_returned
+        self.snapshot_array_size = snapshot_array_size
+        self.snapshot_array = []
+
+    def ioctl(self, fd, op):
+        buf = bytearray()
+        buf.extend(struct.pack("III",
+                               self.nb_snapshots,
+                               self.nb_snapshots_returned,
+                               self.snapshot_array_size))
+
+        buf.extend(bytearray(16 + self.snapshot_array_size))
+        fcntl.ioctl(fd, op, buf, True)
+
+        out = SnapshotArrayStruct()
+        out.nb_snapshots, out.nb_snapshots_returned, out.snapshot_array_size = struct.unpack_from('III', buf, 0)
+        data = buf[12:]
+
+        for gmt in re.findall(rb'''@([^\x00]+)''', data):
+            d = datetime.strptime("@GMT-%Y.%m.%d-%H.%M.%S")
+            out.snapshot_array.append(d)
+
+        return out
+
+def datetime_to_smb(dt):
+    ntfs_time_offset = (369*365 + 89) * 24 * 3600 * 10000000
+    return datetime.datetime.timestamp(dt) * 10000000 + ntfs_time_offset
+
+def cmd_list_snapshots(args):
+    sa1req = SnapshotArrayStruct()
+    sa1res = None
+    sa2req = None
+    sa2res = None
+
+    try:
+        fd = os.open(args.file, os.O_RDONLY)
+        sa1res = sa1req.ioctl(fd, CIFS_ENUMERATE_SNAPSHOTS)
+    except Exception as e:
+        print("syscall failed: %s"%e)
+        return False
+
+    if sa1res.nb_snapshots == 0:
+        return
+
+    sa2req = SnapshotArrayStruct(nb_snapshots=sa1res.nb_snapshots, snapshot_array_size=sa1res.snapshot_array_size)
+    try:
+        fd = os.open(args.file, os.O_RDONLY)
+        sa2res = sa2req.ioctl(fd, CIFS_ENUMERATE_SNAPSHOTS)
+    except Exception as e:
+        print("syscall failed: %s"%e)
+        return False
+
+
+    print("Number of snapshots: %d Number of snapshots returned: %d"%(sa2res.nb_snapshots, sa2res.nb_snapshots_returned))
+    print("Snapshot list in GMT (Coordinated UTC Time) and SMB format (100 nanosecond units needed for snapshot mounts):")
+    for i, d in enumerate(sa2res.snapshot_array):
+        print("%d) GMT: %s\n   SMB3: %d"%(i, d, datetime_to_smb(d)))
+
+class SID:
+    def __init__(self, buf, off=0):
+        rev, sublen = struct.unpack_from('BB', buf, off+0)
+        off += 2
+        auth = 0
+        subauth = []
+        for i in range(6):
+            auth = (auth << 8)|buf[off]
+            off += 1
+        for i in range(sublen):
+            subauth.append(struct.unpack_from('<I', buf, off))
+            off += 4
+
+        self.rev = rev
+        self.auth = auth
+        self.subauth = subauth
+
+    def __str__(self):
+        auth = ("0x%x" if self.auth >= 2**32 else "%d")%self.auth
+        return  "S-%d-%s-%s"%(self.rev, auth, '-'.join(["%d"%x for x in self.subauth]))
+
+class ACE:
+    def __init__(self, buf, off=0, is_dir=False):
+        self.typ, self.flags, self.size = struct.unpack_from('<BBH', buf, off)
+        self.is_dir = is_dir
+        if self.typ not in [0,1,2]:
+            self.buf = buf[4:]
+        else:
+            self.mask = struct.unpack_from('<I', buf, off+4)[0]
+            self.sid = SID(buf, off+8)
+
+    def __str__(self):
+        s = []
+        s.append("Type: %s" % type_to_str(self.typ, ACE_TYPES))
+        s.append("Flags: %s" % flags_to_str(self.flags, ACE_FLAGS))
+        if self.typ not in [0,1,2]:
+            s.append("<%s>"%(" ".join(["%02x"%x for x in self.buf])))
+        else:
+            s.append("Mask: %s"%flags_to_str(self.mask, (DIR_ACCESS_FLAGS if self.is_dir else FILE_ACCESS_FLAGS)))
+            s.append("SID: %s"%self.sid)
+        return ", ".join(s)
+
+def cmd_quota(args):
+    qi = QueryInfoStruct(info_type=0x04, input_buffer_length=INPUT_BUFFER_LENGTH)
+    qi.pack_input('BBI', 0,
+                  0, # return single
+                  1, # restart scan
+                  0, # sid list length
+                  )
+    qi.output_buffer_length = 16
+    buf = None
+
+    try:
+        fd = os.open(args.file, os.O_RDONLY)
+        buf = qi.ioctl(fd)
+    except Exception as e:
+        print("syscall failed: %s"%e)
+        return False
+
+    off = 0
+    while off < len(buf):
+        next_off = struct.unpack_from('<I', buf, off+ 0)[0]
+        sid_len  = struct.unpack_from('<I', buf, off+ 4)[0]
+        atime    = struct.unpack_from('<Q', buf, off+ 8)[0]
+        qused    = struct.unpack_from('<Q', buf, off+16)[0]
+        qthresh  = struct.unpack_from('<Q', buf, off+24)[0]
+        qlimit   = struct.unpack_from('<Q', buf, off+32)[0]
+        sid = SID(buf, off+40)
+
+        print("SID Length: %d"%sid_len)
+        print("Change Time: %s"%win_to_datetime(atime))
+        print("Quota Used: %d"%qused)
+        print("Quota Threshold:", ("NO THRESHOLD" if qthresh == 0xffffffffffffffff else "%d"%qthresh))
+        print("Quota Limit:", ("NO LIMIT" if qlimit == 0xffffffffffffffff else "%d"%qlimit))
+        print("SID: %s"%sid)
+
+        if next_off == 0:
+            break
+        off += next_off
+
+def cmd_secdesc(args):
+    qi = QueryInfoStruct(info_type=0x03,
+                         additional_information=0x7, # owner, group, dacl
+                         input_buffer_length=INPUT_BUFFER_LENGTH)
+    buf = None
+    info = None
+
+    try:
+        fd = os.open(args.file, os.O_RDONLY)
+        info = os.fstat(fd)
+        buf = qi.ioctl(fd)
+    except Exception as e:
+        print("syscall failed: %s"%e)
+        return False
+
+    is_dir = stat.S_ISDIR(info.st_mode)
+    rev, ctrl, off_owner, off_group, off_dacl = struct.unpack_from('<BxHIIxxxxI', buf, 0)
+
+    print("Revision: %d"%rev)
+    print("Control: %s"%flags_to_str(ctrl, CONTROL_FLAGS))
+    if off_owner:
+        print("Owner: %s"%SID(buf, off_owner))
+    if off_group:
+        print("Group: %s"%SID(buf, off_group))
+    if off_dacl:
+        print("DACL:")
+        rev, count = struct.unpack_from('<BxxxH', buf, off_dacl)
+        off_dacl += 8
+        for i in range(count):
+              ace = ACE(buf, off_dacl, is_dir=is_dir)
+              print(ace)
+              off_dacl += ace.size
+
+
+class KeyDebugInfoStruct:
+    def __init__(self):
+        self.suid = bytearray()
+        self.cipher = 0
+        self.auth_key = bytearray()
+        self.enc_key = bytearray()
+        self.dec_key = bytearray()
+
+    def ioctl(self, fd):
+        buf = bytearray()
+        buf.extend(struct.pack("= 8s H 16s 16s 16s", self.suid, self.cipher,
+                               self.auth_key, self.enc_key, self.dec_key))
+        fcntl.ioctl(fd, CIFS_DUMP_KEY, buf, True)
+        (self.suid, self.cipher, self.auth_key,
+         self.enc_key, self.dec_key) = struct.unpack_from('= 8s H 16s 16s 16s', buf, 0)
+
+def bytes_to_hex(buf):
+    return " ".join(["%02x"%x for x in buf])
+
+def cmd_keys(args):
+    kd = KeyDebugInfoStruct()
+    try:
+        fd = os.open(args.file, os.O_RDONLY)
+        kd.ioctl(fd)
+    except Exception as e:
+        print("syscall failed: %s"%e)
+        return False
+
+    print("Session Id: %s"%bytes_to_hex(kd.suid))
+    print("Cipher: %s"%type_to_str(kd.cipher, CIPHER_TYPES, verbose=True))
+    print("Session Key: %s"%bytes_to_hex(kd.auth_key))
+    print("Encryption key: %s"%bytes_to_hex(kd.enc_key))
+    print("Decryption key: %s"%bytes_to_hex(kd.dec_key))
+
+if __name__ == '__main__':
+    main()
diff --git a/smbinfo.c b/smbinfo.c
deleted file mode 100644
index 636f1bd..0000000
--- a/smbinfo.c
+++ /dev/null
@@ -1,1296 +0,0 @@ 
-/*
- * smbinfo
- *
- * Copyright (C) Ronnie Sahlberg (lsahlberg@redhat.com) 2018
- * Copyright (C) Aurelien Aptel (aaptel@suse.com) 2018
- *
- * Display SMB-specific file information using cifs IOCTL
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include <endian.h>
-#include <errno.h>
-#include <getopt.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <string.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <unistd.h>
-#include <inttypes.h>
-
-#define CIFS_IOCTL_MAGIC 0xCF
-
-/* query_info flags */
-#define PASSTHRU_QUERY_INFO     0x00000000
-#define PASSTHRU_FSCTL          0x00000001
-
-struct smb_query_info {
-	uint32_t   info_type;
-	uint32_t   file_info_class;
-	uint32_t   additional_information;
-	uint32_t   flags;
-	uint32_t   input_buffer_length;
-	uint32_t   output_buffer_length;
-	/* char buffer[]; */
-} __packed;
-
-#define SMB3_SIGN_KEY_SIZE 16
-struct smb3_key_debug_info {
-	uint64_t Suid;
-	uint16_t cipher_type;
-	uint8_t auth_key[16]; /* SMB2_NTLMV2_SESSKEY_SIZE */
-	uint8_t	smb3encryptionkey[SMB3_SIGN_KEY_SIZE];
-	uint8_t	smb3decryptionkey[SMB3_SIGN_KEY_SIZE];
-} __attribute__((packed));
-
-#define CIFS_QUERY_INFO _IOWR(CIFS_IOCTL_MAGIC, 7, struct smb_query_info)
-#define CIFS_DUMP_KEY _IOWR(CIFS_IOCTL_MAGIC, 8, struct smb3_key_debug_info)
-#define INPUT_BUFFER_LENGTH 16384
-
-int verbose;
-
-static void
-usage(char *name)
-{
-	fprintf(stderr, "Usage: %s [-V] <command> <file>\n"
-		"-V for verbose output\n"
-		"-h display this help text\n"
-		"-v print smbinfo version\n"
-		"Commands are\n"
-		"  fileaccessinfo:\n"
-		"      Prints FileAccessInfo for a cifs file.\n"
-		"  filealigninfo:\n"
-		"      Prints FileAlignInfo for a cifs file.\n"
-		"  fileallinfo:\n"
-		"      Prints FileAllInfo for a cifs file.\n"
-		"  filebasicinfo:\n"
-		"      Prints FileBasicInfo for a cifs file.\n"
-		"  fileeainfo:\n"
-		"      Prints FileEAInfo for a cifs file.\n"
-		"  filefsfullsizeinfo:\n"
-		"      Prints FileFsFullSizeInfo for a cifs share.\n"
-		"  fileinternalinfo:\n"
-		"      Prints FileInternalInfo for a cifs file.\n"
-		"  filemodeinfo:\n"
-		"      Prints FileModeInfo for a cifs file.\n"
-		"  filepositioninfo:\n"
-		"      Prints FilePositionInfo for a cifs file.\n"
-		"  filestandardinfo:\n"
-		"      Prints FileStandardInfo for a cifs file.\n"
-		"  fsctl-getobjid:\n"
-		"      Prints the objectid of the file and GUID of the underlying volume.\n"
-		"  getcompression:\n"
-		"      Prints the compression setting for the file.\n"
-		"  setcompression <no|default|lznt1>:\n"
-		"      Sets the compression level for the file.\n"
-		"  list-snapshots:\n"
-		"      List the previous versions of the volume that backs this file.\n"
-		"  quota:\n"
-		"      Prints the quota for a cifs file.\n"
-		"  secdesc:\n"
-		"      Prints the security descriptor for a cifs file.\n"
-		"  keys:\n"
-		"      Prints the decryption information needed to view encrypted network traces.\n",
-		name);
-	exit(1);
-}
-
-static void
-short_usage(char *name)
-{
-	fprintf(stderr, "Usage: %s [-v] [-V] <command> <file>\n"
-		"Try 'smbinfo -h' for more information.\n", name);
-	exit(1);
-}
-
-static void
-win_to_timeval(uint64_t smb2_time, struct timeval *tv)
-{
-	tv->tv_usec = (smb2_time / 10) % 1000000;
-	tv->tv_sec  = (smb2_time - 116444736000000000) / 10000000;
-}
-
-struct bit_string {
-	unsigned int bit;
-	char *string;
-};
-
-struct bit_string directory_access_mask[] = {
-	{ 0x00000001, "LIST_DIRECTORY" },
-	{ 0x00000002, "ADD_FILE" },
-	{ 0x00000004, "ADD_SUBDIRECTORY" },
-	{ 0x00000008, "READ_EA" },
-	{ 0x00000010, "WRITE_EA" },
-	{ 0x00000020, "TRAVERSE" },
-	{ 0x00000040, "DELETE_CHILD" },
-	{ 0x00000080, "READ_ATTRIBUTES" },
-	{ 0x00000100, "WRITE_ATTRIBUTES" },
-	{ 0x00010000, "DELETE" },
-	{ 0x00020000, "READ_CONTROL" },
-	{ 0x00040000, "WRITE_DAC" },
-	{ 0x00080000, "WRITE_OWNER" },
-	{ 0x00100000, "SYNCHRONIZER" },
-	{ 0x01000000, "ACCESS_SYSTEM_SECURITY" },
-	{ 0x02000000, "MAXIMUM_ALLOWED" },
-	{ 0x10000000, "GENERIC_ALL" },
-	{ 0x20000000, "GENERIC_EXECUTE" },
-	{ 0x40000000, "GENERIC_WRITE" },
-	{ 0x80000000, "GENERIC_READ" },
-	{ 0, NULL }
-};
-
-struct bit_string file_access_mask[] = {
-	{ 0x00000001, "READ_DATA" },
-	{ 0x00000002, "WRITE_DATA" },
-	{ 0x00000004, "APPEND_DATA" },
-	{ 0x00000008, "READ_EA" },
-	{ 0x00000010, "WRITE_EA" },
-	{ 0x00000020, "EXECUTE" },
-	{ 0x00000040, "DELETE_CHILD" },
-	{ 0x00000080, "READ_ATTRIBUTES" },
-	{ 0x00000100, "WRITE_ATTRIBUTES" },
-	{ 0x00010000, "DELETE" },
-	{ 0x00020000, "READ_CONTROL" },
-	{ 0x00040000, "WRITE_DAC" },
-	{ 0x00080000, "WRITE_OWNER" },
-	{ 0x00100000, "SYNCHRONIZER" },
-	{ 0x01000000, "ACCESS_SYSTEM_SECURITY" },
-	{ 0x02000000, "MAXIMUM_ALLOWED" },
-	{ 0x10000000, "GENERIC_ALL" },
-	{ 0x20000000, "GENERIC_EXECUTE" },
-	{ 0x40000000, "GENERIC_WRITE" },
-	{ 0x80000000, "GENERIC_READ" },
-	{ 0, NULL }
-};
-
-static void
-print_bits(uint32_t mask, struct bit_string *bs)
-{
-	int first = 1;
-
-	if (!verbose)
-		return;
-
-	while (bs->string) {
-		if (mask & bs->bit) {
-			printf("%s%s", first?"":",", bs->string);
-			first = 0;
-		}
-		bs++;
-	}
-	if (!first)
-		printf(" ");
-}
-
-static void
-print_guid(uint8_t *sd)
-{
-	uint32_t u32;
-	uint16_t u16;
-	int i;
-
-	memcpy(&u32, &sd[0], 4);
-	printf("%08x-", le32toh(u32));
-
-	memcpy(&u16, &sd[4], 2);
-	printf("%04x-", le16toh(u16));
-
-	memcpy(&u16, &sd[6], 2);
-	printf("%04x-", le16toh(u16));
-
-	printf("%02x%02x-", sd[8], sd[9]);
-	for (i = 0; i < 6; i++)
-		printf("%02x", sd[10 + i]);
-}
-
-static void
-print_objidbuf(uint8_t *sd)
-{
-	printf("Object-ID: ");
-	print_guid(&sd[0]);
-	printf("\n");
-
-	printf("Birth-Volume-ID: ");
-	print_guid(&sd[16]);
-	printf("\n");
-
-	printf("Birth-Object-ID: ");
-	print_guid(&sd[32]);
-	printf("\n");
-
-	printf("Domain-ID: ");
-	print_guid(&sd[48]);
-	printf("\n");
-}
-
-static void
-fsctlgetobjid(int f)
-{
-	struct smb_query_info *qi;
-	struct stat st;
-
-	fstat(f, &st);
-
-	qi = malloc(sizeof(struct smb_query_info) + 64);
-	memset(qi, 0, sizeof(qi) + 64);
-	qi->info_type = 0x9009c;
-	qi->file_info_class = 0;
-	qi->additional_information = 0;
-	qi->input_buffer_length = 64;
-	qi->flags = PASSTHRU_FSCTL;
-
-	if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
-		fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
-		exit(1);
-	}
-	print_objidbuf((uint8_t *)(&qi[1]));
-
-	free(qi);
-}
-
-static void
-print_getcompression(uint8_t *sd)
-{
-	uint16_t u16;
-
-	memcpy(&u16, &sd[0], 2);
-	u16 = le16toh(u16);
-
-	printf("Compression: ");
-	switch (u16) {
-	case 0:
-		printf("(0) NONE\n");
-		break;
-	case 2:
-		printf("(2) LZNT1\n");
-		break;
-	default:
-		printf("(%d) UNKNOWN\n", u16);
-		break;
-	}
-}
-
-static void
-getcompression(int f)
-{
-	struct smb_query_info *qi;
-
-	qi = malloc(sizeof(struct smb_query_info) + 2);
-	memset(qi, 0, sizeof(qi) + 2);
-	qi->info_type = 0x9003c;
-	qi->file_info_class = 0;
-	qi->additional_information = 0;
-	qi->input_buffer_length = 2;
-	qi->flags = PASSTHRU_FSCTL;
-
-	if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
-		fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
-		exit(1);
-	}
-	print_getcompression((uint8_t *)(&qi[1]));
-
-	free(qi);
-}
-
-static void
-setcompression(int f, uint16_t level)
-{
-	struct smb_query_info *qi;
-
-	qi = malloc(sizeof(struct smb_query_info) + 2);
-	memset(qi, 0, sizeof(qi) + 2);
-	qi->info_type = 0x9c040;
-	qi->file_info_class = 0;
-	qi->additional_information = 0;
-	qi->output_buffer_length = 2;
-	qi->flags = PASSTHRU_FSCTL;
-
-	level = htole16(level);
-	memcpy(&qi[1], &level, 2);
-
-	if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
-		fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
-		exit(1);
-	}
-
-	free(qi);
-}
-
-static void
-print_fileaccessinfo(uint8_t *sd, int type)
-{
-	uint32_t access_flags;
-
-	memcpy(&access_flags, &sd[0], 4);
-	access_flags = le32toh(access_flags);
-
-	if (type == S_IFDIR) {
-		printf("Directory access flags 0x%08x: ", access_flags);
-		print_bits(access_flags, directory_access_mask);
-	} else {
-		printf("File/Printer access flags 0x%08x: ", access_flags);
-		print_bits(access_flags, file_access_mask);
-	}
-	printf("\n");
-}
-
-static void
-fileaccessinfo(int f)
-{
-	struct smb_query_info *qi;
-	struct stat st;
-
-	fstat(f, &st);
-
-	qi = malloc(sizeof(struct smb_query_info) + 4);
-	memset(qi, 0, sizeof(qi) + 4);
-	qi->info_type = 0x01;
-	qi->file_info_class = 8;
-	qi->additional_information = 0;
-	qi->input_buffer_length = 4;
-
-	if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
-		fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
-		exit(1);
-	}
-
-	print_fileaccessinfo((uint8_t *)(&qi[1]), st.st_mode & S_IFMT);
-	free(qi);
-}
-
-static void
-print_filealigninfo(uint8_t *sd)
-{
-	uint32_t mask;
-
-	memcpy(&mask, &sd[0], 4);
-	mask = le32toh(mask);
-
-	printf("File alignment: ");
-	if (mask == 0)
-		printf("BYTE_ALIGNMENT");
-	else if (mask == 1)
-		printf("WORD_ALIGNMENT");
-	else if (mask == 3)
-		printf("LONG_ALIGNMENT");
-	else if (mask == 7)
-		printf("QUAD_ALIGNMENT");
-	else if (mask == 15)
-		printf("OCTA_ALIGNMENT");
-	else if (mask == 31)
-		printf("32_bit_ALIGNMENT");
-	else if (mask == 63)
-		printf("64_bit_ALIGNMENT");
-	else if (mask == 127)
-		printf("128_bit_ALIGNMENT");
-	else if (mask == 255)
-		printf("254_bit_ALIGNMENT");
-	else if (mask == 511)
-		printf("512_bit_ALIGNMENT");
-
-	printf("\n");
-}
-
-static void
-filealigninfo(int f)
-{
-	struct smb_query_info *qi;
-
-	qi = malloc(sizeof(struct smb_query_info) + 4);
-	memset(qi, 0, sizeof(qi) + 4);
-	qi->info_type = 0x01;
-	qi->file_info_class = 17;
-	qi->additional_information = 0;
-	qi->input_buffer_length = 4;
-
-	if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
-		fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
-		exit(1);
-	}
-
-	print_filealigninfo((uint8_t *)(&qi[1]));
-	free(qi);
-}
-
-struct bit_string file_attributes_mask[] = {
-	{ 0x00000001, "READ_ONLY" },
-	{ 0x00000002, "HIDDEN" },
-	{ 0x00000004, "SYSTEM" },
-	{ 0x00000010, "DIRECTORY" },
-	{ 0x00000020, "ARCHIVE" },
-	{ 0x00000080, "NORMAL" },
-	{ 0x00000100, "TEMPORARY" },
-	{ 0x00000200, "SPARSE_FILE" },
-	{ 0x00000400, "REPARSE_POINT" },
-	{ 0x00000800, "COMPRESSED" },
-	{ 0x00001000, "OFFLINE" },
-	{ 0x00002000, "NOT_CONTENT_INDEXED" },
-	{ 0x00004000, "ENCRYPTED" },
-	{ 0x00008000, "INTEGRITY_STREAM" },
-	{ 0x00020000, "NO_SCRUB_DATA" },
-	{ 0, NULL }
-};
-
-static void
-print_filebasicinfo(uint8_t *sd)
-{
-	struct timeval tv;
-	uint64_t u64;
-	uint32_t u32;
-
-	memcpy(&u64, &sd[0], 8);
-	win_to_timeval(le64toh(u64), &tv);
-	printf("Creation Time %s", ctime(&tv.tv_sec));
-
-	memcpy(&u64, &sd[8], 8);
-	win_to_timeval(le64toh(u64), &tv);
-	printf("Last Access Time %s", ctime(&tv.tv_sec));
-
-	memcpy(&u64, &sd[16], 8);
-	win_to_timeval(le64toh(u64), &tv);
-	printf("Last Write Time %s", ctime(&tv.tv_sec));
-
-	memcpy(&u64, &sd[24], 8);
-	win_to_timeval(le64toh(u64), &tv);
-	printf("Last Change Time %s", ctime(&tv.tv_sec));
-
-	memcpy(&u32, &sd[32], 4);
-	u32 = le32toh(u32);
-	printf("File Attributes 0x%08x: ", u32);
-	print_bits(u32, file_attributes_mask);
-	printf("\n");
-}
-
-static void
-filebasicinfo(int f)
-{
-	struct smb_query_info *qi;
-
-	qi = malloc(sizeof(struct smb_query_info) + 40);
-	memset(qi, 0, sizeof(qi) + 40);
-	qi->info_type = 0x01;
-	qi->file_info_class = 4;
-	qi->additional_information = 0;
-	qi->input_buffer_length = 40;
-
-	if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
-		fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
-		exit(1);
-	}
-
-	print_filebasicinfo((uint8_t *)(&qi[1]));
-	free(qi);
-}
-
-static void
-print_filestandardinfo(uint8_t *sd)
-{
-	uint64_t u64;
-	uint32_t u32;
-
-	memcpy(&u64, &sd[0], 8);
-	printf("Allocation Size %" PRIu64 "\n", le64toh(u64));
-
-	memcpy(&u64, &sd[8], 8);
-	printf("End Of File %" PRIu64 "\n", le64toh(u64));
-
-	memcpy(&u32, &sd[16], 4);
-	printf("Number Of Links %" PRIu32 "\n", le32toh(u32));
-
-	printf("Delete Pending %d\n", sd[20]);
-	printf("Delete Directory %d\n", sd[21]);
-}
-
-static void
-filestandardinfo(int f)
-{
-	struct smb_query_info *qi;
-
-	qi = malloc(sizeof(struct smb_query_info) + 24);
-	memset(qi, 0, sizeof(qi) + 24);
-	qi->info_type = 0x01;
-	qi->file_info_class = 5;
-	qi->additional_information = 0;
-	qi->input_buffer_length = 24;
-
-	if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
-		fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
-		exit(1);
-	}
-
-	print_filestandardinfo((uint8_t *)(&qi[1]));
-	free(qi);
-}
-
-static void
-print_fileinternalinfo(uint8_t *sd)
-{
-	uint64_t u64;
-
-	memcpy(&u64, &sd[0], 8);
-	printf("Index Number %" PRIu64 "\n", le64toh(u64));
-}
-
-static void
-fileinternalinfo(int f)
-{
-	struct smb_query_info *qi;
-
-	qi = malloc(sizeof(struct smb_query_info) + 8);
-	memset(qi, 0, sizeof(qi) + 8);
-	qi->info_type = 0x01;
-	qi->file_info_class = 6;
-	qi->additional_information = 0;
-	qi->input_buffer_length = 8;
-
-	if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
-		fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
-		exit(1);
-	}
-
-	print_fileinternalinfo((uint8_t *)(&qi[1]));
-	free(qi);
-}
-
-struct bit_string file_mode_mask[] = {
-	{ 0x00000002, "WRITE_THROUGH" },
-	{ 0x00000004, "SEQUENTIAL_ONLY" },
-	{ 0x00000008, "NO_INTERMEDIATE_BUFFERING" },
-	{ 0x00000010, "SYNCHRONOUS_IO_ALERT" },
-	{ 0x00000020, "SYNCHRONOUS_IO_NONALERT" },
-	{ 0x00001000, "DELETE_ON_CLOSE" },
-	{ 0, NULL }
-};
-
-static void
-print_filemodeinfo(uint8_t *sd)
-{
-	uint32_t u32;
-
-	memcpy(&u32, &sd[32], 4);
-	u32 = le32toh(u32);
-	printf("Mode 0x%08x: ", u32);
-	print_bits(u32, file_mode_mask);
-	printf("\n");
-}
-
-static void
-filemodeinfo(int f)
-{
-	struct smb_query_info *qi;
-
-	qi = malloc(sizeof(struct smb_query_info) + 4);
-	memset(qi, 0, sizeof(qi) + 4);
-	qi->info_type = 0x01;
-	qi->file_info_class = 16;
-	qi->additional_information = 0;
-	qi->input_buffer_length = 4;
-
-	if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
-		fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
-		exit(1);
-	}
-
-	print_filemodeinfo((uint8_t *)(&qi[1]));
-	free(qi);
-}
-
-static void
-print_filepositioninfo(uint8_t *sd)
-{
-	uint64_t u64;
-
-	memcpy(&u64, &sd[0], 8);
-	printf("Current Byte Offset %" PRIu64 "\n", le64toh(u64));
-}
-
-static void
-filepositioninfo(int f)
-{
-	struct smb_query_info *qi;
-
-	qi = malloc(sizeof(struct smb_query_info) + 8);
-	memset(qi, 0, sizeof(qi) + 8);
-	qi->info_type = 0x01;
-	qi->file_info_class = 14;
-	qi->additional_information = 0;
-	qi->input_buffer_length = 8;
-
-	if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
-		fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
-		exit(1);
-	}
-
-	print_filepositioninfo((uint8_t *)(&qi[1]));
-	free(qi);
-}
-
-static void
-print_fileeainfo(uint8_t *sd)
-{
-	uint32_t u32;
-
-	memcpy(&u32, &sd[0], 4);
-	printf("Ea Size %" PRIu32 "\n", le32toh(u32));
-}
-
-static void
-fileeainfo(int f)
-{
-	struct smb_query_info *qi;
-
-	qi = malloc(sizeof(struct smb_query_info) + 4);
-	memset(qi, 0, sizeof(qi) + 4);
-	qi->info_type = 0x01;
-	qi->file_info_class = 7;
-	qi->additional_information = 0;
-	qi->input_buffer_length = 4;
-
-	if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
-		fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
-		exit(1);
-	}
-
-	print_fileeainfo((uint8_t *)(&qi[1]));
-	free(qi);
-}
-
-static void
-print_filefullsizeinfo(uint8_t *sd)
-{
-	uint32_t u32;
-	uint64_t u64;
-
-	memcpy(&u64, &sd[0], 8);
-	printf("Total Allocation Units: %" PRIu64 "\n", le64toh(u64));
-
-	memcpy(&u64, &sd[8], 8);
-	printf("Caller Available Allocation Units: %" PRIu64 "\n",
-	       le64toh(u64));
-
-	memcpy(&u64, &sd[16], 8);
-	printf("Actual Available Allocation Units: %" PRIu64 "\n",
-	       le64toh(u64));
-
-	memcpy(&u32, &sd[24], 4);
-	printf("Sectors Per Allocation Unit: %" PRIu32 "\n", le32toh(u32));
-
-	memcpy(&u32, &sd[28], 4);
-	printf("Bytes Per Sector: %" PRIu32 "\n", le32toh(u32));
-}
-
-static void
-filefsfullsizeinfo(int f)
-{
-	struct smb_query_info *qi;
-
-	qi = malloc(sizeof(struct smb_query_info) + 32);
-	memset(qi, 0, sizeof(qi) + 32);
-	qi->info_type = 0x02;
-	qi->file_info_class = 7;
-	qi->additional_information = 0;
-	qi->input_buffer_length = 32;
-
-	if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
-		fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
-		exit(1);
-	}
-
-	print_filefullsizeinfo((uint8_t *)(&qi[1]));
-	free(qi);
-}
-
-static void
-fileallinfo(int f)
-{
-	struct smb_query_info *qi;
-	struct stat st;
-
-	fstat(f, &st);
-
-	qi = malloc(sizeof(struct smb_query_info) + INPUT_BUFFER_LENGTH);
-	memset(qi, 0, sizeof(qi) + INPUT_BUFFER_LENGTH);
-	qi->info_type = 0x01;
-	qi->file_info_class = 18;
-	qi->additional_information = 0;
-	qi->input_buffer_length = INPUT_BUFFER_LENGTH;
-
-	if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
-		fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
-		exit(1);
-	}
-
-	print_filebasicinfo((uint8_t *)(&qi[1]));
-	print_filestandardinfo((uint8_t *)(&qi[1]) + 40);
-	print_fileinternalinfo((uint8_t *)(&qi[1]) + 64);
-	print_fileeainfo((uint8_t *)(&qi[1]) + 72);
-	print_fileaccessinfo((uint8_t *)(&qi[1]) + 76, st.st_mode & S_IFMT);
-	print_filepositioninfo((uint8_t *)(&qi[1]) + 80);
-	print_filemodeinfo((uint8_t *)(&qi[1]) + 88);
-	print_filealigninfo((uint8_t *)(&qi[1]) + 92);
-	// SMB2 servers like Win16 does not seem to return name info
-	free(qi);
-}
-
-static void
-print_sid(unsigned char *sd)
-{
-	int i;
-	uint32_t subauth;
-	uint64_t idauth;
-
-	if (sd[0] != 1) {
-		fprintf(stderr, "Unknown SID revision\n");
-		return;
-	}
-
-	idauth = 0;
-	for (i = 0; i < 6; i++)
-		idauth = (idauth << 8) | sd[2 + i];
-
-	printf("S-1-%" PRIu64, idauth);
-	for (i = 0; i < sd[1]; i++) {
-		memcpy(&subauth, &sd[8 + 4 * i], 4);
-		subauth = le32toh(subauth);
-		printf("-%u", subauth);
-	}
-}
-
-static void
-print_ace_type(uint8_t t)
-{
-	switch(t) {
-	case 0x00: printf("ALLOWED"); break;
-	case 0x01: printf("DENIED"); break;
-	case 0x02: printf("AUDIT"); break;
-	case 0x03: printf("ALARM"); break;
-	case 0x04: printf("ALLOWED_COMPOUND"); break;
-	case 0x05: printf("ALLOWED_OBJECT"); break;
-	case 0x06: printf("DENIED_OBJECT"); break;
-	case 0x07: printf("AUDIT_OBJECT"); break;
-	case 0x08: printf("ALARM_OBJECT"); break;
-	case 0x09: printf("ALLOWED_CALLBACK"); break;
-	case 0x0a: printf("DENIED_CALLBACK"); break;
-	case 0x0b: printf("ALLOWED_CALLBACK_OBJECT"); break;
-	case 0x0c: printf("DENIED_CALLBACK_OBJECT"); break;
-	case 0x0d: printf("AUDIT_CALLBACK"); break;
-	case 0x0e: printf("ALARM_CALLBACK"); break;
-	case 0x0f: printf("AUDIT_CALLBACK_OBJECT"); break;
-	case 0x10: printf("ALARM_CALLBACK_OBJECT"); break;
-	case 0x11: printf("MANDATORY_LABEL"); break;
-	case 0x12: printf("RESOURCE_ATTRIBUTE"); break;
-	case 0x13: printf("SCOPED_POLICY_ID"); break;
-	default: printf("<UNKNOWN>");
-	}
-	printf(" ");
-}
-
-struct bit_string ace_flags_mask[] = {
-	{ 0x80, "FAILED_ACCESS" },
-	{ 0x40, "SUCCESSFUL_ACCESS" },
-	{ 0x10, "INHERITED" },
-	{ 0x08, "INHERIT_ONLY" },
-	{ 0x04, "NO_PROPAGATE_INHERIT" },
-	{ 0x02, "CONTAINER_INHERIT" },
-	{ 0x01, "OBJECT_INHERIT" },
-	{ 0, NULL }
-};
-
-static void
-print_mask_sid_ace(unsigned char *sd, int type)
-{
-	uint32_t u32;
-
-	memcpy(&u32, &sd[0], 4);
-	printf("Mask:0x%08x ", le32toh(u32));
-	if (type == S_IFDIR)
-		print_bits(le32toh(u32), directory_access_mask);
-	else
-		print_bits(le32toh(u32), file_access_mask);
-	printf("SID:");
-	print_sid(&sd[4]);
-	printf("\n");
-}
-
-static int
-print_ace(unsigned char *sd, int type)
-{
-	uint16_t size;
-	int i;
-
-	printf("Type:0x%02x ", sd[0]);
-	if (verbose) {
-		print_ace_type(sd[0]);
-	}
-
-	printf("Flags:0x%02x ", sd[1]);
-	print_bits(sd[1], ace_flags_mask);
-
-	memcpy(&size, &sd[2], 2);
-	size = le16toh(size);
-
-	switch (sd[0]) {
-	case 0x00:
-	case 0x01:
-	case 0x02:
-		print_mask_sid_ace(&sd[4], type);
-		break;
-	default:
-		for (i = 0; i < size; i++)
-			printf("%02x", sd[4 + i]);
-	}
-
-	printf("\n");
-	return size;
-}
-
-static void
-print_acl(unsigned char *sd, int type)
-{
-	int i, off;
-	uint16_t count;
-
-	if ((sd[0] != 2) && (sd[0] != 4)) {
-		fprintf(stderr, "Unknown ACL revision\n");
-		return;
-	}
-
-	memcpy(&count, &sd[4], 2);
-	count = le16toh(count);
-	off = 8;
-	for (i = 0; i < count; i++)
-		off += print_ace(&sd[off], type);
-}
-
-struct bit_string control_bits_mask[] = {
-	{ 0x8000, "SR" },
-	{ 0x4000, "RM" },
-	{ 0x2000, "PS" },
-	{ 0x1000, "PD" },
-	{ 0x0800, "SI" },
-	{ 0x0400, "DI" },
-	{ 0x0200, "SC" },
-	{ 0x0100, "DC" },
-	{ 0x0080, "DT" },
-	{ 0x0040, "SS" },
-	{ 0x0020, "SD" },
-	{ 0x0010, "SP" },
-	{ 0x0008, "DD" },
-	{ 0x0004, "DP" },
-	{ 0x0002, "GD" },
-	{ 0x0001, "OD" },
-	{ 0, NULL }
-};
-
-static void
-print_control(uint16_t c)
-{
-	printf("Control: 0x%04x ", c);
-	print_bits(c, control_bits_mask);
-	printf("\n");
-}
-
-static void
-print_sd(uint8_t *sd, int type)
-{
-	int offset_owner, offset_group, offset_dacl;
-	uint16_t u16;
-
-	printf("Revision:%d\n", sd[0]);
-	if (sd[0] != 1) {
-		fprintf(stderr, "Unknown SD revision\n");
-		exit(1);
-	}
-
-	memcpy(&u16, &sd[2], 2);
-	print_control(le16toh(u16));
-
-	memcpy(&offset_owner, &sd[4], 4);
-	offset_owner = le32toh(offset_owner);
-	memcpy(&offset_group, &sd[8], 4);
-	offset_group = le32toh(offset_group);
-	memcpy(&offset_dacl, &sd[16], 4);
-	offset_dacl = le32toh(offset_dacl);
-
-	if (offset_owner) {
-		printf("Owner: ");
-		print_sid(&sd[offset_owner]);
-		printf("\n");
-	}
-	if (offset_group) {
-		printf("Group: ");
-		print_sid(&sd[offset_group]);
-		printf("\n");
-	}
-	if (offset_dacl) {
-		printf("DACL:\n");
-		print_acl(&sd[offset_dacl], type);
-	}
-}
-
-static void
-secdesc(int f)
-{
-	struct smb_query_info *qi;
-	struct stat st;
-
-	fstat(f, &st);
-
-	qi = malloc(sizeof(struct smb_query_info) + INPUT_BUFFER_LENGTH);
-	memset(qi, 0, sizeof(qi) + INPUT_BUFFER_LENGTH);
-	qi->info_type = 0x03;
-	qi->file_info_class = 0;
-	qi->additional_information = 0x00000007; /* Owner, Group, Dacl */
-	qi->input_buffer_length = INPUT_BUFFER_LENGTH;
-
-	if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
-		fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
-		exit(1);
-	}
-
-	print_sd((uint8_t *)(&qi[1]), st.st_mode & S_IFMT);
-	free(qi);
-}
-
-static void
-print_quota(unsigned char *sd)
-{
-	uint32_t u32, neo;
-	uint64_t u64;
-	struct timeval tv;
-	int i, off = 0;
-
-one_more:
-	memcpy(&u32, &sd[off], 4);
-	neo = le32toh(u32);
-
-	memcpy(&u32, &sd[off + 4], 4);
-	u32 = le32toh(u32);
-	printf("SID Length %d\n", u32);
-
-	memcpy(&u64, &sd[off + 8], 8);
-	win_to_timeval(le64toh(u64), &tv);
-	printf("Change Time %s", ctime(&tv.tv_sec));
-
-	memcpy(&u64, &sd[off + 16], 8);
-	u64 = le32toh(u64);
-	printf("Quota Used %" PRIu64 "\n", u64);
-
-	memcpy(&u64, &sd[off + 24], 8);
-	u64 = le64toh(u64);
-	if (u64 == 0xffffffffffffffff)
-		printf("Quota Threshold NO THRESHOLD\n");
-	else
-		printf("Quota Threshold %" PRIu64 "\n", u64);
-
-	memcpy(&u64, &sd[off + 32], 8);
-	u64 = le64toh(u64);
-	if (u64 == 0xffffffffffffffff)
-		printf("Quota Limit NO LIMIT\n");
-	else
-		printf("Quota Limit %" PRIu64 "\n", u64);
-
-	printf("SID: S-1");
-	u64 = 0;
-	for (i = 0; i < 6; i++)
-		u64 = (u64 << 8) | sd[off + 42 + i];
-	printf("-%" PRIu64, u64);
-
-	for (i = 0; i < sd[off + 41]; i++) {
-		memcpy(&u32, &sd[off + 48 + 4 * i], 4);
-		u32 = le32toh(u32);
-		printf("-%u", u32);
-	}
-	printf("\n\n");
-	off += neo;
-
-	if (neo != 0)
-		goto one_more;
-}
-
-static void
-quota(int f)
-{
-	struct smb_query_info *qi;
-	char *buf;
-	int i;
-
-	qi = malloc(sizeof(struct smb_query_info) + INPUT_BUFFER_LENGTH);
-	memset(qi, 0, sizeof(struct smb_query_info) + INPUT_BUFFER_LENGTH);
-	qi->info_type = 0x04;
-	qi->file_info_class = 0;
-	qi->additional_information = 0; /* Owner, Group, Dacl */
-	qi->input_buffer_length = INPUT_BUFFER_LENGTH;
-
-	buf = (char *)&qi[1];
-	buf[0] = 0; /* return single */
-	buf[1] = 1; /* restart scan */
-
-	/* sid list length */
-	i = 0;
-	memcpy(&buf[4], &i, 4);
-
-	qi->output_buffer_length = 16;
-
-	if (ioctl(f, CIFS_QUERY_INFO, qi) < 0) {
-		fprintf(stderr, "ioctl failed with %s\n", strerror(errno));
-		exit(1);
-	}
-
-	print_quota((unsigned char *)(&qi[1]));
-	free(qi);
-}
-
-
-struct smb_snapshot_array {
-	int32_t	number_of_snapshots;
-	int32_t	number_of_snapshots_returned;
-	int32_t	snapshot_array_size;
-	char snapshot_data[0];
-};
-
-
-#define GMT_NAME_LEN 24 /* length of a @GMT- name */
-#define GMT_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
-
-#define NTFS_TIME_OFFSET ((unsigned long long)(369*365 + 89) * 24 * 3600 * 10000000)
-
-static void print_snapshots(struct smb_snapshot_array *psnap)
-{
-	int current_snapshot_entry = 0;
-	char gmt_token[GMT_NAME_LEN + 1] = {0};
-	int i;
-	int j = 0;
-	struct tm tm;
-	unsigned long long dce_time;
-
-	printf("Number of snapshots: %d Number of snapshots returned: %d\n",
-		psnap->number_of_snapshots,
-		psnap->number_of_snapshots_returned);
-	printf("Snapshot list in GMT (Coordinated UTC Time) and SMB format (100 nanosecond units needed for snapshot mounts):");
-	for (i = 0; i < psnap->snapshot_array_size; i++) {
-		if (psnap->snapshot_data[i] == '@') {
-			j = 0;
-			current_snapshot_entry++;
-			printf("\n%d) GMT:", current_snapshot_entry);
-		}
-		if (psnap->snapshot_data[i] != 0) {
-			gmt_token[j] = psnap->snapshot_data[i];
-			j++;
-		}
-		if (j == GMT_NAME_LEN) {
-			printf("%s", gmt_token);
-			j = 0;
-			strptime(gmt_token, GMT_FORMAT, &tm);
-			dce_time = timegm(&tm) * 10000000 + NTFS_TIME_OFFSET;
-			printf("\n   SMB3:%llu", dce_time);
-		}
-	}
-	printf("\n");
-}
-
-static void
-dump_keys(int f)
-{
-	struct smb3_key_debug_info keys_info;
-	uint8_t *psess_id;
-
-	if (ioctl(f, CIFS_DUMP_KEY, &keys_info) < 0) {
-		fprintf(stderr, "Querying keys information failed with %s\n", strerror(errno));
-		exit(1);
-	}
-
-	if (keys_info.cipher_type == 1)
-		printf("CCM encryption");
-	else if (keys_info.cipher_type == 2)
-		printf("GCM encryption");
-	else if (keys_info.cipher_type == 0)
-		printf("SMB3.0 CCM encryption");
-	else
-		printf("unknown encryption type");
-
-	printf("\nSession Id:  ");
-	psess_id = (uint8_t *)&keys_info.Suid;
-	for (int i = 0; i < 8; i++)
-		printf(" %02x", psess_id[i]);
-
-	printf("\nSession Key: ");
-	for (int i = 0; i < 16; i++)
-		printf(" %02x", keys_info.auth_key[i]);
-	printf("\nServer Encryption Key: ");
-	for (int i = 0; i < SMB3_SIGN_KEY_SIZE; i++)
-		printf(" %02x", keys_info.smb3encryptionkey[i]);
-	printf("\nServer Decryption Key: ");
-	for (int i = 0; i < SMB3_SIGN_KEY_SIZE; i++)
-		printf(" %02x", keys_info.smb3decryptionkey[i]);
-	printf("\n");
-}
-
-#define CIFS_ENUMERATE_SNAPSHOTS _IOR(CIFS_IOCTL_MAGIC, 6, struct smb_snapshot_array)
-
-#define MIN_SNAPSHOT_ARRAY_SIZE 16 /* See MS-SMB2 section 3.3.5.15.1 */
-
-static void
-list_snapshots(int f)
-{
-
-	struct smb_snapshot_array snap_inf;
-	struct smb_snapshot_array *buf;
-
-	/*
-	 * When first field in structure we pass in here is zero, cifs.ko can
-	 * recognize that this is the first query and that it must set the SMB3
-	 * FSCTL response buffer size (in the request) to exactly 16 bytes
-	 * (which is required by some servers to process the initial query)
-	 */
-	snap_inf.number_of_snapshots = 0;
-	snap_inf.number_of_snapshots_returned = 0;
-	snap_inf.snapshot_array_size = sizeof(struct smb_snapshot_array);
-
-	/* Query the number of snapshots so we know how much to allocate */
-	if (ioctl(f, CIFS_ENUMERATE_SNAPSHOTS, &snap_inf) < 0) {
-		fprintf(stderr, "Querying snapshots failed with %s\n", strerror(errno));
-		exit(1);
-	}
-
-	if (snap_inf.number_of_snapshots == 0)
-		return;
-
-	/* Now that we know the size, query the list from the server */
-
-	buf = malloc(snap_inf.snapshot_array_size + MIN_SNAPSHOT_ARRAY_SIZE);
-
-	if (buf == NULL) {
-		printf("Failed, out of memory.\n");
-		exit(1);
-	}
-	/*
-	 * first parm is non-zero which allows cifs.ko to recognize that this is
-	 * the second query (it has to set response buf size larger)
-	 */
-	buf->number_of_snapshots = snap_inf.number_of_snapshots;
-
-	buf->snapshot_array_size = snap_inf.snapshot_array_size;
-
-	if (ioctl(f, CIFS_ENUMERATE_SNAPSHOTS, buf) < 0) {
-		fprintf(stderr, "Querying snapshots failed with %s\n", strerror(errno));
-		exit(1);
-	}
-
-	print_snapshots(buf);
-	free(buf);
-}
-
-static int
-parse_compression(const char *arg)
-{
-	if (!strcmp(arg, "no"))
-		return 0;
-	else if (!strcmp(arg, "default"))
-		return 1;
-	else if (!strcmp(arg, "lznt1"))
-		return 2;
-
-	fprintf(stderr, "compression must be no|default|lznt1\n");
-	exit(10);
-}
-
-int main(int argc, char *argv[])
-{
-	int c;
-	int f;
-	int compression = 1;
-
-	if (argc < 2) {
-		short_usage(argv[0]);
-	}
-
-	while ((c = getopt_long(argc, argv, "c:vVh", NULL, NULL)) != -1) {
-		switch (c) {
-		case 'c':
-			compression = parse_compression(optarg);
-			break;
-		case 'v':
-			printf("smbinfo version %s\n", VERSION);
-			return 0;
-		case 'V':
-			verbose = 1;
-			break;
-		case 'h':
-			usage(argv[0]);
-			break;
-		default:
-			short_usage(argv[0]);
-		}
-	}
-
-	if (optind >= argc -1)
-		short_usage(argv[0]);
-
-	if ((f = open(argv[optind + 1 ], O_RDONLY)) < 0) {
-		fprintf(stderr, "Failed to open %s\n", argv[optind + 1]);
-		exit(1);
-	}
-
-	if (!strcmp(argv[optind], "fileaccessinfo"))
-		fileaccessinfo(f);
-	else if (!strcmp(argv[optind], "filealigninfo"))
-		filealigninfo(f);
-	else if (!strcmp(argv[optind], "fileallinfo"))
-		fileallinfo(f);
-	else if (!strcmp(argv[optind], "filebasicinfo"))
-		filebasicinfo(f);
-	else if (!strcmp(argv[optind], "fileeainfo"))
-		fileeainfo(f);
-	else if (!strcmp(argv[optind], "filefsfullsizeinfo"))
-		filefsfullsizeinfo(f);
-	else if (!strcmp(argv[optind], "fileinternalinfo"))
-		fileinternalinfo(f);
-	else if (!strcmp(argv[optind], "filemodeinfo"))
-		filemodeinfo(f);
-	else if (!strcmp(argv[optind], "filepositioninfo"))
-		filepositioninfo(f);
-	else if (!strcmp(argv[optind], "filestandardinfo"))
-		filestandardinfo(f);
-	else if (!strcmp(argv[optind], "fsctl-getobjid"))
-		fsctlgetobjid(f);
-	else if (!strcmp(argv[optind], "getcompression"))
-		getcompression(f);
-	else if (!strcmp(argv[optind], "setcompression"))
-		setcompression(f, compression);
-	else if (!strcmp(argv[optind], "list-snapshots"))
-		list_snapshots(f);
-	else if (!strcmp(argv[optind], "quota"))
-		quota(f);
-	else if (!strcmp(argv[optind], "secdesc"))
-		secdesc(f);
-	else if (!strcmp(argv[optind], "keys"))
-		dump_keys(f);
-	else {
-		fprintf(stderr, "Unknown command %s\n", argv[optind]);
-		exit(1);
-	}
-
-	close(f);
-	return 0;
-}