~ubuntu-branches/ubuntu/raring/tracker/raring

« back to all changes in this revision

Viewing changes to src/tracker-writeback/tracker-writeback-file.c

  • Committer: Package Import Robot
  • Author(s): Michael Biebl
  • Date: 2011-08-26 00:26:14 UTC
  • mfrom: (4.3.17 sid)
  • Revision ID: package-import@ubuntu.com-20110826002614-4qjfs9jhh5gs4p13
Tags: 0.10.24-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
 
20
20
#include "config.h"
21
21
 
 
22
#include <stdio.h>
 
23
#include <fcntl.h> /* O_WRONLY */
 
24
 
 
25
#include <gio/gunixoutputstream.h>
 
26
 
22
27
#include <libtracker-common/tracker-file-utils.h>
23
28
 
24
29
#include "tracker-writeback-file.h"
25
30
 
26
31
static gboolean tracker_writeback_file_update_metadata (TrackerWriteback        *writeback,
27
32
                                                        GPtrArray               *values,
28
 
                                                        TrackerSparqlConnection *connection);
 
33
                                                        TrackerSparqlConnection *connection,
 
34
                                                        GCancellable            *cancellable);
29
35
 
30
36
G_DEFINE_ABSTRACT_TYPE (TrackerWritebackFile, tracker_writeback_file, TRACKER_TYPE_WRITEBACK)
31
37
 
42
48
{
43
49
}
44
50
 
45
 
static gboolean
46
 
file_unlock_cb (gpointer user_data)
47
 
{
48
 
        GFile *file;
49
 
        gchar *path;
50
 
 
51
 
        file = user_data;
52
 
        path = g_file_get_path (file);
53
 
        g_message ("Unlocking file '%s'", path);
54
 
        g_free (path);
55
 
 
56
 
        tracker_file_unlock (file);
57
 
        g_object_unref (file);
58
 
 
59
 
        return FALSE;
60
 
}
61
 
 
62
51
static GFile *
63
 
get_tmp_file (GFile *file)
 
52
create_temporary_file (GFile     *file,
 
53
                       GFileInfo *file_info)
64
54
{
 
55
        GInputStream *input_stream;
 
56
        GOutputStream *output_stream;
65
57
        GFile *tmp_file, *parent;
66
 
        gchar *tmp_name, *name;
67
 
 
68
 
        /* Create a temporary, hidden file
69
 
         * within the same directory */
 
58
        gchar *dir, *name, *tmp_path;
 
59
        guint32 mode;
 
60
        gint fd;
 
61
        GError *error = NULL;
 
62
 
 
63
        if (!g_file_is_native (file)) {
 
64
                gchar *uri;
 
65
 
 
66
                uri = g_file_get_uri (file);
 
67
                g_warning ("Could not create temporary file, file is not native: '%s'", uri);
 
68
                g_free (uri);
 
69
 
 
70
                return NULL;
 
71
        }
 
72
 
 
73
        /* Create input stream */
 
74
        input_stream = G_INPUT_STREAM (g_file_read (file, NULL, &error));
 
75
 
 
76
        if (error) {
 
77
                g_critical ("Could not create temporary file, %s", error->message);
 
78
                g_error_free (error);
 
79
                return NULL;
 
80
        }
 
81
 
 
82
        /* Create output stream in a tmp file */
70
83
        parent = g_file_get_parent (file);
 
84
        dir = g_file_get_path (parent);
 
85
        g_object_unref (parent);
 
86
 
71
87
        name = g_file_get_basename (file);
72
 
 
73
 
        tmp_name = g_strdup_printf ("._tracker_%s", name);
74
 
        tmp_file = g_file_get_child (parent, tmp_name);
75
 
 
76
 
        g_object_unref (parent);
77
 
        g_free (tmp_name);
 
88
        tmp_path = g_strdup_printf ("%s" G_DIR_SEPARATOR_S ".tracker-XXXXXX.%s",
 
89
                                    dir, name);
 
90
        g_free (dir);
78
91
        g_free (name);
79
92
 
 
93
        mode = g_file_info_get_attribute_uint32 (file_info,
 
94
                                                 G_FILE_ATTRIBUTE_UNIX_MODE);
 
95
        fd = g_mkstemp_full (tmp_path, O_WRONLY, mode);
 
96
 
 
97
        output_stream = g_unix_output_stream_new (fd, TRUE);
 
98
 
 
99
        /* Splice the original file into the tmp file */
 
100
        g_output_stream_splice (output_stream,
 
101
                                input_stream,
 
102
                                G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
 
103
                                G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
 
104
                                NULL, &error);
 
105
 
 
106
        g_object_unref (output_stream);
 
107
        g_object_unref (input_stream);
 
108
 
 
109
        tmp_file = g_file_new_for_path (tmp_path);
 
110
        g_free (tmp_path);
 
111
 
 
112
        if (error) {
 
113
                g_critical ("Could not copy temporary file, %s", error->message);
 
114
                g_error_free (error);
 
115
 
 
116
                g_file_delete (tmp_file, NULL, NULL);
 
117
                g_object_unref (tmp_file);
 
118
 
 
119
                return NULL;
 
120
        }
 
121
 
80
122
        return tmp_file;
81
123
}
82
124
 
83
125
static gboolean
84
126
tracker_writeback_file_update_metadata (TrackerWriteback        *writeback,
85
127
                                        GPtrArray               *values,
86
 
                                        TrackerSparqlConnection *connection)
 
128
                                        TrackerSparqlConnection *connection,
 
129
                                        GCancellable            *cancellable)
87
130
{
88
131
        TrackerWritebackFileClass *writeback_file_class;
89
132
        gboolean retval;
90
133
        GFile *file, *tmp_file;
91
134
        GFileInfo *file_info;
92
 
        const gchar *urls[2] = { NULL, NULL };
93
135
        GStrv row;
94
136
        const gchar * const *content_types;
95
137
        const gchar *mime_type;
114
156
        file = g_file_new_for_uri (row[0]);
115
157
 
116
158
        file_info = g_file_query_info (file,
 
159
                                       G_FILE_ATTRIBUTE_UNIX_MODE ","
117
160
                                       G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
118
161
                                       G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
119
162
                                       NULL, NULL);
123
166
                return FALSE;
124
167
        }
125
168
 
126
 
        /* Copy to a temporary file so we can perform an atomic write on move */
127
 
        tmp_file = get_tmp_file (file);
128
 
        if (!g_file_copy (file, tmp_file, 0,
129
 
                          NULL, NULL, NULL, NULL)) {
130
 
                g_object_unref (file);
131
 
                g_object_unref (tmp_file);
132
 
                return FALSE;
133
 
        }
134
 
 
135
169
        mime_type = g_file_info_get_content_type (file_info);
136
170
        content_types = (writeback_file_class->content_types) (TRACKER_WRITEBACK_FILE (writeback));
137
171
 
144
178
                }
145
179
        }
146
180
 
 
181
        if (!retval) {
 
182
                /* module does not support writeback for this file */
 
183
                g_object_unref (file);
 
184
                g_object_unref (file_info);
 
185
 
 
186
                return FALSE;
 
187
        }
 
188
 
 
189
        /* Copy to a temporary file so we can perform an atomic write on move */
 
190
        tmp_file = create_temporary_file (file, file_info);
 
191
 
 
192
        if (!tmp_file) {
 
193
                g_object_unref (file);
 
194
                g_object_unref (file_info);
 
195
 
 
196
                return FALSE;
 
197
        }
 
198
 
 
199
        retval = (writeback_file_class->update_file_metadata) (TRACKER_WRITEBACK_FILE (writeback),
 
200
                                                               tmp_file,
 
201
                                                               values,
 
202
                                                               connection,
 
203
                                                               cancellable);
 
204
 
 
205
        if (!retval) {
 
206
                /* Delete the temporary file and preserve original */
 
207
                g_file_delete (tmp_file, NULL, NULL);
 
208
        } else {
 
209
                /* Move back the modified file to the original location */
 
210
                g_file_move (tmp_file, file,
 
211
                             G_FILE_COPY_OVERWRITE,
 
212
                             NULL, NULL, NULL, NULL);
 
213
        }
 
214
 
 
215
        g_object_unref (tmp_file);
147
216
        g_object_unref (file_info);
148
 
 
149
 
        if (retval) {
150
 
                g_message ("Locking file '%s' in order to write metadata", row[0]);
151
 
 
152
 
                tracker_file_lock (file);
153
 
 
154
 
                urls[0] = row[0];
155
 
 
156
 
                tracker_miner_manager_ignore_next_update (tracker_writeback_get_miner_manager (),
157
 
                                                          "org.freedesktop.Tracker1.Miner.Files",
158
 
                                                          urls);
159
 
 
160
 
                /* A note on IgnoreNextUpdate + Writeback. Consider this situation
161
 
                 * I see with an application recording a video:
162
 
                 *  - Application creates a resource for a video in the store and
163
 
                 *    sets slo:location
164
 
                 *  - Application starts writting the new video file.
165
 
                 *  - Store tells writeback to write the new slo:location in the file
166
 
                 *  - Writeback reaches this exact function and sends IgnoreNextUpdate,
167
 
                 *    then tries to update metadata.
168
 
                 *  - Miner-fs gets the IgnoreNextUpdate (sent by the line above).
169
 
                 *  - Application is still recording the video, which gets translated
170
 
                 *    into an original CREATED event plus multiple UPDATE events which
171
 
                 *    are being merged at tracker-monitor level, still not notified to
172
 
                 *    TrackerMinerFS.
173
 
                 *  - TrackerWriteback tries to updte file metadata (line below) but cannot
174
 
                 *    do it yet as application is still updating the file, thus, the real
175
 
                 *    metadata update gets delayed until the application ends writing
176
 
                 *    the video.
177
 
                 *  - Application ends writing the video.
178
 
                 *  - Now TrackerWriteback really updates the file. This happened N seconds
179
 
                 *    after we sent the IgnoreNextUpdate, being N the length of the video...
180
 
                 *  - TrackerMonitor sends the merged CREATED event to TrackerMinerFS,
181
 
                 *    detects the IgnoreNextUpdate request and in this case we ignore the
182
 
                 *    IgnoreNextUpdate request as this is a CREATED event.
183
 
                 *
184
 
                 * Need to review the whole writeback+IgnoreNextUpdate mechanism to cope
185
 
                 * with situations like the one above.
186
 
                 */
187
 
 
188
 
                retval = (writeback_file_class->update_file_metadata) (TRACKER_WRITEBACK_FILE (writeback),
189
 
                                                                       tmp_file, values, connection);
190
 
 
191
 
                /*
192
 
                 * This timeout value was 3s before, which could have been in
193
 
                 * order to have miner-fs skip the updates we just made, something
194
 
                 * along the purpose of IgnoreNextUpdate.
195
 
                 *
196
 
                 * But this is a problem when the event being ignored is a CREATED
197
 
                 * event. This is, tracker-writeback modifies a file that was just
198
 
                 * created. If we ignore this in the miner-fs, it will never index
199
 
                 * it, and that is not good. As there is already the
200
 
                 * IgnoreNextUpdate mechanism in place, I'm moving this timeout
201
 
                 * value to 1s. So, once writeback has written the file, only 1s
202
 
                 * after will unlock it. This synchronizes well with the 2s timeout
203
 
                 * in the miner-fs between detecting the file update and the actual
204
 
                 * processing.
205
 
                 */
206
 
                g_timeout_add_seconds (1, file_unlock_cb, g_object_ref (file));
207
 
        }
208
 
 
209
 
        /* Move back the modified file to the original location */
210
 
        g_file_move (tmp_file, file,
211
 
                     G_FILE_COPY_OVERWRITE,
212
 
                     NULL, NULL, NULL, NULL);
213
 
 
214
 
        g_object_unref (tmp_file);
215
217
        g_object_unref (file);
216
218
 
217
219
        return retval;