1#!/usr/bin/env python 2 3## 4# Copyright (c) 2007 - 2009 Apple Inc. 5# 6# This is the MIT license. This software may also be distributed under the 7# same terms as Python (the PSF license). 8# 9# Permission is hereby granted, free of charge, to any person obtaining a 10# copy of this software and associated documentation files (the "Software"), 11# to deal in the Software without restriction, including without limitation 12# the rights to use, copy, modify, merge, publish, distribute, sublicense, 13# and/or sell copies of the Software, and to permit persons to whom the 14# Software is furnished to do so, subject to the following conditions: 15# 16# The above copyright notice and this permission notice shall be included in 17# all copies or substantial portions of the Software. 18# 19# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 25# IN THE SOFTWARE. 26## 27 28import sys 29import os 30import getopt 31import xattr 32import binascii 33import string 34 35def usage(e=None): 36 if e: 37 print e 38 print "" 39 40 name = os.path.basename(sys.argv[0]) 41 print "usage: %s [-l] [-r] [-v] [-x] file [file ...]" % (name,) 42 print " %s -p [-l] [-r] [-v] [-x] attr_name file [file ...]" % (name,) 43 print " %s -w [-r] [-v] [-x] attr_name attr_value file [file ...]" % (name,) 44 print " %s -d [-r] [-v] attr_name file [file ...]" % (name,) 45 print "" 46 print "The first form lists the names of all xattrs on the given file(s)." 47 print "The second form (-p) prints the value of the xattr attr_name." 48 print "The third form (-w) sets the value of the xattr attr_name to the string attr_value." 49 print "The fourth form (-d) deletes the xattr attr_name." 50 print "" 51 print "options:" 52 print " -h: print this help" 53 print " -r: act recursively" 54 print " -l: print long format (attr_name: attr_value and hex output has offsets and" 55 print " ascii representation)" 56 print " -v: also print filename (automatic with -r and with multiple files)" 57 print " -x: attr_value is represented as a hex string for input and output" 58 59 if e: 60 sys.exit(64) 61 else: 62 sys.exit(0) 63 64_FILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or '.' for x in range(256)]) 65 66def _dump(src, length=16, long=0): 67 result=[] 68 for i in xrange(0, len(src), length): 69 s = src[i:i+length] 70 hexa = ' '.join(["%02X"%ord(x) for x in s]) 71 if long: 72 printable = s.translate(_FILTER) 73 result.append("%08X %-*s |%s|" % (i, length*3, hexa, printable)) 74 else: 75 result.append(hexa) 76 if long: 77 result.append("%08x" % len(src)) 78 return '\n'.join(result) 79 80status = 0 81 82def main(): 83 global status 84 try: 85 (optargs, args) = getopt.getopt(sys.argv[1:], "hlpwdrvx", ["help"]) 86 except getopt.GetoptError, e: 87 usage(e) 88 89 attr_name = None 90 attr_value = None 91 long_format = False 92 read = False 93 hex = False 94 write = False 95 delete = False 96 recursive = False 97 verbose = False 98 99 for opt, arg in optargs: 100 if opt in ("-h", "--help"): 101 usage() 102 elif opt == "-l": 103 long_format = True 104 elif opt == "-p": 105 read = True 106 elif opt == "-w": 107 write = True 108 elif opt == "-d": 109 delete = True 110 elif opt == "-r": 111 recursive = True 112 elif opt == "-v": 113 verbose = True 114 elif opt == "-x": 115 hex = True 116 117 if write or delete: 118 if long_format: 119 usage("-l not allowed with -w or -p") 120 121 if read or write or delete: 122 if not args: 123 usage("No attr_name") 124 attr_name = args.pop(0) 125 126 if write: 127 if not args: 128 usage("No attr_value") 129 attr_value = args.pop(0) 130 131 if len(args) > 1: 132 multiple_files = True 133 else: 134 multiple_files = False 135 136 for filename in args: 137 def doSinglePathChange(filename,attr_name,attr_value,read,write,delete,recursive): 138 def onError(e): 139 global status 140 if not os.path.exists(filename): 141 sys.stderr.write("xattr: No such file: %s\n" % (filename,)) 142 else: 143 sys.stderr.write("xattr: " + str(e) + "\n") 144 status = 1 145 146 def hasNulls(s): 147 try: 148 if s.find('\0') >= 0: 149 return True 150 return False 151 except UnicodeDecodeError: 152 return True 153 154 if verbose or recursive or multiple_files: 155 file_prefix = "%s: " % filename 156 else: 157 file_prefix = "" 158 159 if recursive and os.path.isdir(filename) and not os.path.islink(filename): 160 listdir = os.listdir(filename) 161 for subfilename in listdir: 162 doSinglePathChange(filename+'/'+subfilename,attr_name,attr_value,read,write,delete,recursive) 163 164 try: 165 attrs = xattr.xattr(filename) 166 except (IOError, OSError), e: 167 onError(e) 168 return 169 170 if write: 171 try: 172 if hex: 173 # strip whitespace and unhexlify 174 attr_value = binascii.unhexlify(attr_value.translate(string.maketrans('', ''), string.whitespace)) 175 attrs[attr_name] = attr_value 176 except (IOError, OSError, TypeError), e: 177 onError(e) 178 return 179 180 elif delete: 181 try: 182 del attrs[attr_name] 183 except (IOError, OSError), e: 184 onError(e) 185 return 186 except KeyError: 187 if not recursive: 188 onError("%s: No such xattr: %s" % (filename, attr_name,)) 189 return 190 191 else: 192 try: 193 if read: 194 attr_names = (attr_name,) 195 else: 196 attr_names = [a.encode('utf8') for a in attrs.keys()] 197 except (IOError, OSError), e: 198 onError(e) 199 return 200 201 for attr_name in attr_names: 202 try: 203 if long_format: 204 if hex or hasNulls(attrs[attr_name]): 205 print "%s%s:" % (file_prefix, attr_name) 206 print _dump(attrs[attr_name], long=1) 207 else: 208 print "%s%s: %s" % (file_prefix, attr_name, attrs[attr_name]) 209 else: 210 if read: 211 if hex or hasNulls(attrs[attr_name]): 212 if len(file_prefix) > 0: 213 print file_prefix 214 print _dump(attrs[attr_name]) 215 else: 216 print "%s%s" % (file_prefix, attrs[attr_name]) 217 else: 218 print "%s%s" % (file_prefix, attr_name) 219 except (IOError, OSError), e: 220 onError(e) 221 return 222 except KeyError: 223 onError("%s: No such xattr: %s" % (filename, attr_name)) 224 return 225 226 return 227 228 doSinglePathChange(filename,attr_name,attr_value,read,write,delete,recursive) 229 sys.exit(status) 230 231if __name__ == "__main__": 232 main() 233