Add a more explaining comment
[kai/samba-autobuild/.git] / source3 / smbd / dosmode.c
1 /* 
2    Unix SMB/CIFS implementation.
3    dos mode handling functions
4    Copyright (C) Andrew Tridgell 1992-1998
5    Copyright (C) James Peach 2006
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22
23 static int set_sparse_flag(const SMB_STRUCT_STAT * const sbuf)
24 {
25 #if defined (HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE)
26         if (sbuf->st_size > sbuf->st_blocks * (SMB_OFF_T)STAT_ST_BLOCKSIZE) {
27                 return FILE_ATTRIBUTE_SPARSE;
28         }
29 #endif
30         return 0;
31 }
32
33 /****************************************************************************
34  Change a dos mode to a unix mode.
35     Base permission for files:
36          if creating file and inheriting (i.e. parent_dir != NULL)
37            apply read/write bits from parent directory.
38          else   
39            everybody gets read bit set
40          dos readonly is represented in unix by removing everyone's write bit
41          dos archive is represented in unix by the user's execute bit
42          dos system is represented in unix by the group's execute bit
43          dos hidden is represented in unix by the other's execute bit
44          if !inheriting {
45            Then apply create mask,
46            then add force bits.
47          }
48     Base permission for directories:
49          dos directory is represented in unix by unix's dir bit and the exec bit
50          if !inheriting {
51            Then apply create mask,
52            then add force bits.
53          }
54 ****************************************************************************/
55
56 mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname,
57                  const char *inherit_from_dir)
58 {
59         mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
60         mode_t dir_mode = 0; /* Mode of the inherit_from directory if
61                               * inheriting. */
62
63         if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
64                 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
65         }
66
67         if (fname && (inherit_from_dir != NULL)
68             && lp_inherit_perms(SNUM(conn))) {
69                 SMB_STRUCT_STAT sbuf;
70
71                 DEBUG(2, ("unix_mode(%s) inheriting from %s\n", fname,
72                           inherit_from_dir));
73                 if (SMB_VFS_STAT(conn, inherit_from_dir, &sbuf) != 0) {
74                         DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n", fname,
75                                  inherit_from_dir, strerror(errno)));
76                         return(0);      /* *** shouldn't happen! *** */
77                 }
78
79                 /* Save for later - but explicitly remove setuid bit for safety. */
80                 dir_mode = sbuf.st_mode & ~S_ISUID;
81                 DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode));
82                 /* Clear "result" */
83                 result = 0;
84         } 
85
86         if (IS_DOS_DIR(dosmode)) {
87                 /* We never make directories read only for the owner as under DOS a user
88                 can always create a file in a read-only directory. */
89                 result |= (S_IFDIR | S_IWUSR);
90
91                 if (dir_mode) {
92                         /* Inherit mode of parent directory. */
93                         result |= dir_mode;
94                 } else {
95                         /* Provisionally add all 'x' bits */
96                         result |= (S_IXUSR | S_IXGRP | S_IXOTH);                 
97
98                         /* Apply directory mask */
99                         result &= lp_dir_mask(SNUM(conn));
100                         /* Add in force bits */
101                         result |= lp_force_dir_mode(SNUM(conn));
102                 }
103         } else { 
104                 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
105                         result |= S_IXUSR;
106
107                 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
108                         result |= S_IXGRP;
109  
110                 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
111                         result |= S_IXOTH;  
112
113                 if (dir_mode) {
114                         /* Inherit 666 component of parent directory mode */
115                         result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
116                 } else {
117                         /* Apply mode mask */
118                         result &= lp_create_mask(SNUM(conn));
119                         /* Add in force bits */
120                         result |= lp_force_create_mode(SNUM(conn));
121                 }
122         }
123
124         DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result ));
125         return(result);
126 }
127
128 /****************************************************************************
129  Change a unix mode to a dos mode.
130 ****************************************************************************/
131
132 static uint32 dos_mode_from_sbuf(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
133 {
134         int result = 0;
135         enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
136
137         if (ro_opts == MAP_READONLY_YES) {
138                 /* Original Samba method - map inverse of user "w" bit. */
139                 if ((sbuf->st_mode & S_IWUSR) == 0) {
140                         result |= aRONLY;
141                 }
142         } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
143                 /* Check actual permissions for read-only. */
144                 if (!can_write_to_file(conn, path, sbuf)) {
145                         result |= aRONLY;
146                 }
147         } /* Else never set the readonly bit. */
148
149         if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
150                 result |= aARCH;
151
152         if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
153                 result |= aSYSTEM;
154         
155         if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
156                 result |= aHIDDEN;   
157   
158         if (S_ISDIR(sbuf->st_mode))
159                 result = aDIR | (result & aRONLY);
160
161         result |= set_sparse_flag(sbuf);
162  
163 #ifdef S_ISLNK
164 #if LINKS_READ_ONLY
165         if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
166                 result |= aRONLY;
167 #endif
168 #endif
169
170         DEBUG(8,("dos_mode_from_sbuf returning "));
171
172         if (result & aHIDDEN) DEBUG(8, ("h"));
173         if (result & aRONLY ) DEBUG(8, ("r"));
174         if (result & aSYSTEM) DEBUG(8, ("s"));
175         if (result & aDIR   ) DEBUG(8, ("d"));
176         if (result & aARCH  ) DEBUG(8, ("a"));
177         
178         DEBUG(8,("\n"));
179         return result;
180 }
181
182 /****************************************************************************
183  Get DOS attributes from an EA.
184 ****************************************************************************/
185
186 static bool get_ea_dos_attribute(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf, uint32 *pattr)
187 {
188         ssize_t sizeret;
189         fstring attrstr;
190         unsigned int dosattr;
191
192         if (!lp_store_dos_attributes(SNUM(conn))) {
193                 return False;
194         }
195
196         /* Don't reset pattr to zero as we may already have filename-based attributes we
197            need to preserve. */
198
199         sizeret = SMB_VFS_GETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr));
200         if (sizeret == -1) {
201 #if defined(ENOTSUP) && defined(ENOATTR)
202                 if ((errno != ENOTSUP) && (errno != ENOATTR) && (errno != EACCES) && (errno != EPERM)) {
203                         DEBUG(1,("get_ea_dos_attributes: Cannot get attribute from EA on file %s: Error = %s\n",
204                                 path, strerror(errno) ));
205                         set_store_dos_attributes(SNUM(conn), False);
206                 }
207 #endif
208                 return False;
209         }
210         /* Null terminate string. */
211         attrstr[sizeret] = 0;
212         DEBUG(10,("get_ea_dos_attribute: %s attrstr = %s\n", path, attrstr));
213
214         if (sizeret < 2 || attrstr[0] != '0' || attrstr[1] != 'x' ||
215                         sscanf(attrstr, "%x", &dosattr) != 1) {
216                 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on file %s - %s\n", path, attrstr));
217                 return False;
218         }
219
220         if (S_ISDIR(sbuf->st_mode)) {
221                 dosattr |= aDIR;
222         }
223         *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
224
225         DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
226
227         if (dosattr & aHIDDEN) DEBUG(8, ("h"));
228         if (dosattr & aRONLY ) DEBUG(8, ("r"));
229         if (dosattr & aSYSTEM) DEBUG(8, ("s"));
230         if (dosattr & aDIR   ) DEBUG(8, ("d"));
231         if (dosattr & aARCH  ) DEBUG(8, ("a"));
232         
233         DEBUG(8,("\n"));
234
235         return True;
236 }
237
238 /****************************************************************************
239  Set DOS attributes in an EA.
240 ****************************************************************************/
241
242 static bool set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode)
243 {
244         fstring attrstr;
245         files_struct *fsp = NULL;
246         bool ret = False;
247
248         if (!lp_store_dos_attributes(SNUM(conn))) {
249                 return False;
250         }
251
252         snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK);
253         if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) {
254                 if((errno != EPERM) && (errno != EACCES)) {
255                         if (errno == ENOSYS
256 #if defined(ENOTSUP)
257                                 || errno == ENOTSUP) {
258 #else
259                                 ) {
260 #endif
261                                 set_store_dos_attributes(SNUM(conn), False);
262                         }
263                         return False;
264                 }
265
266                 /* We want DOS semantics, ie allow non owner with write permission to change the
267                         bits on a file. Just like file_ntimes below.
268                 */
269
270                 /* Check if we have write access. */
271                 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
272                         return False;
273
274                 /*
275                  * We need to open the file with write access whilst
276                  * still in our current user context. This ensures we
277                  * are not violating security in doing the setxattr.
278                  */
279
280                 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,path,sbuf,&fsp)))
281                         return ret;
282                 become_root();
283                 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
284                         ret = True;
285                 }
286                 unbecome_root();
287                 close_file_fchmod(fsp);
288                 return ret;
289         }
290         DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
291         return True;
292 }
293
294 /****************************************************************************
295  Change a unix mode to a dos mode for an ms dfs link.
296 ****************************************************************************/
297
298 uint32 dos_mode_msdfs(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
299 {
300         uint32 result = 0;
301
302         DEBUG(8,("dos_mode_msdfs: %s\n", path));
303
304         if (!VALID_STAT(*sbuf)) {
305                 return 0;
306         }
307
308         /* First do any modifications that depend on the path name. */
309         /* hide files with a name starting with a . */
310         if (lp_hide_dot_files(SNUM(conn))) {
311                 const char *p = strrchr_m(path,'/');
312                 if (p) {
313                         p++;
314                 } else {
315                         p = path;
316                 }
317                 
318                 if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
319                         result |= aHIDDEN;
320                 }
321         }
322         
323         result |= dos_mode_from_sbuf(conn, path, sbuf);
324
325         /* Optimization : Only call is_hidden_path if it's not already
326            hidden. */
327         if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
328                 result |= aHIDDEN;
329         }
330
331         DEBUG(8,("dos_mode_msdfs returning "));
332
333         if (result & aHIDDEN) DEBUG(8, ("h"));
334         if (result & aRONLY ) DEBUG(8, ("r"));
335         if (result & aSYSTEM) DEBUG(8, ("s"));
336         if (result & aDIR   ) DEBUG(8, ("d"));
337         if (result & aARCH  ) DEBUG(8, ("a"));
338         if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
339         
340         DEBUG(8,("\n"));
341
342         return(result);
343 }
344
345 /****************************************************************************
346  Change a unix mode to a dos mode.
347 ****************************************************************************/
348
349 uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
350 {
351         uint32 result = 0;
352         bool offline;
353
354         DEBUG(8,("dos_mode: %s\n", path));
355
356         if (!VALID_STAT(*sbuf)) {
357                 return 0;
358         }
359
360         /* First do any modifications that depend on the path name. */
361         /* hide files with a name starting with a . */
362         if (lp_hide_dot_files(SNUM(conn))) {
363                 const char *p = strrchr_m(path,'/');
364                 if (p) {
365                         p++;
366                 } else {
367                         p = path;
368                 }
369                 
370                 if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
371                         result |= aHIDDEN;
372                 }
373         }
374         
375         /* Get the DOS attributes from an EA by preference. */
376         if (get_ea_dos_attribute(conn, path, sbuf, &result)) {
377                 result |= set_sparse_flag(sbuf);
378         } else {
379                 result |= dos_mode_from_sbuf(conn, path, sbuf);
380         }
381
382         
383         offline = SMB_VFS_IS_OFFLINE(conn, path, sbuf);
384         if (S_ISREG(sbuf->st_mode) && offline) {
385                 result |= FILE_ATTRIBUTE_OFFLINE;
386         }
387
388         /* Optimization : Only call is_hidden_path if it's not already
389            hidden. */
390         if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
391                 result |= aHIDDEN;
392         }
393
394         DEBUG(8,("dos_mode returning "));
395
396         if (result & aHIDDEN) DEBUG(8, ("h"));
397         if (result & aRONLY ) DEBUG(8, ("r"));
398         if (result & aSYSTEM) DEBUG(8, ("s"));
399         if (result & aDIR   ) DEBUG(8, ("d"));
400         if (result & aARCH  ) DEBUG(8, ("a"));
401         if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
402         
403         DEBUG(8,("\n"));
404
405         return(result);
406 }
407
408 /*******************************************************************
409  chmod a file - but preserve some bits.
410 ********************************************************************/
411
412 int file_set_dosmode(connection_struct *conn, const char *fname,
413                      uint32 dosmode, SMB_STRUCT_STAT *st,
414                      const char *parent_dir,
415                      bool newfile)
416 {
417         SMB_STRUCT_STAT st1;
418         int mask=0;
419         mode_t tmp;
420         mode_t unixmode;
421         int ret = -1, lret = -1;
422         uint32_t old_mode;
423
424         /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
425         dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
426
427         DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));
428
429         if (st == NULL) {
430                 SET_STAT_INVALID(st1);
431                 st = &st1;
432         }
433
434         if (!VALID_STAT(*st)) {
435                 if (SMB_VFS_STAT(conn,fname,st))
436                         return(-1);
437         }
438
439         unixmode = st->st_mode;
440
441         get_acl_group_bits(conn, fname, &st->st_mode);
442
443         if (S_ISDIR(st->st_mode))
444                 dosmode |= aDIR;
445         else
446                 dosmode &= ~aDIR;
447
448         old_mode = dos_mode(conn,fname,st);
449         
450         if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
451                 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
452                         lret = SMB_VFS_SET_OFFLINE(conn, fname);
453                         if (lret == -1) {
454                                 DEBUG(0, ("set_dos_mode: client has asked to set "
455                                           "FILE_ATTRIBUTE_OFFLINE to %s/%s but there was "
456                                           "an error while setting it or it is not supported.\n",
457                                           parent_dir, fname));
458                         }
459                 }
460         }
461
462         dosmode  &= ~FILE_ATTRIBUTE_OFFLINE;
463         old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
464
465         if (old_mode == dosmode) {
466                 st->st_mode = unixmode;
467                 return(0);
468         }
469
470         /* Store the DOS attributes in an EA by preference. */
471         if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
472                 if (!newfile) {
473                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
474                                 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
475                 }
476                 st->st_mode = unixmode;
477                 return 0;
478         }
479
480         unixmode = unix_mode(conn,dosmode,fname, parent_dir);
481
482         /* preserve the s bits */
483         mask |= (S_ISUID | S_ISGID);
484
485         /* preserve the t bit */
486 #ifdef S_ISVTX
487         mask |= S_ISVTX;
488 #endif
489
490         /* possibly preserve the x bits */
491         if (!MAP_ARCHIVE(conn))
492                 mask |= S_IXUSR;
493         if (!MAP_SYSTEM(conn))
494                 mask |= S_IXGRP;
495         if (!MAP_HIDDEN(conn))
496                 mask |= S_IXOTH;
497
498         unixmode |= (st->st_mode & mask);
499
500         /* if we previously had any r bits set then leave them alone */
501         if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
502                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
503                 unixmode |= tmp;
504         }
505
506         /* if we previously had any w bits set then leave them alone 
507                 whilst adding in the new w bits, if the new mode is not rdonly */
508         if (!IS_DOS_READONLY(dosmode)) {
509                 unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
510         }
511
512         ret = SMB_VFS_CHMOD(conn, fname, unixmode);
513         if (ret == 0) {
514                 if(!newfile || (lret != -1)) {
515                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
516                                      FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
517                 }
518                 st->st_mode = unixmode;
519                 return 0;
520         }
521
522         if((errno != EPERM) && (errno != EACCES))
523                 return -1;
524
525         if(!lp_dos_filemode(SNUM(conn)))
526                 return -1;
527
528         /* We want DOS semantics, ie allow non owner with write permission to change the
529                 bits on a file. Just like file_ntimes below.
530         */
531
532         /* Check if we have write access. */
533         if (CAN_WRITE(conn)) {
534                 /*
535                  * We need to open the file with write access whilst
536                  * still in our current user context. This ensures we
537                  * are not violating security in doing the fchmod.
538                  * This file open does *not* break any oplocks we are
539                  * holding. We need to review this.... may need to
540                  * break batch oplocks open by others. JRA.
541                  */
542                 files_struct *fsp;
543                 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,fname,st,&fsp)))
544                         return -1;
545                 become_root();
546                 ret = SMB_VFS_FCHMOD(fsp, unixmode);
547                 unbecome_root();
548                 close_file_fchmod(fsp);
549                 if (!newfile) {
550                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
551                                 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
552                 }
553                 if (ret == 0) {
554                         st->st_mode = unixmode;
555                 }
556         }
557
558         return( ret );
559 }
560
561 /*******************************************************************
562  Wrapper around the VFS ntimes that possibly allows DOS semantics rather
563  than POSIX.
564 *******************************************************************/
565
566 int file_ntimes(connection_struct *conn, const char *fname, const struct timespec ts[2])
567 {
568         SMB_STRUCT_STAT sbuf;
569         int ret = -1;
570
571         errno = 0;
572         ZERO_STRUCT(sbuf);
573
574         DEBUG(6, ("file_ntime: actime: %s",
575                   time_to_asc(convert_timespec_to_time_t(ts[0]))));
576         DEBUG(6, ("file_ntime: modtime: %s",
577                   time_to_asc(convert_timespec_to_time_t(ts[1]))));
578
579         /* Don't update the time on read-only shares */
580         /* We need this as set_filetime (which can be called on
581            close and other paths) can end up calling this function
582            without the NEED_WRITE protection. Found by : 
583            Leo Weppelman <leo@wau.mis.ah.nl>
584         */
585
586         if (!CAN_WRITE(conn)) {
587                 return 0;
588         }
589
590         if(SMB_VFS_NTIMES(conn, fname, ts) == 0) {
591                 return 0;
592         }
593
594         if((errno != EPERM) && (errno != EACCES)) {
595                 return -1;
596         }
597
598         if(!lp_dos_filetimes(SNUM(conn))) {
599                 return -1;
600         }
601
602         /* We have permission (given by the Samba admin) to
603            break POSIX semantics and allow a user to change
604            the time on a file they don't own but can write to
605            (as DOS does).
606          */
607
608         /* Check if we have write access. */
609         if (can_write_to_file(conn, fname, &sbuf)) {
610                 /* We are allowed to become root and change the filetime. */
611                 become_root();
612                 ret = SMB_VFS_NTIMES(conn, fname, ts);
613                 unbecome_root();
614         }
615
616         return ret;
617 }
618
619 /******************************************************************
620  Force a "sticky" write time on a pathname. This will always be
621  returned on all future write time queries and set on close.
622 ******************************************************************/
623
624 bool set_sticky_write_time_path(connection_struct *conn, const char *fname,
625                          struct file_id fileid, const struct timespec mtime)
626 {
627         if (null_timespec(mtime)) {
628                 return true;
629         }
630
631         if (!set_sticky_write_time(fileid, mtime)) {
632                 return false;
633         }
634
635         return true;
636 }
637
638 /******************************************************************
639  Force a "sticky" write time on an fsp. This will always be
640  returned on all future write time queries and set on close.
641 ******************************************************************/
642
643 bool set_sticky_write_time_fsp(struct files_struct *fsp, const struct timespec mtime)
644 {
645         fsp->write_time_forced = true;
646         TALLOC_FREE(fsp->update_write_time_event);
647
648         return set_sticky_write_time_path(fsp->conn, fsp->fsp_name,
649                         fsp->file_id, mtime);
650 }
651
652 /******************************************************************
653  Update a write time immediately, without the 2 second delay.
654 ******************************************************************/
655
656 bool update_write_time(struct files_struct *fsp)
657 {
658         if (!set_write_time(fsp->file_id, timespec_current())) {
659                 return false;
660         }
661
662         notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
663                         FILE_NOTIFY_CHANGE_LAST_WRITE, fsp->fsp_name);
664
665         return true;
666 }