Coverage for /pythoncovmergedfiles/medio/medio/src/paramiko/paramiko/sftp_attr.py: 15%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

151 statements  

1# Copyright (C) 2003-2006 Robey Pointer <robeypointer@gmail.com> 

2# 

3# This file is part of paramiko. 

4# 

5# Paramiko is free software; you can redistribute it and/or modify it under the 

6# terms of the GNU Lesser General Public License as published by the Free 

7# Software Foundation; either version 2.1 of the License, or (at your option) 

8# any later version. 

9# 

10# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 

11# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 

12# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 

13# details. 

14# 

15# You should have received a copy of the GNU Lesser General Public License 

16# along with Paramiko; if not, write to the Free Software Foundation, Inc., 

17# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 

18 

19import stat 

20import time 

21from paramiko.common import x80000000, o700, o70, xffffffff 

22 

23 

24class SFTPAttributes: 

25 """ 

26 Representation of the attributes of a file (or proxied file) for SFTP in 

27 client or server mode. It attempts to mirror the object returned by 

28 `os.stat` as closely as possible, so it may have the following fields, 

29 with the same meanings as those returned by an `os.stat` object: 

30 

31 - ``st_size`` 

32 - ``st_uid`` 

33 - ``st_gid`` 

34 - ``st_mode`` 

35 - ``st_atime`` 

36 - ``st_mtime`` 

37 

38 Because SFTP allows flags to have other arbitrary named attributes, these 

39 are stored in a dict named ``attr``. Occasionally, the filename is also 

40 stored, in ``filename``. 

41 """ 

42 

43 FLAG_SIZE = 1 

44 FLAG_UIDGID = 2 

45 FLAG_PERMISSIONS = 4 

46 FLAG_AMTIME = 8 

47 FLAG_EXTENDED = x80000000 

48 

49 def __init__(self): 

50 """ 

51 Create a new (empty) SFTPAttributes object. All fields will be empty. 

52 """ 

53 self._flags = 0 

54 self.st_size = None 

55 self.st_uid = None 

56 self.st_gid = None 

57 self.st_mode = None 

58 self.st_atime = None 

59 self.st_mtime = None 

60 self.attr = {} 

61 

62 @classmethod 

63 def from_stat(cls, obj, filename=None): 

64 """ 

65 Create an `.SFTPAttributes` object from an existing ``stat`` object (an 

66 object returned by `os.stat`). 

67 

68 :param object obj: an object returned by `os.stat` (or equivalent). 

69 :param str filename: the filename associated with this file. 

70 :return: new `.SFTPAttributes` object with the same attribute fields. 

71 """ 

72 attr = cls() 

73 attr.st_size = obj.st_size 

74 attr.st_uid = obj.st_uid 

75 attr.st_gid = obj.st_gid 

76 attr.st_mode = obj.st_mode 

77 attr.st_atime = obj.st_atime 

78 attr.st_mtime = obj.st_mtime 

79 if filename is not None: 

80 attr.filename = filename 

81 return attr 

82 

83 def __repr__(self): 

84 return "<SFTPAttributes: {}>".format(self._debug_str()) 

85 

86 # ...internals... 

87 @classmethod 

88 def _from_msg(cls, msg, filename=None, longname=None): 

89 attr = cls() 

90 attr._unpack(msg) 

91 if filename is not None: 

92 attr.filename = filename 

93 if longname is not None: 

94 attr.longname = longname 

95 return attr 

96 

97 def _unpack(self, msg): 

98 self._flags = msg.get_int() 

99 if self._flags & self.FLAG_SIZE: 

100 self.st_size = msg.get_int64() 

101 if self._flags & self.FLAG_UIDGID: 

102 self.st_uid = msg.get_int() 

103 self.st_gid = msg.get_int() 

104 if self._flags & self.FLAG_PERMISSIONS: 

105 self.st_mode = msg.get_int() 

106 if self._flags & self.FLAG_AMTIME: 

107 self.st_atime = msg.get_int() 

108 self.st_mtime = msg.get_int() 

109 if self._flags & self.FLAG_EXTENDED: 

110 count = msg.get_int() 

111 for i in range(count): 

112 self.attr[msg.get_string()] = msg.get_string() 

113 

114 def _pack(self, msg): 

115 self._flags = 0 

116 if self.st_size is not None: 

117 self._flags |= self.FLAG_SIZE 

118 if (self.st_uid is not None) and (self.st_gid is not None): 

119 self._flags |= self.FLAG_UIDGID 

120 if self.st_mode is not None: 

121 self._flags |= self.FLAG_PERMISSIONS 

122 if (self.st_atime is not None) and (self.st_mtime is not None): 

123 self._flags |= self.FLAG_AMTIME 

124 if len(self.attr) > 0: 

125 self._flags |= self.FLAG_EXTENDED 

126 msg.add_int(self._flags) 

127 if self._flags & self.FLAG_SIZE: 

128 msg.add_int64(self.st_size) 

129 if self._flags & self.FLAG_UIDGID: 

130 msg.add_int(self.st_uid) 

131 msg.add_int(self.st_gid) 

132 if self._flags & self.FLAG_PERMISSIONS: 

133 msg.add_int(self.st_mode) 

134 if self._flags & self.FLAG_AMTIME: 

135 # throw away any fractional seconds 

136 msg.add_int(int(self.st_atime)) 

137 msg.add_int(int(self.st_mtime)) 

138 if self._flags & self.FLAG_EXTENDED: 

139 msg.add_int(len(self.attr)) 

140 for key, val in self.attr.items(): 

141 msg.add_string(key) 

142 msg.add_string(val) 

143 return 

144 

145 def _debug_str(self): 

146 out = "[ " 

147 if self.st_size is not None: 

148 out += "size={} ".format(self.st_size) 

149 if (self.st_uid is not None) and (self.st_gid is not None): 

150 out += "uid={} gid={} ".format(self.st_uid, self.st_gid) 

151 if self.st_mode is not None: 

152 out += "mode=" + oct(self.st_mode) + " " 

153 if (self.st_atime is not None) and (self.st_mtime is not None): 

154 out += "atime={} mtime={} ".format(self.st_atime, self.st_mtime) 

155 for k, v in self.attr.items(): 

156 out += '"{}"={!r} '.format(str(k), v) 

157 out += "]" 

158 return out 

159 

160 @staticmethod 

161 def _rwx(n, suid, sticky=False): 

162 if suid: 

163 suid = 2 

164 out = "-r"[n >> 2] + "-w"[(n >> 1) & 1] 

165 if sticky: 

166 out += "-xTt"[suid + (n & 1)] 

167 else: 

168 out += "-xSs"[suid + (n & 1)] 

169 return out 

170 

171 def __str__(self): 

172 """create a unix-style long description of the file (like ls -l)""" 

173 if self.st_mode is not None: 

174 kind = stat.S_IFMT(self.st_mode) 

175 if kind == stat.S_IFIFO: 

176 ks = "p" 

177 elif kind == stat.S_IFCHR: 

178 ks = "c" 

179 elif kind == stat.S_IFDIR: 

180 ks = "d" 

181 elif kind == stat.S_IFBLK: 

182 ks = "b" 

183 elif kind == stat.S_IFREG: 

184 ks = "-" 

185 elif kind == stat.S_IFLNK: 

186 ks = "l" 

187 elif kind == stat.S_IFSOCK: 

188 ks = "s" 

189 else: 

190 ks = "?" 

191 ks += self._rwx( 

192 (self.st_mode & o700) >> 6, self.st_mode & stat.S_ISUID 

193 ) 

194 ks += self._rwx( 

195 (self.st_mode & o70) >> 3, self.st_mode & stat.S_ISGID 

196 ) 

197 ks += self._rwx( 

198 self.st_mode & 7, self.st_mode & stat.S_ISVTX, True 

199 ) 

200 else: 

201 ks = "?---------" 

202 # compute display date 

203 if (self.st_mtime is None) or (self.st_mtime == xffffffff): 

204 # shouldn't really happen 

205 datestr = "(unknown date)" 

206 else: 

207 time_tuple = time.localtime(self.st_mtime) 

208 if abs(time.time() - self.st_mtime) > 15_552_000: 

209 # (15,552,000s = 6 months) 

210 datestr = time.strftime("%d %b %Y", time_tuple) 

211 else: 

212 datestr = time.strftime("%d %b %H:%M", time_tuple) 

213 filename = getattr(self, "filename", "?") 

214 

215 # not all servers support uid/gid 

216 uid = self.st_uid 

217 gid = self.st_gid 

218 size = self.st_size 

219 if uid is None: 

220 uid = 0 

221 if gid is None: 

222 gid = 0 

223 if size is None: 

224 size = 0 

225 

226 # TODO: not sure this actually worked as expected beforehand, leaving 

227 # it untouched for the time being, re: .format() upgrade, until someone 

228 # has time to doublecheck 

229 return "%s 1 %-8d %-8d %8d %-12s %s" % ( 

230 ks, 

231 uid, 

232 gid, 

233 size, 

234 datestr, 

235 filename, 

236 ) 

237 

238 def asbytes(self): 

239 return str(self).encode()