2 * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
3 * Copyright (C) 2004-2006 Kay Sievers <kay.sievers@vrfy.org>
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation version 2 of the License.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
30 #include <sys/types.h>
33 #include "udev_rules.h"
34 #include "udev_selinux.h"
36 #define TMP_FILE_EXT ".udev-tmp"
38 int udev_node_mknod(struct udevice
*udev
, const char *file
, dev_t devt
, mode_t mode
, uid_t uid
, gid_t gid
)
40 char file_tmp
[PATH_SIZE
+ sizeof(TMP_FILE_EXT
)];
44 if (major(devt
) != 0 && strcmp(udev
->dev
->subsystem
, "block") == 0)
49 if (lstat(file
, &stats
) == 0) {
50 if ((stats
.st_mode
& S_IFMT
) == (mode
& S_IFMT
) && (stats
.st_rdev
== devt
)) {
51 info("preserve file '%s', because it has correct dev_t", file
);
52 selinux_setfilecon(file
, udev
->dev
->kernel
, stats
.st_mode
);
56 selinux_setfscreatecon(file
, udev
->dev
->kernel
, mode
);
57 retval
= mknod(file
, mode
, devt
);
58 selinux_resetfscreatecon();
63 info("atomically replace '%s'", file
);
64 strlcpy(file_tmp
, file
, sizeof(file_tmp
));
65 strlcat(file_tmp
, TMP_FILE_EXT
, sizeof(file_tmp
));
66 selinux_setfscreatecon(file_tmp
, udev
->dev
->kernel
, mode
);
67 retval
= mknod(file_tmp
, mode
, devt
);
68 selinux_resetfscreatecon();
70 err("mknod(%s, %#o, %u, %u) failed: %s",
71 file_tmp
, mode
, major(devt
), minor(devt
), strerror(errno
));
74 retval
= rename(file_tmp
, file
);
76 err("rename(%s, %s) failed: %s",
77 file_tmp
, file
, strerror(errno
));
83 dbg("chmod(%s, %#o)", file
, mode
);
84 if (chmod(file
, mode
) != 0) {
85 err("chmod(%s, %#o) failed: %s", file
, mode
, strerror(errno
));
89 if (uid
!= 0 || gid
!= 0) {
90 dbg("chown(%s, %u, %u)", file
, uid
, gid
);
91 if (chown(file
, uid
, gid
) != 0) {
92 err("chown(%s, %u, %u) failed: %s",
93 file
, uid
, gid
, strerror(errno
));
101 static int node_symlink(const char *node
, const char *slink
)
104 char target
[PATH_SIZE
] = "";
105 char slink_tmp
[PATH_SIZE
+ sizeof(TMP_FILE_EXT
)];
111 /* use relative link */
112 while (node
[i
] && (node
[i
] == slink
[i
])) {
117 while (slink
[i
] != '\0') {
119 strlcat(target
, "../", sizeof(target
));
122 strlcat(target
, &node
[tail
], sizeof(target
));
124 /* preserve link with correct target, do not replace node of other device */
125 if (lstat(slink
, &stats
) == 0) {
126 if (S_ISBLK(stats
.st_mode
) || S_ISCHR(stats
.st_mode
)) {
129 info("found existing node instead of symlink '%s'", slink
);
130 if (lstat(node
, &stats2
) == 0) {
131 if ((stats
.st_mode
& S_IFMT
) == (stats2
.st_mode
& S_IFMT
) &&
132 stats
.st_rdev
== stats2
.st_rdev
) {
133 info("replace device node '%s' with symlink to our node '%s'", slink
, node
);
135 err("device node '%s' already exists, link '%s' will not overwrite it", node
, slink
);
139 } else if (S_ISLNK(stats
.st_mode
)) {
142 info("found existing symlink '%s'", slink
);
143 len
= readlink(slink
, buf
, sizeof(buf
));
146 if (strcmp(target
, buf
) == 0) {
147 info("preserve already existing symlink '%s' to '%s'", slink
, target
);
148 selinux_setfilecon(slink
, NULL
, S_IFLNK
);
154 info("creating symlink '%s' to '%s'", slink
, target
);
155 selinux_setfscreatecon(slink
, NULL
, S_IFLNK
);
156 retval
= symlink(target
, slink
);
157 selinux_resetfscreatecon();
162 info("atomically replace '%s'", slink
);
163 strlcpy(slink_tmp
, slink
, sizeof(slink_tmp
));
164 strlcat(slink_tmp
, TMP_FILE_EXT
, sizeof(slink_tmp
));
165 selinux_setfscreatecon(slink_tmp
, NULL
, S_IFLNK
);
166 retval
= symlink(target
, slink_tmp
);
167 selinux_resetfscreatecon();
169 err("symlink(%s, %s) failed: %s", target
, slink_tmp
, strerror(errno
));
172 retval
= rename(slink_tmp
, slink
);
174 err("rename(%s, %s) failed: %s", slink_tmp
, slink
, strerror(errno
));
182 static int update_link(struct udevice
*udev
, const char *name
)
184 LIST_HEAD(name_list
);
185 char slink
[PATH_SIZE
];
186 char node
[PATH_SIZE
];
187 struct udevice
*udev_db
;
188 struct name_entry
*device
;
189 char target
[PATH_MAX
] = "";
194 strlcpy(slink
, udev_root
, sizeof(slink
));
195 strlcat(slink
, "/", sizeof(slink
));
196 strlcat(slink
, name
, sizeof(slink
));
198 count
= udev_db_get_devices_by_name(name
, &name_list
);
199 info("found %i devices with name '%s'", count
, name
);
201 /* if we don't have a reference, delete it */
203 info("no reference left, remove '%s'", name
);
204 if (!udev
->test_run
) {
211 /* find the device with the highest priority */
212 list_for_each_entry(device
, &name_list
, node
) {
213 info("found '%s' for '%s'", device
->name
, name
);
215 /* did we find ourself? we win, if we have the same priority */
216 if (strcmp(udev
->dev
->devpath
, device
->name
) == 0) {
217 info("compare (our own) priority of '%s' %i >= %i",
218 udev
->dev
->devpath
, udev
->link_priority
, priority
);
219 if (target
[0] == '\0' || udev
->link_priority
>= priority
) {
220 priority
= udev
->link_priority
;
221 strlcpy(target
, udev
->name
, sizeof(target
));
226 /* or something else, then read priority from database */
227 udev_db
= udev_device_init(NULL
);
230 if (udev_db_get_device(udev_db
, device
->name
) == 0) {
231 info("compare priority of '%s' %i > %i",
232 udev_db
->dev
->devpath
, udev_db
->link_priority
, priority
);
233 if (target
[0] == '\0' || udev_db
->link_priority
> priority
) {
234 priority
= udev_db
->link_priority
;
235 strlcpy(target
, udev_db
->name
, sizeof(target
));
238 udev_device_cleanup(udev_db
);
240 name_list_cleanup(&name_list
);
242 if (target
[0] == '\0') {
243 err("missing target for '%s'", name
);
248 /* create symlink to the target with the highest priority */
249 strlcpy(node
, udev_root
, sizeof(node
));
250 strlcat(node
, "/", sizeof(node
));
251 strlcat(node
, target
, sizeof(node
));
252 info("'%s' with target '%s' has the highest priority %i, create it", name
, target
, priority
);
253 if (!udev
->test_run
) {
255 node_symlink(node
, slink
);
261 void udev_node_update_symlinks(struct udevice
*udev
, struct udevice
*udev_old
)
263 struct name_entry
*name_loop
;
264 char symlinks
[PATH_SIZE
] = "";
266 list_for_each_entry(name_loop
, &udev
->symlink_list
, node
) {
267 info("update symlink '%s' of '%s'", name_loop
->name
, udev
->dev
->devpath
);
268 update_link(udev
, name_loop
->name
);
269 strlcat(symlinks
, udev_root
, sizeof(symlinks
));
270 strlcat(symlinks
, "/", sizeof(symlinks
));
271 strlcat(symlinks
, name_loop
->name
, sizeof(symlinks
));
272 strlcat(symlinks
, " ", sizeof(symlinks
));
275 /* export symlinks to environment */
276 remove_trailing_chars(symlinks
, ' ');
277 if (symlinks
[0] != '\0')
278 setenv("DEVLINKS", symlinks
, 1);
280 /* update possible left-over symlinks (device metadata changed) */
281 if (udev_old
!= NULL
) {
282 struct name_entry
*link_loop
;
283 struct name_entry
*link_old_loop
;
286 /* remove current symlinks from old list */
287 list_for_each_entry(link_old_loop
, &udev_old
->symlink_list
, node
) {
289 list_for_each_entry(link_loop
, &udev
->symlink_list
, node
) {
290 if (strcmp(link_old_loop
->name
, link_loop
->name
) == 0) {
296 /* link does no longer belong to this device */
297 info("update old symlink '%s' no longer belonging to '%s'",
298 link_old_loop
->name
, udev
->dev
->devpath
);
299 update_link(udev
, link_old_loop
->name
);
304 * if the node name has changed, delete the node,
305 * or possibly restore a symlink of another device
307 if (strcmp(udev
->name
, udev_old
->name
) != 0)
308 update_link(udev
, udev_old
->name
);
312 int udev_node_add(struct udevice
*udev
)
314 char filename
[PATH_SIZE
];
320 strlcpy(filename
, udev_root
, sizeof(filename
));
321 strlcat(filename
, "/", sizeof(filename
));
322 strlcat(filename
, udev
->name
, sizeof(filename
));
323 create_path(filename
);
325 if (strcmp(udev
->owner
, "root") == 0)
331 id
= strtoul(udev
->owner
, &endptr
, 10);
332 if (endptr
[0] == '\0')
335 uid
= lookup_user(udev
->owner
);
338 if (strcmp(udev
->group
, "root") == 0)
344 id
= strtoul(udev
->group
, &endptr
, 10);
345 if (endptr
[0] == '\0')
348 gid
= lookup_group(udev
->group
);
351 info("creating device node '%s', major=%d, minor=%d, mode=%#o, uid=%d, gid=%d",
352 filename
, major(udev
->devt
), minor(udev
->devt
), udev
->mode
, uid
, gid
);
355 if (udev_node_mknod(udev
, filename
, udev
->devt
, udev
->mode
, uid
, gid
) != 0) {
360 setenv("DEVNAME", filename
, 1);
362 /* create all_partitions if requested */
363 if (udev
->partitions
) {
364 char partitionname
[PATH_SIZE
];
368 /* take the maximum registered minor range */
369 attr
= sysfs_attr_get_value(udev
->dev
->devpath
, "range");
373 udev
->partitions
= range
-1;
375 info("creating device partition nodes '%s[1-%i]'", filename
, udev
->partitions
);
376 if (!udev
->test_run
) {
377 for (i
= 1; i
<= udev
->partitions
; i
++) {
380 snprintf(partitionname
, sizeof(partitionname
), "%s%d", filename
, i
);
381 partitionname
[sizeof(partitionname
)-1] = '\0';
382 part_devt
= makedev(major(udev
->devt
), minor(udev
->devt
) + i
);
383 udev_node_mknod(udev
, partitionname
, part_devt
, udev
->mode
, uid
, gid
);
391 int udev_node_remove(struct udevice
*udev
)
393 char filename
[PATH_SIZE
];
394 char partitionname
[PATH_SIZE
];
399 strlcpy(filename
, udev_root
, sizeof(filename
));
400 strlcat(filename
, "/", sizeof(filename
));
401 strlcat(filename
, udev
->name
, sizeof(filename
));
402 if (stat(filename
, &stats
) != 0) {
403 dbg("device node '%s' not found", filename
);
406 if (udev
->devt
&& stats
.st_rdev
!= udev
->devt
) {
407 info("device node '%s' points to a different device, skip removal", filename
);
411 info("removing device node '%s'", filename
);
413 retval
= unlink_secure(filename
);
417 setenv("DEVNAME", filename
, 1);
418 num
= udev
->partitions
;
422 info("removing all_partitions '%s[1-%i]'", filename
, num
);
425 for (i
= 1; i
<= num
; i
++) {
426 snprintf(partitionname
, sizeof(partitionname
), "%s%d", filename
, i
);
427 partitionname
[sizeof(partitionname
)-1] = '\0';
429 unlink_secure(partitionname
);
432 delete_path(filename
);