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.

316 lines
6.6 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. #ifndef __FS_BASE_UTIME_GENERIC_HPP__
  16. #define __FS_BASE_UTIME_GENERIC_HPP__
  17. #include <string>
  18. #include <fcntl.h>
  19. #include <sys/stat.h>
  20. #include <sys/time.h>
  21. #include "fs_base_futimesat.hpp"
  22. #include "fs_base_stat.hpp"
  23. #ifndef UTIME_NOW
  24. # define UTIME_NOW ((1l << 30) - 1l)
  25. #endif
  26. #ifndef UTIME_OMIT
  27. # define UTIME_OMIT ((1l << 30) - 2l)
  28. #endif
  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. (_timespec_invalid(ts[0]) ||
  75. _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(!_any_timespec_is_utime_omit(ts))
  114. return 0;
  115. rv = ::fstatat(dirfd,path.c_str(),&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(!_any_timespec_is_utime_omit(ts))
  138. return 0;
  139. rv = ::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(_any_timespec_is_utime_now(ts))
  159. return 0;
  160. rv = ::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(_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 = _set_utime_omit_to_current_value(dirfd,path,ts,tv,flags);
  185. if(rv == -1)
  186. return -1;
  187. rv = _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(_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 = _set_utime_omit_to_current_value(fd,ts,tv);
  206. if(rv == -1)
  207. return -1;
  208. rv = _set_utime_now_to_now(ts,tv);
  209. if(rv == -1)
  210. return -1;
  211. return (tvp=tv,0);
  212. }
  213. namespace fs
  214. {
  215. static
  216. inline
  217. int
  218. utime(const int dirfd,
  219. const std::string &path,
  220. const struct timespec ts[2],
  221. const int flags)
  222. {
  223. int rv;
  224. struct timeval tv[2];
  225. struct timeval *tvp;
  226. if(_flags_invalid(flags))
  227. return (errno=EINVAL,-1);
  228. if(_timespec_invalid(ts))
  229. return (errno=EINVAL,-1);
  230. if(_should_ignore(ts))
  231. return 0;
  232. rv = _convert_timespec_to_timeval(dirfd,path,ts,tv,tvp,flags);
  233. if(rv == -1)
  234. return -1;
  235. if((flags & AT_SYMLINK_NOFOLLOW) == 0)
  236. return fs::futimesat(dirfd,path.c_str(),tvp);
  237. if(_can_call_lutimes(dirfd,path,flags))
  238. return ::lutimes(path.c_str(),tvp);
  239. return (errno=ENOTSUP,-1);
  240. }
  241. static
  242. inline
  243. int
  244. utime(const int fd,
  245. const struct timespec ts[2])
  246. {
  247. int rv;
  248. struct timeval tv[2];
  249. struct timeval *tvp;
  250. if(_timespec_invalid(ts))
  251. return (errno=EINVAL,-1);
  252. if(_should_ignore(ts))
  253. return 0;
  254. rv = _convert_timespec_to_timeval(fd,ts,tv,tvp);
  255. if(rv == -1)
  256. return -1;
  257. return ::futimes(fd,tvp);
  258. }
  259. }
  260. #endif