1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
/* Making new files
Copyright (C) 1992,93,94,96,98,2001 Free Software Foundation
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2, or (at
your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "priv.h"
/* Create a new node. Give it MODE; if that includes IFDIR, also
initialize `.' and `..' in the new directory. Return the node in NPP.
CRED identifies the user responsible for the call. If NAME is nonzero,
then link the new node into DIR with name NAME; DS is the result of a
prior diskfs_lookup for creation (and DIR has been held locked since).
DIR must always be provided as at least a hint for disk allocation
strategies. */
error_t
diskfs_create_node (struct node *dir,
const char *name,
mode_t mode,
struct node **newnode,
struct protid *cred,
struct dirstat *ds)
{
struct node *np;
error_t err;
uid_t newuid;
gid_t newgid;
if (diskfs_check_readonly ())
{
*newnode = NULL;
return EROFS;
}
/* Make the node */
err = diskfs_alloc_node (dir, mode, newnode);
if (err)
{
if (name)
diskfs_drop_dirstat (dir, ds);
*newnode = NULL;
return err;
}
np = *newnode;
/* Initialize the on-disk fields. */
if (cred->user->uids->num)
newuid = cred->user->uids->ids[0];
else
{
newuid = dir->dn_stat.st_uid;
mode &= ~S_ISUID;
}
err = diskfs_validate_owner_change (np, newuid);
if (err)
goto change_err;
np->dn_stat.st_uid = newuid;
if (np->author_tracks_uid)
np->dn_stat.st_author = newuid;
newgid = dir->dn_stat.st_gid;
if (!idvec_contains (cred->user->gids, newgid))
mode &= ~S_ISGID;
err = diskfs_validate_group_change (np, newgid);
if (err)
goto change_err;
np->dn_stat.st_gid = newgid;
np->dn_stat.st_rdev = 0;
np->dn_stat.st_nlink = !!name;
err = diskfs_validate_mode_change (np, mode);
if (err)
goto change_err;
np->dn_stat.st_mode = mode;
np->dn_stat.st_blocks = 0;
np->dn_stat.st_size = 0;
np->dn_stat.st_flags = 0;
np->dn_set_atime = 1;
np->dn_set_mtime = 1;
np->dn_set_ctime = 1;
if (S_ISDIR (mode))
err = diskfs_init_dir (np, dir, cred);
diskfs_node_update (np, 1);
if (err)
{
change_err:
np->dn_stat.st_mode = 0;
np->dn_stat.st_nlink = 0;
if (name)
diskfs_drop_dirstat (dir, ds);
*newnode = NULL;
return err;
}
if (name)
{
err = diskfs_direnter (dir, name, np, ds, cred);
if (err)
{
if (S_ISDIR (mode))
diskfs_clear_directory (np, dir, cred);
np->dn_stat.st_nlink = 0;
np->dn_set_ctime = 1;
diskfs_nput (np);
}
}
if (err)
*newnode = NULL;
return err;
}
|