rsync_remote.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. """
  2. (c) 2006-2013, Armin Rigo, Holger Krekel, Maciej Fijalkowski
  3. """
  4. def serve_rsync(channel):
  5. import os
  6. import stat
  7. import shutil
  8. from hashlib import md5
  9. destdir, options = channel.receive()
  10. modifiedfiles = []
  11. def remove(path):
  12. assert path.startswith(destdir)
  13. try:
  14. os.unlink(path)
  15. except OSError:
  16. # assume it's a dir
  17. shutil.rmtree(path, True)
  18. def receive_directory_structure(path, relcomponents):
  19. try:
  20. st = os.lstat(path)
  21. except OSError:
  22. st = None
  23. msg = channel.receive()
  24. if isinstance(msg, list):
  25. if st and not stat.S_ISDIR(st.st_mode):
  26. os.unlink(path)
  27. st = None
  28. if not st:
  29. os.makedirs(path)
  30. mode = msg.pop(0)
  31. if mode:
  32. os.chmod(path, mode)
  33. entrynames = {}
  34. for entryname in msg:
  35. destpath = os.path.join(path, entryname)
  36. receive_directory_structure(
  37. destpath, relcomponents + [entryname])
  38. entrynames[entryname] = True
  39. if options.get('delete'):
  40. for othername in os.listdir(path):
  41. if othername not in entrynames:
  42. otherpath = os.path.join(path, othername)
  43. remove(otherpath)
  44. elif msg is not None:
  45. assert isinstance(msg, tuple)
  46. checksum = None
  47. if st:
  48. if stat.S_ISREG(st.st_mode):
  49. msg_mode, msg_mtime, msg_size = msg
  50. if msg_size != st.st_size:
  51. pass
  52. elif msg_mtime != st.st_mtime:
  53. f = open(path, 'rb')
  54. checksum = md5(f.read()).digest()
  55. f.close()
  56. elif msg_mode and msg_mode != st.st_mode:
  57. os.chmod(path, msg_mode)
  58. return
  59. else:
  60. return # already fine
  61. else:
  62. remove(path)
  63. channel.send(("send", (relcomponents, checksum)))
  64. modifiedfiles.append((path, msg))
  65. receive_directory_structure(destdir, [])
  66. STRICT_CHECK = False # seems most useful this way for py.test
  67. channel.send(("list_done", None))
  68. for path, (mode, time, size) in modifiedfiles:
  69. data = channel.receive()
  70. channel.send(("ack", path[len(destdir) + 1:]))
  71. if data is not None:
  72. if STRICT_CHECK and len(data) != size:
  73. raise IOError('file modified during rsync: {!r}'.format(path))
  74. f = open(path, 'wb')
  75. f.write(data)
  76. f.close()
  77. try:
  78. if mode:
  79. os.chmod(path, mode)
  80. os.utime(path, (time, time))
  81. except OSError:
  82. pass
  83. del data
  84. channel.send(("links", None))
  85. msg = channel.receive()
  86. while msg != 42:
  87. # we get symlink
  88. _type, relpath, linkpoint = msg
  89. path = os.path.join(destdir, relpath)
  90. try:
  91. remove(path)
  92. except OSError:
  93. pass
  94. if _type == "linkbase":
  95. src = os.path.join(destdir, linkpoint)
  96. else:
  97. assert _type == "link", _type
  98. src = linkpoint
  99. os.symlink(src, path)
  100. msg = channel.receive()
  101. channel.send(("done", None))
  102. if __name__ == '__channelexec__':
  103. serve_rsync(channel) # noqa