fatattr: program to set attributes on a FAT filesystem
diff --git a/MCONFIG.in b/MCONFIG.in
new file mode 100644
index 0000000..1c0ba11
--- /dev/null
+++ b/MCONFIG.in
@@ -0,0 +1,38 @@
+# Prefixes
+prefix      = @prefix@
+exec_prefix = @exec_prefix@
+
+# Directory for user binaries
+bindir  = @bindir@
+
+# Man page tree
+mandir  = @mandir@
+
+# System binaries
+sbindir = @sbindir@
+
+# Install into chroot area
+# Useful when making rpms and similar
+INSTALLROOT = 
+
+# Install program
+INSTALL         = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA    = @INSTALL_DATA@
+
+# Compiler and compiler flags
+CC      = @CC@
+CFLAGS  = @CFLAGS@
+
+# Link flags
+LDFLAGS = @LDFLAGS@
+
+# Libraries
+LIBS    = @LIBS@
+
+# Additional library we need to build
+LIBOBJS	= @LIBOBJS@
+
+# ar and ranlib (for making libraries)
+AR	= ar cq
+RANLIB	= @RANLIB@
diff --git a/MRULES b/MRULES
new file mode 100644
index 0000000..decd202
--- /dev/null
+++ b/MRULES
@@ -0,0 +1,22 @@
+# Standard compilation rules (don't use make builtins)
+
+.SUFFIXES: .c .cc .o .s .S .i
+
+.c.o:
+	$(CC) $(CFLAGS) -c $<
+
+.c.s:
+	$(CC) $(CFLAGS) -S -o $@ $<
+
+.c.i:
+	$(CC) $(CFLAGS) -E -o $@ $<
+
+.cc.o:
+	$(CXX) $(CXXFLAGS) -c $<
+
+.cc.s:
+	$(CXX) $(CXXFLAGS) -S -o $@ $<
+
+.cc.i:
+	$(CXX) $(CXXFLAGS) -E -o $@ $<
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..ea21bf1
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,79 @@
+## -----------------------------------------------------------------------
+##   
+##   Copyright 2005 H. Peter Anvin - All Rights Reserved
+##
+##   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, Inc., 675 Mass Ave, Cambridge MA 02139,
+##   USA; either version 2 of the License, or (at your option) any later
+##   version; incorporated herein by reference.
+##
+## -----------------------------------------------------------------------
+
+##
+## Makefile for fatattr
+##
+
+-include MCONFIG
+include MRULES
+
+PROGS     = fatattr
+MAN1PAGES = # fatattr.1
+
+OBJS = 	fatattr.o
+
+all: $(PROGS)
+
+spec: fatattr.spec
+
+clean:
+	rm -f *.o *.i *.s version.h $(PROGS)
+
+distclean: clean
+	rm -f MCONFIG config.status config.log config.h *~ \#*
+	rm -f core *.orig *.rej
+	rm -rf *.cache
+
+release:
+	$(MAKE) configure
+	$(MAKE) spec
+	$(MAKE) distclean
+
+mkzftree: $(OBJS)
+	$(CC) $(LDFLAGS) -o mkzftree $(OBJS) $(LIBS)
+
+spotless: distclean
+	rm -f configure config.h.in zisofs-tools.spec
+
+install: all
+	mkdir -p $(INSTALLROOT)$(bindir)
+	$(INSTALL_PROGRAM) $(PROGS) $(INSTALLROOT)$(bindir)
+	mkdir -p $(INSTALLROOT)$(mandir)/man1
+	$(INSTALL_DATA) $(MAN1PAGES) $(INSTALLROOT)$(mandir)/man1
+
+config:	MCONFIG
+
+MCONFIG: configure MCONFIG.in config.h.in
+	./configure
+
+config.h: MCONFIG
+	: Generated by side effect
+
+config.h.in: configure.in
+	rm -f config.h.in
+	autoheader
+
+configure: configure.in aclocal.m4
+	autoconf
+	rm -f MCONFIG config.cache config.log config.status config.h
+
+#
+# Version header
+#
+VERSION = $(shell cat version)
+
+version.h: version
+	echo "#define ZISOFS_TOOLS_VERSION \"$(VERSION)\"" > version.h
+
+fatattr.spec: fatattr.spec.in version
+	sed -e 's/@@VERSION@@/$(VERSION)/g' < $< > $@
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644
index 0000000..a3d8427
--- /dev/null
+++ b/aclocal.m4
@@ -0,0 +1,35 @@
+dnl --------------------------------------------------------------------------
+dnl PA_ADD_CFLAGS()
+dnl
+dnl Attempt to add the given option to CFLAGS, if it doesn't break compilation
+dnl --------------------------------------------------------------------------
+AC_DEFUN(PA_ADD_CFLAGS,
+[AC_MSG_CHECKING([if $CC accepts $1])
+ pa_add_cflags__old_cflags="$CFLAGS"
+ CFLAGS="$CFLAGS $1"
+ AC_TRY_LINK([#include <stdio.h>],
+ [printf("Hello, World!\n");],
+ AC_MSG_RESULT([yes]),
+ AC_MSG_RESULT([no])
+ CFLAGS="$pa_add_cflags__old_cflags")])
+
+dnl ------------------------------------------------------------------------
+dnl  PA_WITH_BOOL
+dnl
+dnl  PA_WITH_BOOL(option, default, help, enable, disable)
+dnl
+dnl  Provides a more convenient way to specify --with-option and
+dnl  --without-option, with a default.  default should be either 0 or 1.
+dnl ------------------------------------------------------------------------
+AC_DEFUN(PA_WITH_BOOL,
+[AC_ARG_WITH([$1], [$3],
+if test ["$withval"] != no; then
+[$4]
+else
+[$5]
+fi,
+if test [$2] -ne 0; then
+[$4]
+else
+[$5]
+fi)])
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000..c821e28
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,58 @@
+dnl
+dnl autoconf input file to generate MCONFIG
+dnl
+
+AC_PREREQ(2.57)
+AC_REVISION([$Id$])
+AC_INIT(MCONFIG.in)
+AC_PREFIX_DEFAULT(/usr)
+
+AC_PROG_CC
+AC_GNU_SOURCE
+AC_SYS_LARGEFILE
+AC_C_CONST
+AC_C_INLINE
+
+PA_ADD_CFLAGS(-Wall)
+PA_ADD_CFLAGS(-W)
+PA_ADD_CFLAGS(-Wpointer-arith)
+PA_ADD_CFLAGS(-Wbad-function-cast)
+PA_ADD_CFLAGS(-Wcast-equal)
+PA_ADD_CFLAGS(-Wstrict-prototypes)
+PA_ADD_CFLAGS(-Wmissing-prototypes)
+PA_ADD_CFLAGS(-Wmissing-declarations)
+PA_ADD_CFLAGS(-Wnested-externs)
+PA_ADD_CFLAGS(-Winline)
+PA_ADD_CFLAGS(-Wshadow)
+PA_ADD_CFLAGS(-Wcast-align)
+PA_ADD_CFLAGS(-pipe)
+
+AC_CHECK_HEADERS(inttypes.h)
+AC_CHECK_HEADERS(sysexits.h)
+AC_CHECK_HEADERS(getopt.h)
+AC_CHECK_HEADERS(endian.h)
+
+AC_CHECK_TYPE(off_t, signed long)
+AC_CHECK_TYPE(size_t, unsigned long)
+AC_CHECK_TYPE(ssize_t, signed long)
+
+AC_CHECK_FUNCS(lchown)
+AC_CHECK_FUNCS(utimes)
+
+AC_CHECK_MEMBERS(struct stat.st_mtim.tv_usec, , , [#include <sys/stat.h>])
+AH_TEMPLATE([HAVE_STRUCT_STAT_ST_MTIM_TV_USEC],
+          [Define to 1 if `struct stat.st_[mca]tim' are struct timeval])
+AC_CHECK_MEMBERS(struct stat.st_mtim.tv_nsec, , , [#include <sys/stat.h>])
+AH_TEMPLATE([HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC], 
+          [Define to 1 if `struct stat.st_[mca]tim' are struct timespec])
+
+AC_SEARCH_LIBS(compress2, z, , [AC_MSG_ERROR(zlib not found, cannot continue)])
+AC_SEARCH_LIBS(getopt_long, [getopt getopt_long])
+AC_CHECK_FUNCS(getopt_long)
+AH_TEMPLATE([HAVE_GETOPT_LONG], [Define to 1 if you have the `getopt_long' function])
+
+AC_PROG_RANLIB
+AC_PROG_INSTALL
+
+AC_CONFIG_HEADER(config.h)
+AC_OUTPUT(MCONFIG)
diff --git a/fatattr.1 b/fatattr.1
new file mode 100644
index 0000000..47afc3c
--- /dev/null
+++ b/fatattr.1
@@ -0,0 +1,66 @@
+.\" $Id$
+.\" -----------------------------------------------------------------------
+.\"   
+.\"   Copyright 2005 H. Peter Anvin - All Rights Reserved
+.\"
+.\"   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, Inc., 675 Mass Ave, Cambridge MA 02139,
+.\"   USA; either version 2 of the License, or (at your option) any later
+.\"   version; incorporated herein by reference.
+.\"
+.\" -----------------------------------------------------------------------
+.TH FATATTR "1" "3 January 2005" "fatattr" "H. Peter Anvin"
+.SH NAME
+fatattr \- Display or change attributes on a FAT filesystem
+.SH SYNOPSIS
+.B fatattr
+[\fI+-rhsvda67\fP] \fIfiles\fP...
+.SH DESCRIPTION
+.PP
+.B fatattr
+displays or changes attributes on an MS-DOS FAT filesystem.  If only a
+list of diles (or directories) are given, it displays the attributes
+for those files or directories.  If given a list of attributes, it
+will add (+) or remove (-) the specified attributes from the given
+list of files or directories.
+.PP
+The attributes are:
+.TP
+.B r
+File or directory is readonly.
+.TP
+.B h
+File or directory is hidden.
+.TP
+.B s
+File or directory is used by the system in a nonstandard way.
+Standard defragmentation software will avoid this file.  Microsoft
+operating systems will treat any file or directory with the system
+attribute as hidden, even if the hidden bit is not set.
+.TP
+.B v
+Entry is a volume label.  This bit cannot be changed.
+.TP
+.B d
+Entry is a directory.  This bit cannot be changed.
+.TP
+.B a
+File has been changed.  This bit is set any time a file (but not a
+directory) is changed, and is typically cleared by backup software.
+.TP
+\fB6\fP, \fB7\fP
+Unused attributes, set to zero when the file is created and otherwise
+unchanged.
+.SH BUGS
+Requires Linux kernel version 2.6.11 or later.
+.SH AUTHOR
+Written by H. Peter Anvin <hpa@zytor.com>.
+.SH COPYRIGHT
+Copyright \(co 2005 H. Peter Anvin.
+.br
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+.SH "SEE ALSO"
+.BR chmod (1),
+.BR chattr (1)
diff --git a/fatattr.c b/fatattr.c
new file mode 100644
index 0000000..7f0a35b
--- /dev/null
+++ b/fatattr.c
@@ -0,0 +1,172 @@
+#ident "$Id$"
+/* ----------------------------------------------------------------------- *
+ *   
+ *   Copyright 2005 H. Peter Anvin - All Rights Reserved
+ *
+ *   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, Inc., 53 Temple Place Ste 330,
+ *   Boston MA 02111-1307, USA; either version 2 of the License, or
+ *   (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * fatattr.c
+ *
+ * Display or change attributes on a FAT filesystem, similar to the
+ * DOS attrib command.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <sysexits.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <linux/msdos_fs.h>
+
+#ifndef FAT_IOCTL_GET_ATTRIBUTES
+# define FAT_IOCTL_GET_ATTRIBUTES        _IOR('r', 0x10, __u8)
+#endif
+#ifndef FAT_IOCTL_SET_ATTRIBUTES
+# define FAT_IOCTL_SET_ATTRIBUTES        _IOW('r', 0x11, __u8)
+#endif
+
+const char *program;
+const char bit_to_char[8] = "rhsvda67";
+
+static int
+chattr(const char *file, uint8_t s_attr, uint8_t c_attr)
+{
+  int fd = -1;
+  uint8_t attr, nattr;
+  int e;
+
+  fd = open(file, O_RDONLY);
+  if ( fd < 0 )
+    goto err;
+
+  if ( ioctl(fd, FAT_IOCTL_GET_ATTRIBUTES, &attr) )
+    goto err;
+
+  nattr = (attr & ~c_attr) | s_attr;
+
+  if ( nattr != attr ) {
+    if ( ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &nattr) )
+      goto err;
+  }
+
+  close(fd);
+  return 0;
+
+ err:
+  e = errno;
+  if ( fd >= 0 )
+    close(fd);
+
+  errno = e;
+  return -1;
+}
+
+static int
+lsattr(const char *file)
+{
+  int fd = -1;
+  uint8_t attr;
+  int i, e;
+
+  fd = open(file, O_RDONLY);
+  if ( fd < 0 )
+    goto err;
+
+  if ( ioctl(fd, FAT_IOCTL_GET_ATTRIBUTES, &attr) )
+    goto err;
+
+  close(fd);
+
+  for ( i = 0 ; i < 8 ; i++ ) {
+    putchar( (attr & 1) ? bit_to_char[i] : ' ' );
+    attr >>= 1;
+  }
+
+  putchar(' ');
+  puts(file);
+  return 0;
+
+ err:
+  e = errno;
+  if ( fd >= 0 )
+    close(fd);
+  errno = e;
+  return -1;
+}
+
+static uint8_t
+parse_attr(const char *attrs)
+{
+  uint8_t attr = 0;
+  const char *p;
+
+  while ( *attrs ) {
+    p = strchr(bit_to_char, *attrs);
+    if ( !p ) {
+      fprintf(stderr, "%s: unknown attribute: '%c'\n", program, *attrs);
+      exit(EX_USAGE);
+    }
+
+    attr |= 1 << (p-bit_to_char);
+    attrs++;
+  }
+
+  return attr;
+}
+
+static void __attribute__((noreturn)) usage(void)
+{
+  fprintf(stderr, "Usage: %s [-+rhsa67] files...\n", program);
+  exit(EX_USAGE);
+}
+
+int main(int argc, char *argv[])
+{
+  uint8_t s_attr = 0;		/* Attributes to set */
+  uint8_t c_attr = 0;		/* Attributes to clear */
+  int i;
+  int files = 0;		/* Files processed */
+  int e;
+
+  program = argv[0];
+
+  for ( i = 1 ; i < argc ; i++ ) {
+    if ( argv[i][0] == '-' )
+      c_attr |= parse_attr(argv[i]+1);
+    else if ( argv[i][0] == '+' )
+      s_attr |= parse_attr(argv[i]+1);
+    else {
+      e = 0;
+
+      if ( c_attr | s_attr )
+	e = chattr(argv[i], s_attr, c_attr);
+      else
+	e = lsattr(argv[i]);
+
+      if ( e ) {
+	perror(argv[i]);
+	return EX_DATAERR;
+      }
+
+      files++;
+    }
+  }
+
+  if ( !files )
+    usage();
+
+  return 0;
+}
+