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.

298 lines
6.3 KiB

  1. /*
  2. ISC License
  3. Copyright (c) 2016, 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 <string>
  16. #include <fcntl.h>
  17. #include <sys/stat.h>
  18. #include <sys/time.h>
  19. #ifndef UTIME_NOW
  20. # define UTIME_NOW ((1l << 30) - 1l)
  21. #endif
  22. #ifndef UTIME_OMIT
  23. # define UTIME_OMIT ((1l << 30) - 2l)
  24. #endif
  25. static
  26. inline
  27. bool
  28. _can_call_lutimes(const int dirfd,
  29. const std::string &path,
  30. const int flags)
  31. {
  32. return ((flags == AT_SYMLINK_NOFOLLOW) &&
  33. ((dirfd == AT_FDCWD) ||
  34. (path[0] == '/')));
  35. }
  36. static
  37. inline
  38. bool
  39. _should_ignore(const struct timespec ts[2])
  40. {
  41. return ((ts != NULL) &&
  42. (ts[0].tv_nsec == UTIME_OMIT) &&
  43. (ts[1].tv_nsec == UTIME_OMIT));
  44. }
  45. static
  46. inline
  47. bool
  48. _should_be_set_to_now(const struct timespec ts[2])
  49. {
  50. return ((ts == NULL) ||
  51. ((ts[0].tv_nsec == UTIME_NOW) &&
  52. (ts[1].tv_nsec == UTIME_NOW)));
  53. }
  54. static
  55. inline
  56. bool
  57. _timespec_invalid(const struct timespec &ts)
  58. {
  59. return (((ts.tv_nsec < 0) ||
  60. (ts.tv_nsec > 999999999)) &&
  61. ((ts.tv_nsec != UTIME_NOW) &&
  62. (ts.tv_nsec != UTIME_OMIT)));
  63. }
  64. static
  65. inline
  66. bool
  67. _timespec_invalid(const struct timespec ts[2])
  68. {
  69. return ((ts != NULL) &&
  70. (_timespec_invalid(ts[0]) ||
  71. _timespec_invalid(ts[1])));
  72. }
  73. static
  74. inline
  75. bool
  76. _flags_invalid(const int flags)
  77. {
  78. return ((flags & ~AT_SYMLINK_NOFOLLOW) != 0);
  79. }
  80. static
  81. inline
  82. bool
  83. _any_timespec_is_utime_omit(const struct timespec ts[2])
  84. {
  85. return ((ts[0].tv_nsec == UTIME_OMIT) ||
  86. (ts[1].tv_nsec == UTIME_OMIT));
  87. }
  88. static
  89. inline
  90. bool
  91. _any_timespec_is_utime_now(const struct timespec ts[2])
  92. {
  93. return ((ts[0].tv_nsec == UTIME_NOW) ||
  94. (ts[1].tv_nsec == UTIME_NOW));
  95. }
  96. static
  97. inline
  98. int
  99. _set_utime_omit_to_current_value(const int dirfd,
  100. const std::string &path,
  101. const struct timespec ts[2],
  102. struct timeval tv[2],
  103. const int flags)
  104. {
  105. int rv;
  106. struct stat st;
  107. if(!_any_timespec_is_utime_omit(ts))
  108. return 0;
  109. rv = ::fstatat(dirfd,path.c_str(),&st,flags);
  110. if(rv == -1)
  111. return -1;
  112. if(ts[0].tv_nsec == UTIME_OMIT)
  113. TIMESPEC_TO_TIMEVAL(&tv[0],&st.st_atim);
  114. if(ts[1].tv_nsec == UTIME_OMIT)
  115. TIMESPEC_TO_TIMEVAL(&tv[1],&st.st_mtim);
  116. return 0;
  117. }
  118. static
  119. inline
  120. int
  121. _set_utime_omit_to_current_value(const int fd,
  122. const struct timespec ts[2],
  123. struct timeval tv[2])
  124. {
  125. int rv;
  126. struct stat st;
  127. if(!_any_timespec_is_utime_omit(ts))
  128. return 0;
  129. rv = ::fstat(fd,&st);
  130. if(rv == -1)
  131. return -1;
  132. if(ts[0].tv_nsec == UTIME_OMIT)
  133. TIMESPEC_TO_TIMEVAL(&tv[0],&st.st_atim);
  134. if(ts[1].tv_nsec == UTIME_OMIT)
  135. TIMESPEC_TO_TIMEVAL(&tv[1],&st.st_mtim);
  136. return 0;
  137. }
  138. static
  139. inline
  140. int
  141. _set_utime_now_to_now(const struct timespec ts[2],
  142. struct timeval tv[2])
  143. {
  144. int rv;
  145. struct timeval now;
  146. if(_any_timespec_is_utime_now(ts))
  147. return 0;
  148. rv = ::gettimeofday(&now,NULL);
  149. if(rv == -1)
  150. return -1;
  151. if(ts[0].tv_nsec == UTIME_NOW)
  152. tv[0] = now;
  153. if(ts[1].tv_nsec == UTIME_NOW)
  154. tv[1] = now;
  155. return 0;
  156. }
  157. static
  158. inline
  159. int
  160. _convert_timespec_to_timeval(const int dirfd,
  161. const std::string &path,
  162. const struct timespec ts[2],
  163. struct timeval tv[2],
  164. struct timeval *&tvp,
  165. const int flags)
  166. {
  167. int rv;
  168. if(_should_be_set_to_now(ts))
  169. return (tvp=NULL,0);
  170. TIMESPEC_TO_TIMEVAL(&tv[0],&ts[0]);
  171. TIMESPEC_TO_TIMEVAL(&tv[1],&ts[1]);
  172. rv = _set_utime_omit_to_current_value(dirfd,path,ts,tv,flags);
  173. if(rv == -1)
  174. return -1;
  175. rv = _set_utime_now_to_now(ts,tv);
  176. if(rv == -1)
  177. return -1;
  178. return (tvp=tv,0);
  179. }
  180. static
  181. inline
  182. int
  183. _convert_timespec_to_timeval(const int fd,
  184. const struct timespec ts[2],
  185. struct timeval tv[2],
  186. struct timeval *&tvp)
  187. {
  188. int rv;
  189. if(_should_be_set_to_now(ts))
  190. return (tvp=NULL,0);
  191. TIMESPEC_TO_TIMEVAL(&tv[0],&ts[0]);
  192. TIMESPEC_TO_TIMEVAL(&tv[1],&ts[1]);
  193. rv = _set_utime_omit_to_current_value(fd,ts,tv);
  194. if(rv == -1)
  195. return -1;
  196. rv = _set_utime_now_to_now(ts,tv);
  197. if(rv == -1)
  198. return -1;
  199. return (tvp=tv,0);
  200. }
  201. namespace fs
  202. {
  203. static
  204. inline
  205. int
  206. utime(const int dirfd,
  207. const std::string &path,
  208. const struct timespec ts[2],
  209. const int flags)
  210. {
  211. int rv;
  212. struct timeval tv[2];
  213. struct timeval *tvp;
  214. if(_flags_invalid(flags))
  215. return (errno=EINVAL,-1);
  216. if(_timespec_invalid(ts))
  217. return (errno=EINVAL,-1);
  218. if(_should_ignore(ts))
  219. return 0;
  220. rv = _convert_timespec_to_timeval(dirfd,path,ts,tv,tvp,flags);
  221. if(rv == -1)
  222. return -1;
  223. if((flags & AT_SYMLINK_NOFOLLOW) == 0)
  224. return ::futimesat(dirfd,path.c_str(),tvp);
  225. if(_can_call_lutimes(dirfd,path,flags))
  226. return ::lutimes(path.c_str(),tvp);
  227. return (errno=ENOTSUP,-1);
  228. }
  229. static
  230. inline
  231. int
  232. utime(const int fd,
  233. const struct timespec ts[2])
  234. {
  235. int rv;
  236. struct timeval tv[2];
  237. struct timeval *tvp;
  238. if(_timespec_invalid(ts))
  239. return (errno=EINVAL,-1);
  240. if(_should_ignore(ts))
  241. return 0;
  242. rv = _convert_timespec_to_timeval(fd,ts,tv,tvp);
  243. if(rv == -1)
  244. return -1;
  245. return ::futimes(fd,tvp);
  246. }
  247. }