diff --git a/getcifsacl.c b/getcifsacl.c
new file mode 100644
index 0000000..e11148f
--- /dev/null
+++ b/getcifsacl.c
@@ -0,0 +1,372 @@
+/*
+* getcifsacl utility
+*
+* Copyright (C) Shirish Pargaonkar (shirishp@us.ibm.com) 2011
+*
+* Used to display a security descriptor including ACL of a file object
+* that belongs to a share mounted using option cifsacl.
+*
+* 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
+*/
+
+#include "cifsacl.h"
+
+static const char *prog = "getcifsacl";
+
+static void
+print_each_ace_mask(uint32_t mask)
+{
+	if ((mask & ALL_READ_BITS) && ((mask & EREAD) != EREAD &&
+			(mask & OREAD) != OREAD && (mask & BREAD) != BREAD)) {
+		printf("0x%x", mask);
+		return;
+	}
+
+	if ((mask & ALL_WRITE_BITS) && (mask & EWRITE) != EWRITE) {
+		printf("0x%x", mask);
+		return;
+	}
+
+	if ((mask & EREAD) == EREAD || (mask & OREAD) == OREAD ||
+			(mask & BREAD) == BREAD)
+		printf("R");
+	if ((mask & EWRITE) == EWRITE)
+		printf("W");
+	if ((mask & EXEC) == EXEC)
+		printf("X");
+	if ((mask & DELETE) == DELETE)
+		printf("D");
+	if ((mask & WRITE_DAC) == WRITE_DAC)
+		printf("P");
+	if ((mask & WRITE_OWNER) == WRITE_OWNER)
+		printf("O");
+}
+
+static void
+print_ace_mask(uint32_t mask, int raw)
+{
+	if (raw) {
+		printf("0x%x\n", mask);
+		return;
+	}
+
+	if (mask == FULL_CONTROL)
+		printf("FULL");
+	else if (mask == CHANGE)
+		printf("CHANGE");
+	else if (mask == DELETE)
+		printf("D");
+	else if (mask == EREAD)
+		printf("READ");
+	else if (mask & DELDHLD)
+		printf("0x%x", mask);
+	else
+		print_each_ace_mask(mask);
+
+	printf("\n");
+	return;
+}
+
+static void
+print_ace_flags(uint8_t flags, int raw)
+{
+	bool mflags = false;
+
+	if (raw) {
+		printf("0x%x", flags);
+		return;
+	}
+
+	if (flags & OBJECT_INHERIT_FLAG) {
+		if (mflags)
+			printf("|");
+		else
+			mflags = true;
+		printf("OI");
+	}
+	if (flags & CONTAINER_INHERIT_FLAG) {
+		if (mflags)
+			printf("|");
+		else
+			mflags = true;
+		printf("CI");
+	}
+	if (flags & NO_PROPAGATE_INHERIT_FLAG) {
+		if (mflags)
+			printf("|");
+		else
+			mflags = true;
+		printf("NP");
+	}
+	if (flags & INHERIT_ONLY_FLAG) {
+		if (mflags)
+			printf("|");
+		else
+			mflags = true;
+		printf("IO");
+	}
+	if (flags & INHERITED_ACE_FLAG) {
+		if (mflags)
+			printf("|");
+		else
+			mflags = true;
+		printf("I");
+	}
+
+	if (!mflags)
+		printf("0x0");
+}
+
+static void
+print_ace_type(uint8_t acetype, int raw)
+{
+	if (raw) {
+		printf("0x%x", acetype);
+		return;
+	}
+
+	switch (acetype) {
+	case ACCESS_ALLOWED:
+		printf("ALLOWED");
+		break;
+	case ACCESS_DENIED:
+		printf("DENIED");
+		break;
+	case ACCESS_ALLOWED_OBJECT:
+		printf("OBJECT_ALLOWED");
+		break;
+	case ACCESS_DENIED_OBJECT:
+		printf("OBJECT_DENIED");
+		break;
+	default:
+		printf("UNKNOWN");
+		break;
+	}
+}
+
+static void
+print_sid(struct wbcDomainSid *sidptr, int raw)
+{
+	int i;
+	int num_auths;
+	int num_auth = MAX_NUM_AUTHS;
+	wbcErr rc;
+	char *domain_name = NULL;
+	char *sidname = NULL;
+	enum wbcSidType sntype;
+
+	if (raw)
+		goto print_sid_raw;
+
+	rc = wbcLookupSid(sidptr, &domain_name, &sidname, &sntype);
+	if (!rc) {
+		printf("%s", domain_name);
+		if (strlen(domain_name))
+			printf("%c", '\\');
+		printf("%s", sidname);
+		return;
+	}
+
+print_sid_raw:
+	num_auths = sidptr->num_auths;
+	printf("S");
+	printf("-%d", sidptr->sid_rev_num);
+	for (i = 0; i < num_auth; ++i)
+		if (sidptr->id_auth[i])
+			printf("-%d", sidptr->id_auth[i]);
+	for (i = 0; i < num_auths; i++)
+		printf("-%u", le32toh(sidptr->sub_auths[i]));
+}
+
+static void
+print_ace(struct cifs_ace *pace, char *end_of_acl, int raw)
+{
+	/* validate that we do not go past end of acl */
+
+	if (le16toh(pace->size) < 16)
+		return;
+
+	if (end_of_acl < (char *)pace + le16toh(pace->size))
+		return;
+
+	printf("ACL:");
+	print_sid((struct wbcDomainSid *)&pace->sid, raw);
+	printf(":");
+	print_ace_type(pace->type, raw);
+	printf("/");
+	print_ace_flags(pace->flags, raw);
+	printf("/");
+	print_ace_mask(pace->access_req, raw);
+
+
+	return;
+}
+
+static void
+parse_dacl(struct cifs_ctrl_acl *pdacl, char *end_of_acl, int raw)
+{
+	int i;
+	int num_aces = 0;
+	int acl_size;
+	char *acl_base;
+	struct cifs_ace *pace;
+
+	if (!pdacl)
+		return;
+
+	if (end_of_acl < (char *)pdacl + le16toh(pdacl->size))
+		return;
+
+	acl_base = (char *)pdacl;
+	acl_size = sizeof(struct cifs_ctrl_acl);
+
+	num_aces = le32toh(pdacl->num_aces);
+	if (num_aces  > 0) {
+		for (i = 0; i < num_aces; ++i) {
+			pace = (struct cifs_ace *) (acl_base + acl_size);
+			print_ace(pace, end_of_acl, raw);
+			acl_base = (char *)pace;
+			acl_size = le16toh(pace->size);
+		}
+	}
+
+	return;
+}
+
+static int
+parse_sid(struct wbcDomainSid *psid, char *end_of_acl, char *title, int raw)
+{
+	if (end_of_acl < (char *)psid + 8)
+		return -EINVAL;
+
+	if (title)
+		printf("%s:", title);
+	print_sid((struct wbcDomainSid *)psid, raw);
+	printf("\n");
+
+	return 0;
+}
+
+static int
+parse_sec_desc(struct cifs_ntsd *pntsd, ssize_t acl_len, int raw)
+{
+	int rc;
+	uint32_t dacloffset;
+	char *end_of_acl = ((char *)pntsd) + acl_len;
+	struct wbcDomainSid *owner_sid_ptr, *group_sid_ptr;
+	struct cifs_ctrl_acl *dacl_ptr; /* no need for SACL ptr */
+
+	if (pntsd == NULL)
+		return -EIO;
+
+	owner_sid_ptr = (struct wbcDomainSid *)((char *)pntsd +
+				le32toh(pntsd->osidoffset));
+	group_sid_ptr = (struct wbcDomainSid *)((char *)pntsd +
+				le32toh(pntsd->gsidoffset));
+	dacloffset = le32toh(pntsd->dacloffset);
+	dacl_ptr = (struct cifs_ctrl_acl *)((char *)pntsd + dacloffset);
+	printf("REVISION:0x%x\n", pntsd->revision);
+	printf("CONTROL:0x%x\n", pntsd->type);
+
+	rc = parse_sid(owner_sid_ptr, end_of_acl, "OWNER", raw);
+	if (rc)
+		return rc;
+
+	rc = parse_sid(group_sid_ptr, end_of_acl, "GROUP", raw);
+	if (rc)
+		return rc;
+
+	if (dacloffset)
+		parse_dacl(dacl_ptr, end_of_acl, raw);
+	else
+		printf("No ACL\n"); /* BB grant all or default perms? */
+
+	return 0;
+}
+
+static void
+getcifsacl_usage(void)
+{
+	fprintf(stderr,
+	"%s: Display CIFS/NTFS ACL in a security descriptor of a file object\n",
+		prog);
+	fprintf(stderr, "Usage: %s [option] <file_name>\n", prog);
+	fprintf(stderr, "Valid options:\n");
+	fprintf(stderr, "\t-v	Version of the program\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "\t-r	Display raw values of the ACE fields\n");
+	fprintf(stderr, "\nRefer to getcifsacl(8) manpage for details\n");
+}
+
+int
+main(const int argc, char *const argv[])
+{
+	int c, raw = 0;
+	ssize_t attrlen;
+	size_t bufsize = BUFSIZE;
+	char *filename, *attrval;
+
+	openlog(prog, 0, LOG_DAEMON);
+
+	while ((c = getopt_long(argc, argv, "r:v", NULL, NULL)) != -1) {
+		switch (c) {
+		case 'v':
+			printf("Version: %s\n", VERSION);
+			goto out;
+		case 'r':
+			raw = 1;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (raw && argc == 3)
+		filename = argv[2];
+	else if (argc == 2)
+		filename = argv[1];
+	else {
+		getcifsacl_usage();
+		return 0;
+	}
+
+cifsacl:
+	if (bufsize >= XATTR_SIZE_MAX) {
+		printf("buffer to allocate exceeds max size of %d\n",
+				XATTR_SIZE_MAX);
+		return -1;
+	}
+
+	attrval = malloc(bufsize * sizeof(char));
+	if (!attrval) {
+		printf("error allocating memory for attribute value buffer\n");
+		return -1;
+	}
+
+	attrlen = getxattr(filename, ATTRNAME, attrval, bufsize);
+	if (attrlen == -1) {
+		if (errno == ERANGE) {
+			free(attrval);
+			bufsize += BUFSIZE;
+			goto cifsacl;
+		} else
+			printf("getxattr error: %d\n", errno);
+	}
+
+	parse_sec_desc((struct cifs_ntsd *)attrval, attrlen, raw);
+
+	free(attrval);
+
+out:
+	return 0;
+}
