chiark / gitweb /
Merge branch 'master' of gregkh@master.kernel.org:/pub/scm/linux/hotplug/udev
[elogind.git] / udev_remove.c
1 /*
2  * udev-remove.c
3  *
4  * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
5  * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
6  *
7  *      This program is free software; you can redistribute it and/or modify it
8  *      under the terms of the GNU General Public License as published by the
9  *      Free Software Foundation version 2 of the License.
10  * 
11  *      This program is distributed in the hope that it will be useful, but
12  *      WITHOUT ANY WARRANTY; without even the implied warranty of
13  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *      General Public License for more details.
15  * 
16  *      You should have received a copy of the GNU General Public License along
17  *      with this program; if not, write to the Free Software Foundation, Inc.,
18  *      675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  */
21
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stddef.h>
25 #include <stdio.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <errno.h>
29 #include <sys/stat.h>
30
31 #include "udev_libc_wrapper.h"
32 #include "udev.h"
33 #include "udev_utils.h"
34 #include "udev_version.h"
35 #include "udev_db.h"
36 #include "logging.h"
37
38 static int delete_path(const char *path)
39 {
40         char *pos;
41         int retval;
42
43         pos = strrchr(path, '/');
44         while (1) {
45                 *pos = '\0';
46                 pos = strrchr(path, '/');
47
48                 /* don't remove the last one */
49                 if ((pos == path) || (pos == NULL))
50                         break;
51
52                 /* remove if empty */
53                 retval = rmdir(path);
54                 if (errno == ENOENT)
55                         retval = 0;
56                 if (retval) {
57                         if (errno == ENOTEMPTY)
58                                 return 0;
59                         err("rmdir(%s) failed: %s", path, strerror(errno));
60                         break;
61                 }
62                 dbg("removed '%s'", path);
63         }
64         return 0;
65 }
66
67 static int delete_node(struct udevice *udev)
68 {
69         char filename[PATH_SIZE];
70         char partitionname[PATH_SIZE];
71         struct name_entry *name_loop;
72         struct stat stats;
73         int retval;
74         int i;
75         int num;
76
77         list_for_each_entry(name_loop, &udev->symlink_list, node) {
78                 snprintf(filename, sizeof(filename), "%s/%s", udev_root, name_loop->name);
79                 filename[sizeof(filename)-1] = '\0';
80
81                 if (stat(filename, &stats) != 0) {
82                         dbg("symlink '%s' not found", filename);
83                         continue;
84                 }
85                 if (udev->devt && stats.st_rdev != udev->devt) {
86                         info("symlink '%s' points to a different device, skip removal", filename);
87                         continue;;
88                 }
89
90                 info("removing symlink '%s'", filename);
91                 unlink(filename);
92
93                 if (strchr(filename, '/'))
94                         delete_path(filename);
95         }
96
97         snprintf(filename, sizeof(filename), "%s/%s", udev_root, udev->name);
98         filename[sizeof(filename)-1] = '\0';
99
100         if (stat(filename, &stats) != 0) {
101                 dbg("device node '%s' not found", filename);
102                 return -1;
103         }
104         if (udev->devt && stats.st_rdev != udev->devt) {
105                 info("device node '%s' points to a different device, skip removal", filename);
106                 return -1;
107         }
108
109         info("removing device node '%s'", filename);
110         retval = unlink_secure(filename);
111         if (retval)
112                 return retval;
113
114         /* export DEVNAME to the environment */
115         snprintf(udev->devname, sizeof(udev->devname), "%s/%s", udev_root, udev->name);
116         udev->devname[sizeof(udev->devname)-1] = '\0';
117
118         num = udev->partitions;
119         if (num > 0) {
120                 info("removing all_partitions '%s[1-%i]'", filename, num);
121                 if (num > 255) {
122                         info("garbage from udev database, skip all_partitions removal");
123                         return -1;
124                 }
125                 for (i = 1; i <= num; i++) {
126                         snprintf(partitionname, sizeof(partitionname), "%s%d", filename, i);
127                         partitionname[sizeof(partitionname)-1] = '\0';
128                         unlink_secure(partitionname);
129                 }
130         }
131
132         if (strchr(udev->name, '/'))
133                 delete_path(filename);
134
135         return retval;
136 }
137
138 /*
139  * look up the sysfs path in the database to get the node name to remove
140  * If we can't find it, use kernel name for lack of anything else to know to do
141  */
142 int udev_remove_device(struct udevice *udev)
143 {
144         if (udev->type != DEV_BLOCK && udev->type != DEV_CLASS)
145                 return 0;
146
147         if (udev_db_get_device(udev, udev->devpath) == 0) {
148                 if (udev->ignore_remove) {
149                         dbg("remove event for '%s' requested to be ignored by rule", udev->name);
150                         return 0;
151                 }
152                 dbg("remove name='%s'", udev->name);
153                 udev_db_delete_device(udev);
154         } else {
155                 dbg("'%s' not found in database, don't remove anything", udev->devpath);
156                 return -1;
157         }
158
159         return delete_node(udev);
160 }