Blame view

fs/utimes.c 4.85 KB
82b0547cf   Alexey Dobriyan   [PATCH] Create fs...
1
  #include <linux/compiler.h>
1c710c896   Ulrich Drepper   utimensat impleme...
2
  #include <linux/file.h>
82b0547cf   Alexey Dobriyan   [PATCH] Create fs...
3
4
5
  #include <linux/fs.h>
  #include <linux/linkage.h>
  #include <linux/namei.h>
914e26379   Al Viro   [PATCH] severing ...
6
  #include <linux/sched.h>
1c710c896   Ulrich Drepper   utimensat impleme...
7
  #include <linux/stat.h>
82b0547cf   Alexey Dobriyan   [PATCH] Create fs...
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  #include <linux/utime.h>
  #include <asm/uaccess.h>
  #include <asm/unistd.h>
  
  #ifdef __ARCH_WANT_SYS_UTIME
  
  /*
   * sys_utime() can be implemented in user-level using sys_utimes().
   * Is this for backwards compatibility?  If so, why not move it
   * into the appropriate arch directory (for those architectures that
   * need it).
   */
  
  /* If times==NULL, set access and modification to current time,
   * must be owner or have write permission.
   * Else, update from *times, must be owner or super user.
   */
1c710c896   Ulrich Drepper   utimensat impleme...
25
  asmlinkage long sys_utime(char __user *filename, struct utimbuf __user *times)
82b0547cf   Alexey Dobriyan   [PATCH] Create fs...
26
  {
1c710c896   Ulrich Drepper   utimensat impleme...
27
  	struct timespec tv[2];
82b0547cf   Alexey Dobriyan   [PATCH] Create fs...
28

82b0547cf   Alexey Dobriyan   [PATCH] Create fs...
29
  	if (times) {
1c710c896   Ulrich Drepper   utimensat impleme...
30
31
32
33
34
  		if (get_user(tv[0].tv_sec, &times->actime) ||
  		    get_user(tv[1].tv_sec, &times->modtime))
  			return -EFAULT;
  		tv[0].tv_nsec = 0;
  		tv[1].tv_nsec = 0;
82b0547cf   Alexey Dobriyan   [PATCH] Create fs...
35
  	}
1c710c896   Ulrich Drepper   utimensat impleme...
36
  	return do_utimes(AT_FDCWD, filename, times ? tv : NULL, 0);
82b0547cf   Alexey Dobriyan   [PATCH] Create fs...
37
38
39
40
41
42
43
44
  }
  
  #endif
  
  /* If times==NULL, set access and modification to current time,
   * must be owner or have write permission.
   * Else, update from *times, must be owner or super user.
   */
1c710c896   Ulrich Drepper   utimensat impleme...
45
  long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags)
82b0547cf   Alexey Dobriyan   [PATCH] Create fs...
46
47
48
  {
  	int error;
  	struct nameidata nd;
1c710c896   Ulrich Drepper   utimensat impleme...
49
50
  	struct dentry *dentry;
  	struct inode *inode;
82b0547cf   Alexey Dobriyan   [PATCH] Create fs...
51
  	struct iattr newattrs;
1c710c896   Ulrich Drepper   utimensat impleme...
52
  	struct file *f = NULL;
82b0547cf   Alexey Dobriyan   [PATCH] Create fs...
53

1c710c896   Ulrich Drepper   utimensat impleme...
54
55
  	error = -EINVAL;
  	if (flags & ~AT_SYMLINK_NOFOLLOW)
82b0547cf   Alexey Dobriyan   [PATCH] Create fs...
56
  		goto out;
1c710c896   Ulrich Drepper   utimensat impleme...
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
  
  	if (filename == NULL && dfd != AT_FDCWD) {
  		error = -EINVAL;
  		if (flags & AT_SYMLINK_NOFOLLOW)
  			goto out;
  
  		error = -EBADF;
  		f = fget(dfd);
  		if (!f)
  			goto out;
  		dentry = f->f_path.dentry;
  	} else {
  		error = __user_walk_fd(dfd, filename, (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW, &nd);
  		if (error)
  			goto out;
  
  		dentry = nd.dentry;
  	}
  
  	inode = dentry->d_inode;
82b0547cf   Alexey Dobriyan   [PATCH] Create fs...
77
78
79
80
81
82
83
84
85
86
87
  
  	error = -EROFS;
  	if (IS_RDONLY(inode))
  		goto dput_and_out;
  
  	/* Don't worry, the checks are done in inode_change_ok() */
  	newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
  	if (times) {
  		error = -EPERM;
                  if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
                          goto dput_and_out;
1c710c896   Ulrich Drepper   utimensat impleme...
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
  		if (times[0].tv_nsec == UTIME_OMIT)
  			newattrs.ia_valid &= ~ATTR_ATIME;
  		else if (times[0].tv_nsec != UTIME_NOW) {
  			newattrs.ia_atime.tv_sec = times[0].tv_sec;
  			newattrs.ia_atime.tv_nsec = times[0].tv_nsec;
  			newattrs.ia_valid |= ATTR_ATIME_SET;
  		}
  
  		if (times[1].tv_nsec == UTIME_OMIT)
  			newattrs.ia_valid &= ~ATTR_MTIME;
  		else if (times[1].tv_nsec != UTIME_NOW) {
  			newattrs.ia_mtime.tv_sec = times[1].tv_sec;
  			newattrs.ia_mtime.tv_nsec = times[1].tv_nsec;
  			newattrs.ia_valid |= ATTR_MTIME_SET;
  		}
82b0547cf   Alexey Dobriyan   [PATCH] Create fs...
103
104
105
106
  	} else {
  		error = -EACCES;
                  if (IS_IMMUTABLE(inode))
                          goto dput_and_out;
3bd858ab1   Satyam Sharma   Introduce is_owne...
107
  		if (!is_owner_or_cap(inode)) {
1e5de2837   Linus Torvalds   Fix permission ch...
108
109
110
111
112
113
114
115
116
  			if (f) {
  				if (!(f->f_mode & FMODE_WRITE))
  					goto dput_and_out;
  			} else {
  				error = vfs_permission(&nd, MAY_WRITE);
  				if (error)
  					goto dput_and_out;
  			}
  		}
82b0547cf   Alexey Dobriyan   [PATCH] Create fs...
117
118
  	}
  	mutex_lock(&inode->i_mutex);
1c710c896   Ulrich Drepper   utimensat impleme...
119
  	error = notify_change(dentry, &newattrs);
82b0547cf   Alexey Dobriyan   [PATCH] Create fs...
120
121
  	mutex_unlock(&inode->i_mutex);
  dput_and_out:
1c710c896   Ulrich Drepper   utimensat impleme...
122
123
124
125
  	if (f)
  		fput(f);
  	else
  		path_release(&nd);
82b0547cf   Alexey Dobriyan   [PATCH] Create fs...
126
127
128
  out:
  	return error;
  }
1c710c896   Ulrich Drepper   utimensat impleme...
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
  asmlinkage long sys_utimensat(int dfd, char __user *filename, struct timespec __user *utimes, int flags)
  {
  	struct timespec tstimes[2];
  
  	if (utimes) {
  		if (copy_from_user(&tstimes, utimes, sizeof(tstimes)))
  			return -EFAULT;
  		if ((tstimes[0].tv_nsec == UTIME_OMIT ||
  		     tstimes[0].tv_nsec == UTIME_NOW) &&
  		    tstimes[0].tv_sec != 0)
  			return -EINVAL;
  		if ((tstimes[1].tv_nsec == UTIME_OMIT ||
  		     tstimes[1].tv_nsec == UTIME_NOW) &&
  		    tstimes[1].tv_sec != 0)
  			return -EINVAL;
  
  		/* Nothing to do, we must not even check the path.  */
  		if (tstimes[0].tv_nsec == UTIME_OMIT &&
  		    tstimes[1].tv_nsec == UTIME_OMIT)
  			return 0;
  	}
  
  	return do_utimes(dfd, filename, utimes ? tstimes : NULL, flags);
  }
82b0547cf   Alexey Dobriyan   [PATCH] Create fs...
153
154
155
  asmlinkage long sys_futimesat(int dfd, char __user *filename, struct timeval __user *utimes)
  {
  	struct timeval times[2];
1c710c896   Ulrich Drepper   utimensat impleme...
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
  	struct timespec tstimes[2];
  
  	if (utimes) {
  		if (copy_from_user(&times, utimes, sizeof(times)))
  			return -EFAULT;
  
  		/* This test is needed to catch all invalid values.  If we
  		   would test only in do_utimes we would miss those invalid
  		   values truncated by the multiplication with 1000.  Note
  		   that we also catch UTIME_{NOW,OMIT} here which are only
  		   valid for utimensat.  */
  		if (times[0].tv_usec >= 1000000 || times[0].tv_usec < 0 ||
  		    times[1].tv_usec >= 1000000 || times[1].tv_usec < 0)
  			return -EINVAL;
  
  		tstimes[0].tv_sec = times[0].tv_sec;
  		tstimes[0].tv_nsec = 1000 * times[0].tv_usec;
  		tstimes[1].tv_sec = times[1].tv_sec;
  		tstimes[1].tv_nsec = 1000 * times[1].tv_usec;
  	}
82b0547cf   Alexey Dobriyan   [PATCH] Create fs...
176

1c710c896   Ulrich Drepper   utimensat impleme...
177
  	return do_utimes(dfd, filename, utimes ? tstimes : NULL, 0);
82b0547cf   Alexey Dobriyan   [PATCH] Create fs...
178
179
180
181
182
183
  }
  
  asmlinkage long sys_utimes(char __user *filename, struct timeval __user *utimes)
  {
  	return sys_futimesat(AT_FDCWD, filename, utimes);
  }