]> arthur.barton.de Git - bup.git/blob - lib/bup/xstat.py
f4e5b04659770d10fbb8b831615274018fb6e2fb
[bup.git] / lib / bup / xstat.py
1 """Enhanced stat operations for bup."""
2 import os, sys
3 import stat as pystat
4 from bup import _helpers
5
6 try:
7     _bup_utimensat = _helpers.bup_utimensat
8 except AttributeError as e:
9     _bup_utimensat = False
10
11 try:
12     _bup_utimes = _helpers.bup_utimes
13 except AttributeError as e:
14     _bup_utimes = False
15
16 try:
17     _bup_lutimes = _helpers.bup_lutimes
18 except AttributeError as e:
19     _bup_lutimes = False
20
21
22 def timespec_to_nsecs((ts_s, ts_ns)):
23     return ts_s * 10**9 + ts_ns
24
25
26 def nsecs_to_timespec(ns):
27     """Return (s, ns) where ns is always non-negative
28     and t = s + ns / 10e8""" # metadata record rep
29     ns = int(ns)
30     return (ns / 10**9, ns % 10**9)
31
32
33 def nsecs_to_timeval(ns):
34     """Return (s, us) where ns is always non-negative
35     and t = s + us / 10e5"""
36     ns = int(ns)
37     return (ns / 10**9, (ns % 10**9) / 1000)
38
39
40 def fstime_floor_secs(ns):
41     """Return largest integer not greater than ns / 10e8."""
42     return int(ns) / 10**9;
43
44
45 def fstime_to_timespec(ns):
46     return nsecs_to_timespec(ns)
47
48
49 def fstime_to_sec_str(fstime):
50     (s, ns) = fstime_to_timespec(fstime)
51     if(s < 0):
52         s += 1
53     if ns == 0:
54         return '%d' % s
55     else:
56         return '%d.%09d' % (s, ns)
57
58
59 if _bup_utimensat:
60     def utime(path, times):
61         """Times must be provided as (atime_ns, mtime_ns)."""
62         atime = nsecs_to_timespec(times[0])
63         mtime = nsecs_to_timespec(times[1])
64         _bup_utimensat(_helpers.AT_FDCWD, path, (atime, mtime), 0)
65     def lutime(path, times):
66         """Times must be provided as (atime_ns, mtime_ns)."""
67         atime = nsecs_to_timespec(times[0])
68         mtime = nsecs_to_timespec(times[1])
69         _bup_utimensat(_helpers.AT_FDCWD, path, (atime, mtime),
70                        _helpers.AT_SYMLINK_NOFOLLOW)
71 else: # Must have these if utimensat isn't available.
72     def utime(path, times):
73         """Times must be provided as (atime_ns, mtime_ns)."""
74         atime = nsecs_to_timeval(times[0])
75         mtime = nsecs_to_timeval(times[1])
76         _bup_utimes(path, (atime, mtime))
77     def lutime(path, times):
78         """Times must be provided as (atime_ns, mtime_ns)."""
79         atime = nsecs_to_timeval(times[0])
80         mtime = nsecs_to_timeval(times[1])
81         _bup_lutimes(path, (atime, mtime))
82
83 _cygwin_sys = sys.platform.startswith('cygwin')
84
85 def _fix_cygwin_id(id):
86     if id < 0:
87         id += 0x100000000
88         assert(id >= 0)
89     return id
90
91
92 class stat_result:
93     @staticmethod
94     def from_xstat_rep(st):
95         global _cygwin_sys
96         result = stat_result()
97         (result.st_mode,
98          result.st_ino,
99          result.st_dev,
100          result.st_nlink,
101          result.st_uid,
102          result.st_gid,
103          result.st_rdev,
104          result.st_size,
105          result.st_atime,
106          result.st_mtime,
107          result.st_ctime) = st
108         # Inlined timespec_to_nsecs after profiling
109         result.st_atime = result.st_atime[0] * 10**9 + result.st_atime[1]
110         result.st_mtime = result.st_mtime[0] * 10**9 + result.st_mtime[1]
111         result.st_ctime = result.st_ctime[0] * 10**9 + result.st_ctime[1]
112         if _cygwin_sys:
113             result.st_uid = _fix_cygwin_id(result.st_uid)
114             result.st_gid = _fix_cygwin_id(result.st_gid)
115         return result
116
117
118 def stat(path):
119     return stat_result.from_xstat_rep(_helpers.stat(path))
120
121
122 def fstat(path):
123     return stat_result.from_xstat_rep(_helpers.fstat(path))
124
125
126 def lstat(path):
127     return stat_result.from_xstat_rep(_helpers.lstat(path))
128
129
130 def mode_str(mode):
131     result = ''
132     # FIXME: Other types?
133     if pystat.S_ISREG(mode):
134         result += '-'
135     elif pystat.S_ISDIR(mode):
136         result += 'd'
137     elif pystat.S_ISCHR(mode):
138         result += 'c'
139     elif pystat.S_ISBLK(mode):
140         result += 'b'
141     elif pystat.S_ISFIFO(mode):
142         result += 'p'
143     elif pystat.S_ISLNK(mode):
144         result += 'l'
145     elif pystat.S_ISSOCK(mode):
146         result += 's'
147     else:
148         result += '?'
149
150     result += 'r' if (mode & pystat.S_IRUSR) else '-'
151     result += 'w' if (mode & pystat.S_IWUSR) else '-'
152     result += 'x' if (mode & pystat.S_IXUSR) else '-'
153     result += 'r' if (mode & pystat.S_IRGRP) else '-'
154     result += 'w' if (mode & pystat.S_IWGRP) else '-'
155     result += 'x' if (mode & pystat.S_IXGRP) else '-'
156     result += 'r' if (mode & pystat.S_IROTH) else '-'
157     result += 'w' if (mode & pystat.S_IWOTH) else '-'
158     result += 'x' if (mode & pystat.S_IXOTH) else '-'
159     return result
160
161
162 def classification_str(mode, include_exec):
163     if pystat.S_ISREG(mode):
164         if include_exec \
165            and (pystat.S_IMODE(mode) \
166                 & (pystat.S_IXUSR | pystat.S_IXGRP | pystat.S_IXOTH)):
167             return '*'
168         else:
169             return ''
170     elif pystat.S_ISDIR(mode):
171         return '/'
172     elif pystat.S_ISLNK(mode):
173         return '@'
174     elif pystat.S_ISFIFO(mode):
175         return '|'
176     elif pystat.S_ISSOCK(mode):
177         return '='
178     else:
179         return ''