You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

284 lines
6.3 KiB

  1. /*
  2. ISC License
  3. Copyright (c) 2020, Antonio SJ Musumeci <trapexit@spawn.link>
  4. Permission to use, copy, modify, and/or distribute this software for any
  5. purpose with or without fee is hereby granted, provided that the above
  6. copyright notice and this permission notice appear in all copies.
  7. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8. WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  10. ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  12. ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  13. OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  14. */
  15. #include "fs_futimesat.hpp"
  16. #include "fs_stat_utils.hpp"
  17. #include <string>
  18. #include <fcntl.h>
  19. #include <sys/stat.h>
  20. #include <sys/time.h>
  21. #ifndef UTIME_NOW
  22. # define UTIME_NOW ((1l << 30) - 1l)
  23. #endif
  24. #ifndef UTIME_OMIT
  25. # define UTIME_OMIT ((1l << 30) - 2l)
  26. #endif
  27. namespace l
  28. {
  29. static
  30. inline
  31. bool
  32. can_call_lutimes(const int dirfd_,
  33. const std::string &path_,
  34. const int flags_)
  35. {
  36. return ((flags_ == AT_SYMLINK_NOFOLLOW) &&
  37. ((dirfd_ == AT_FDCWD) ||
  38. (path_[0] == '/')));
  39. }
  40. static
  41. inline
  42. bool
  43. should_ignore(const struct timespec ts_[2])
  44. {
  45. return ((ts_ != NULL) &&
  46. (ts_[0].tv_nsec == UTIME_OMIT) &&
  47. (ts_[1].tv_nsec == UTIME_OMIT));
  48. }
  49. static
  50. inline
  51. bool
  52. should_be_set_to_now(const struct timespec ts_[2])
  53. {
  54. return ((ts_ == NULL) ||
  55. ((ts_[0].tv_nsec == UTIME_NOW) &&
  56. (ts_[1].tv_nsec == UTIME_NOW)));
  57. }
  58. static
  59. inline
  60. bool
  61. timespec_invalid(const struct timespec &ts_)
  62. {
  63. return (((ts_.tv_nsec < 0) ||
  64. (ts_.tv_nsec > 999999999)) &&
  65. ((ts_.tv_nsec != UTIME_NOW) &&
  66. (ts_.tv_nsec != UTIME_OMIT)));
  67. }
  68. static
  69. inline
  70. bool
  71. timespec_invalid(const struct timespec ts_[2])
  72. {
  73. return ((ts_ != NULL) &&
  74. (l::timespec_invalid(ts_[0]) ||
  75. l::timespec_invalid(ts_[1])));
  76. }
  77. static
  78. inline
  79. bool
  80. flags_invalid(const int flags_)
  81. {
  82. return ((flags_ & ~AT_SYMLINK_NOFOLLOW) != 0);
  83. }
  84. static
  85. inline
  86. bool
  87. any_timespec_is_utime_omit(const struct timespec ts_[2])
  88. {
  89. return ((ts_[0].tv_nsec == UTIME_OMIT) ||
  90. (ts_[1].tv_nsec == UTIME_OMIT));
  91. }
  92. static
  93. inline
  94. bool
  95. any_timespec_is_utime_now(const struct timespec ts_[2])
  96. {
  97. return ((ts_[0].tv_nsec == UTIME_NOW) ||
  98. (ts_[1].tv_nsec == UTIME_NOW));
  99. }
  100. static
  101. inline
  102. int
  103. set_utime_omit_to_current_value(const int dirfd_,
  104. const std::string &path_,
  105. const struct timespec ts_[2],
  106. struct timeval tv_[2],
  107. const int flags_)
  108. {
  109. int rv;
  110. struct stat st;
  111. timespec *atime;
  112. timespec *mtime;
  113. if(!l::any_timespec_is_utime_omit(ts_))
  114. return 0;
  115. rv = fs::fstatat(dirfd_,path_,&st,flags_);
  116. if(rv == -1)
  117. return -1;
  118. atime = fs::stat_atime(st);
  119. mtime = fs::stat_mtime(st);
  120. if(ts_[0].tv_nsec == UTIME_OMIT)
  121. TIMESPEC_TO_TIMEVAL(&tv[0],atime);
  122. if(ts_[1].tv_nsec == UTIME_OMIT)
  123. TIMESPEC_TO_TIMEVAL(&tv[1],mtime);
  124. return 0;
  125. }
  126. static
  127. inline
  128. int
  129. set_utime_omit_to_current_value(const int fd_,
  130. const struct timespec ts_[2],
  131. struct timeval tv_[2])
  132. {
  133. int rv;
  134. struct stat st;
  135. timespec *atime;
  136. timespec *mtime;
  137. if(!l::any_timespec_is_utime_omit(ts_))
  138. return 0;
  139. rv = fs::fstat(fd_,&st);
  140. if(rv == -1)
  141. return -1;
  142. atime = fs::stat_atime(st);
  143. mtime = fs::stat_mtime(st);
  144. if(ts_[0].tv_nsec == UTIME_OMIT)
  145. TIMESPEC_TO_TIMEVAL(&tv_[0],atime);
  146. if(ts_[1].tv_nsec == UTIME_OMIT)
  147. TIMESPEC_TO_TIMEVAL(&tv_[1],mtime);
  148. return 0;
  149. }
  150. static
  151. inline
  152. int
  153. set_utime_now_to_now(const struct timespec ts_[2],
  154. struct timeval tv_[2])
  155. {
  156. int rv;
  157. struct timeval now;
  158. if(l::any_timespec_is_utime_now(ts_))
  159. return 0;
  160. rv = time::gettimeofday(&now,NULL);
  161. if(rv == -1)
  162. return -1;
  163. if(ts_[0].tv_nsec == UTIME_NOW)
  164. tv_[0] = now;
  165. if(ts_[1].tv_nsec == UTIME_NOW)
  166. tv_[1] = now;
  167. return 0;
  168. }
  169. static
  170. inline
  171. int
  172. convert_timespec_to_timeval(const int dirfd_,
  173. const std::string &path_,
  174. const struct timespec ts_[2],
  175. struct timeval tv_[2],
  176. struct timeval **tvp_,
  177. const int flags_)
  178. {
  179. int rv;
  180. if(l::should_be_set_to_now(ts_))
  181. return (tvp=NULL,0);
  182. TIMESPEC_TO_TIMEVAL(&tv_[0],&ts_[0]);
  183. TIMESPEC_TO_TIMEVAL(&tv_[1],&ts_[1]);
  184. rv = l::set_utime_omit_to_current_value(dirfd_,path_,ts_,tv_,flags_);
  185. if(rv == -1)
  186. return -1;
  187. rv = l::set_utime_now_to_now(ts_,tv_);
  188. if(rv == -1)
  189. return -1;
  190. return (*tvp_=tv_,0);
  191. }
  192. static
  193. inline
  194. int
  195. convert_timespec_to_timeval(const int fd_,
  196. const struct timespec ts_[2],
  197. struct timeval tv_[2],
  198. struct timeval **tvp_)
  199. {
  200. int rv;
  201. if(l::should_be_set_to_now(ts_))
  202. return (*tvp=NULL,0);
  203. TIMESPEC_TO_TIMEVAL(&tv_[0],&ts_[0]);
  204. TIMESPEC_TO_TIMEVAL(&tv_[1],&ts_[1]);
  205. rv = l::set_utime_omit_to_current_value(fd_,ts_,tv_);
  206. if(rv == -1)
  207. return -1;
  208. rv = l::set_utime_now_to_now(ts_,tv_);
  209. if(rv == -1)
  210. return -1;
  211. return (*tvp=tv,0);
  212. }
  213. }
  214. namespace fs
  215. {
  216. static
  217. inline
  218. int
  219. futimens(const int fd_,
  220. const struct timespec ts_[2])
  221. {
  222. int rv;
  223. struct timeval tv[2];
  224. struct timeval *tvp;
  225. if(l::timespec_invalid(ts_))
  226. return (errno=EINVAL,-1);
  227. if(l::should_ignore(ts_))
  228. return 0;
  229. rv = l::convert_timespec_to_timeval(fd_,ts_,tv,&tvp);
  230. if(rv == -1)
  231. return -1;
  232. return ::futimes(fd_,tvp);
  233. }
  234. }