fixed logic bug
[tridge/junkcode.git] / isoffline_smb.c
1 /*
2   demonstration of checking if a file is offline using DMAPI, with
3   shortcut tricks using st_atime and st_blocks
4
5   Build with
6       gcc -o isoffline isoffline.c -ldmapi
7
8   Andrew Tridgell (tridgell@au1.ibm.com) July 2007
9  */
10
11 #include <stdio.h>
12 #include <dmapi.h>
13 #include <stdbool.h>
14 #include <stdint.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <time.h>
18 #include <errno.h>
19 #include <sys/stat.h>
20
21 /* optimisation tunables - used to avoid the DMAPI slow path */
22 #define FILE_IS_ONLINE_RATIO      0.5
23 #define FILE_IS_ONLINE_ATIME      60
24
25 #define DMAPI_SESSION_NAME "samba"
26 #define DMAPI_TRACE 10
27
28 static dm_sessid_t samba_dmapi_session = DM_NO_SESSION;
29
30 #define DEBUG(lvl, x) printf x
31 #define DEBUGADD(lvl, x) printf x
32
33 /* 
34    Initialise DMAPI session. The session is persistant kernel state,
35    so it might already exist, in which case we merely want to
36    reconnect to it. This function should be called as root.
37  */
38 static int dmapi_init_session(void)
39 {
40         char    buf[DM_SESSION_INFO_LEN];
41         size_t  buflen;
42         uint        nsessions = 5;
43         dm_sessid_t *sessions = NULL;
44         int i, err;
45         char *version;
46
47         if (dm_init_service(&version) < 0) {
48                 DEBUG(0,("dm_init_service failed - disabling DMAPI\n"));
49                 return -1;
50         }
51
52         ZERO_STRUCT(buf);
53
54         do {
55                 dm_sessid_t *new_sessions;
56                 nsessions *= 2;
57                 new_sessions = TALLOC_REALLOC_ARRAY(NULL, sessions, 
58                                                     dm_sessid_t, nsessions);
59                 if (new_sessions == NULL) {
60                         talloc_free(sessions);
61                         return -1;
62                 }
63                 sessions = new_sessions;
64                 err = dm_getall_sessions(nsessions, sessions, &nsessions);
65         } while (err == -1 && errno == E2BIG);
66
67         if (err == -1) {
68                 DEBUGADD(DMAPI_TRACE,
69                         ("failed to retrieve DMAPI sessions: %s\n",
70                         strerror(errno)));
71                 talloc_free(sessions);
72                 return -1;
73         }
74
75         for (i = 0; i < nsessions; ++i) {
76                 err = dm_query_session(sessions[i], sizeof(buf), buf, &buflen);
77                 buf[sizeof(buf) - 1] = '\0';
78                 if (err == 0 && strcmp(DMAPI_SESSION_NAME, buf) == 0) {
79                         samba_dmapi_session = sessions[i];
80                         DEBUGADD(DMAPI_TRACE,
81                                 ("attached to existing DMAPI session "
82                                  "named '%s'\n", buf));
83                         break;
84                 }
85         }
86
87         talloc_free(sessions);
88
89         /* No session already defined. */
90         if (samba_dmapi_session == DM_NO_SESSION) {
91                 err = dm_create_session(DM_NO_SESSION, DMAPI_SESSION_NAME,
92                                         &samba_dmapi_session);
93                 if (err < 0) {
94                         DEBUGADD(DMAPI_TRACE,
95                                 ("failed to create new DMAPI session: %s\n",
96                                 strerror(errno)));
97                         samba_dmapi_session = DM_NO_SESSION;
98                         return -1;
99                 }
100
101                 DEBUG(0,("created new DMAPI session named '%s' for %s\n", 
102                          DMAPI_SESSION_NAME, version));
103         }
104
105         if (samba_dmapi_session != DM_NO_SESSION) {
106                 set_effective_capability(DMAPI_ACCESS_CAPABILITY);
107         }
108
109         /* 
110            Note that we never end the DMAPI session. It gets re-used
111            if possible
112          */
113
114         return 0;
115 }
116
117
118 /* 
119    return a pointer to our dmapi session if available
120    This assumes you have called dmapi_have_session() first
121  */
122 const void *dmapi_get_current_session(void) 
123 {
124         if (samba_dmapi_session == DM_NO_SESSION) {
125                 return NULL;
126         }
127         return (void *)&samba_dmapi_session;
128 }
129
130 /*
131   this must be the first dmapi call you make in Samba. It will initialise dmapi
132   if available and tell you if you can get a dmapi session. This should be called in
133   the client specific child process
134  */
135 BOOL dmapi_have_session(void)
136 {
137         static BOOL initialised;
138         if (!initialised) {
139                 initialised = true;
140                 become_root();
141                 dmapi_init_session();
142                 unbecome_root();
143         }
144         return samba_dmapi_session != DM_NO_SESSION;
145 }
146
147
148 /*
149   see if a file is offline
150
151   return -1 on failure. Set *offline to true/false according to
152   offline status
153  */
154 static int is_offline(char *fname, time_t now, bool *offline)
155 {
156         struct stat st;
157         void *handle=NULL;
158         size_t handle_len=0;
159         size_t rlen;
160         int ret;
161         dm_attrname_t dmAttrName;
162         /* keep some state between calls, to save on session creation calls */
163         static struct dmapi_state {
164                 dm_sessid_t sid;
165                 void *handle;
166                 size_t handle_len;
167         } state;
168
169         if (state.sid == 0) {
170                 /* create a new session if needed */
171                 if (dm_create_session(DM_NO_SESSION, "samba", &state.sid) != 0) {
172                         return -1;
173                 }
174         }
175
176         /* try shortcut methods first */
177         if (stat(fname, &st) != 0) {
178                 return -1;
179         }
180
181         /* if the file has more than FILE_IS_ONLINE_RATIO of blocks available,
182            then assume its not offline (it may not be 100%, as it could be sparse) */
183         if (512 * (off_t)st.st_blocks > st.st_size * FILE_IS_ONLINE_RATIO) {
184                 *offline = false;
185                 return 0;
186         }
187
188         /* go the slow DMAPI route */
189         if (dm_path_to_handle(fname, &handle, &handle_len) != 0) {
190                 return -1;
191         }
192
193         memset(&dmAttrName, 0, sizeof(dmAttrName));
194         strcpy((char *)&dmAttrName.an_chars[0], "IBMObj");
195
196         ret = dm_get_dmattr(state.sid, handle, handle_len, 
197                             DM_NO_TOKEN, &dmAttrName, 0, NULL, &rlen);
198
199         /* its offline if the IBMObj attribute exists */
200         *offline = (ret == 0 || (ret == -1 && errno == E2BIG));
201
202         dm_handle_free(handle, handle_len);
203         return 0;       
204 }
205
206
207 int main(int argc, char *argv[])
208 {
209         int i;
210         time_t now = time(NULL);
211         now = time(NULL);
212         if (argc < 2) {
213                 printf("isoffline <fname...>\n");
214                 exit(1);
215         }
216         for (i=1;i<argc;i++) {
217                 bool offline;
218                 if (is_offline(argv[i], now, &offline) == -1) {
219                         perror(argv[i]);
220                         exit(1);
221                 }
222                 printf("%s\t%s\n", offline?"offline":"online ", argv[i]);
223         }
224         return 0;
225 }